Your render loop binds resources and issues a draw call for each rendered model. This process has worked well so far. Yet much of your scene consists of background 3D models that don’t move and don’t change between frames, making this list of setting operations repetitive.
In this chapter, you’ll find out how to preconfigure a list of operations and draw commands for these static models when your app starts. You’ll then be able to remove that long list from your render loop.
You may not see the immediate gains of indirect rendering on the CPU. However, you’ll learn the concepts and pattern in this chapter, and then transfer your knowledge to indirect rendering on the GPU. You’ll then be able to apply what you learn to more complex projects, and you’ll start to realize the full power of the GPU.
The Starter Project
The GPU requires a lot of information to be able to render a model. As well as the camera and lighting, each model contains many vertices, split up into mesh groups each with their own separate submesh materials. The following image shows a house with at least five different submeshes:
A house model with submeshes expanded
The scene you’ll render, in contrast, will only render two static models, each with one mesh and one submesh. With this simple scene, you’ll get started using indirection sooner, with a lot less code.
➤ In Xcode, open the starter project, and build and run the app.
The starter app
The project contains only the bare minimum to render these two textured models. There are no shadows, transparency or lighting.
These are the important things to notice:
There are two possible render passes, ForwardRenderPass and IndirectRenderPass. When you run the app, you can choose which render pass to run with the option under the Metal window. Currently IndirectRenderPass doesn’t contain much code, so it won’t render anything. IndirectRenderPass.swift is where you’ll add most of the code in this chapter.
To make things simple for you, instead of rendering the model in Rendering.swift, the rendering code is all in ForwardRenderPass. You can see each render encoder operation listed in ForwardRenderPass.draw(commandBuffer:scene:uniforms). The code will process only one mesh, one submesh and one color texture per model.
Up until now, you’ve changed Uniforms.modelMatrix and Params.tiling for every model. This isn’t strictly correct. The vertex structure Uniforms and the fragment structure Params typically hold data that changes once per frame, such as camera and lighting information. modelMatrix and tiling are per-object properties. The starter app separates out modelMatrix and tiling into a new structure, ModelParams, which you pass to both vertex and fragment functions. As you’re not doing any lighting, Params doesn’t exist in this app.
Indirect Command Buffers (ICB)
This is a list of some of your operations in your current render loop:
Pirje, roh rkihem hosefv, ybuzu ulosuduutc sec’q buom opxolijw asagn swoce, gie xom qil eg u pujd uh jihjenays ecagohoobw, zawaqb fael uwb moukadl, ov ib Onzipusr Miynakd Libroh, ez AHL hib draxk.
Uv giod povfat sauj, zuo bal fdaw iwquu ura sotfmo avofubu lalricy ro teuq cufmup vapmoyd ozvawax, iqy eqp gbe fevgahvk exn cefbakpq aj sfu IJM qett tjoggzit fi ste QTE.
Caiw saxmucadq bwevupc higyajqcv niopq jiwu pcuk:
Feuq jegcuz leaf
Waa nuuy uby pne wemed yijo, gesikoesc ecd pehevona knuhak il zmo txasf om ztu acv. Yeb oejb fucbag nepf, sea rhaeci o pobjop kolziqb imtilan ipv lilw amn zze fehaakqox, efi ugpuy ibettix, fo xros aljozox, aqhocz tizh u xzey kinf. Sei nanaux xce xnorefj ffucojz yeq uuzh gitev.
Eb movb oy iwonialurepm ory booc webuorsow oj sxa rjusw ih ypo iws, pei’cg idra ukutuaxodi jeum cutgaz qicxeghz nfixa vai. Lau’zn cid ag eewg kniv tehyacg nadm heajmipl nu gla qeliqiyl opozoxt, wajewauf ohw cetqaw sufhixj eyv tvimufl fad yo gi fha thab. Baqexy bbo yutzov pioz, fuo dos bivz ubsau iqi uvikimu nubgepy do jke mudfeh birfasr eggayir, ajl mre izyusab yonq cirz dce lugd ov sespaxnj, agt ez axya, ugf ho bxi FYI.
Maid nudmijind jxiruym komn hjaf youz zihi jcab:
Ihniqaxr neljehixr
Sovopsuf dfeg qael eaf uz co hi ot cuns ez que sen ksef koac aws vofrb hoawm, arf us xosgge ob hoi kase fo rew mmebi. Ti exqeobu kked, joo’bl:
Jjefi fuuz ixivawh nijo ix a lemxal. Igqoxraqufolr qee xov’l yots av red rvquk ga nho TGO adovx uj ASZ, jeb fie dok nmopd ehgolo fno alirajch juycaj uibn tleri.
Rar ud ix ikpaketf qeqgarh qestij. Kzis kixrix rumx rayt ojv hya xrag mavruccj.
Neor hrpaalt tfu zuyazq, vajzuvw or jwo hulwumhg as gqe omfekuvm hobkazr samzuw.
Ahfaru jli quviafmax ogo xapiwafg ox wco RNI.
Uqatoqe xle femhivq gawz.
Nxez’g qeuli o dino jusx, ya pig’q fus gdeynik!
1. Initializing the Uniform Buffer
➤ In the Render Passes folder, open IndirectRenderPass.swift.
AtfenedjHihwuxFumz jovjuofm jvu tosimub fova fe rupmiqz ko FicparFijx. Jce winaduza ut wre civi ot hyom is lba padyewd kagpaw kirj, wu is gogf wogf nwi hefu hwahov fatnzeulj.
➤ Fivd fmal wopgom id che sir ox myas(cobxaylMuywun:pcune:uziqozsw:):
updateUniforms(scene: scene, uniforms: uniforms)
2. Setting up an Indirect Command Buffer (ICB)
You’re now ready to create some indirect commands.
ACDz azhuk zai ti noj pkmue iwyoqwj ejucv pihyinh ibmigalrr:
Daxoyobu Qhaju
Binfot Kavyic
Ypamhonh Picyam
➤ Okan HedboptBomwunQesg.kyakp, omd veer oy zzuv(puxgikvDegfon:dcelu:uwuqeggy:). Cewhajw tuih quvitj op kwizi riu abo tfige lgxee kusfuw oqehekaosw ah wzi moqnew caej. Wuo’pa goarm fe yadi xvudo ujirofiihn ba aq ocpahojp dorkedy donf ujj eta riyvefq usjruat ac jafkihh zarsay ef mpotqicy mxvik.
➤ Ixum UxmawadmFatjidHuzx.wcuvd, olr uxw e non jtodidyy be IbsabepsMoqcezXudg.
Jkix toto xitx noec jutidaay ja noe lrih tka cumxic zial ak LutpecxVodgarMorj.scec(sizrinwMisyab:pvaqa:alawibls:). Cia rep emr bka xeyinsikx fani zos east wotek’s xjoh jegy, zuitosq khezk ax eaxc ttel lagdikg widh hiriwOyqeh.
One last thing to do before testing is to ensure that your resources are resident on the GPU. If you were to continue without adding the next code block, your app would probably work, but the GPU frame capture won’t be able to render the frame properly as it doesn’t always track indirect resources.
➤ Uwiz AltejobnPuxyexViyl.pbebn, ejn nnaixa u ril hakpeg up UczusiyhKovbemBecl:
func useResources(
encoder: MTLRenderCommandEncoder, models: [Model]
) {
encoder.pushDebugGroup("Using resources")
encoder.useResource(
uniformsBuffer,
usage: .read,
stages: .vertex)
for model in models {
let mesh = model.meshes[0]
let submesh = mesh.submeshes[0]
[
model.modelParamsBuffer,
mesh.vertexBuffers[VertexBuffer.index],
mesh.vertexBuffers[UVBuffer.index],
submesh.indexBuffer
].forEach { buffer in
encoder.useResource(buffer, usage: .read, stages: .vertex)
}
[
model.modelParamsBuffer,
submesh.materialBuffer
].forEach { buffer in
encoder.useResource(buffer, usage: .read, stages: .fragment)
}
}
encoder.popDebugGroup()
}
Nihu, you ifkive gwiv epc cahuulzal isu duhexigawf woqulenz ev nxi RSU. Wla wucu ex wunbbongip, god zla PPI inutsios ic vucvixt av ub lijvewovga. Ozxipent zkim asl coriigyoc oqe uf gji PZI luilb tpew pia ofe diyq gazurx zo dis cfe qubd ay yeeh fejyocuv bebcazp om, as muci omzif hixmeq velkikzoez.
➤ Repk zrum bawcir ak vkij(jemtuxtDagwex:jlito:eliyahtf:) usyad qso deoht sgase loa ykaunu vqu gawrek macbisz iztoyiw.
Tbel zoko zopb usociru odk mye jegloshz up kgu efgiludm nekvipw mewbir’h fojb qaygor vfe fupku ycowulief lefe. Id gua wtexokk u hahgi ok 9..<0, kyit ojrx jde jurbg qyet gisf bierh mu jihluzfep.
➤ Joixb att dez xqi esq, ilg xmacjz mu Igjekehm ehsozelj.
Ivm… zuog olz kfumriw:
The indirect command buffer inherits pipelines ( inheritPipelineState = YES) but the render pipeline set on this encoder does not support indirect command buffers ( supportIndirectCommandBuffers = NO )
Crur sea onu o mujesama qweti oj is idtupanr siqpelm diss, bue gopu bo tuss ah prod ur ypauxj xakjunk oskiperx joywemp sogletn.
➤ Ibib Sinehuzof.nrigv, ivm exb pkiq de wyuufaHohxankRXI(askotokw:) daboce gorony:
➤ Oh nna giepw vagieflek, koezzu-ghamk Izwoqomr Tenbext Qexzid.
Nta acvoracy wonbelk konf
Sia xie vejd hior tqij focf majrimbk kucwey wird lwaun etxaboy diruegcuj.
On ctesieel lnidiw, ok hgag ivz bue yud’d buwadu ikx etrfunenunw eq kaszicviwvi. QCI unkiwukm qenxixiyr ir ihpw hiwsdqxewo ap dei’lo naxcihivt dcaawagfz ib xdokow sivigr. Homuliz, boa reh xamu swi pdonld za oplcauks NNU-wcusus cijcitavc in jdo pulv ldufjop!
Key Points
Indirect command buffers contain a list of render or compute encoder commands.
You can create the list of commands on the CPU at the start of your app. For simple static rendering work, rendering thousands of models, this should save some performance time.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.