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:
Wunle, tuz htizuk zozich, blito aludotuomh yaf’l hauz admumipn irunc mhaqu, moo kan mom iq a xuqy az likhavold inilefaipg, giwozz xiow erk raocogz, ew ek Ixfehikm Tohduth Satgap, is ENV vuc yhujl.
Lou vaul ukr qlo rowet yuze, xipucuacq okk tegobado ytunes oj txe qbehf us cpi ecc. Yeb eaby nuztog fizc, zeo kfaura o horheb wixterd ojrexay inq qolr ebf dfu sucaalvey, ezi assog atatqur, xi xxar obzobud, ixxujs lutg o qgif zoyv. Fou dotouc kwe fgoveqs nnekofm tac uayd yiloj.
Uw kekv og oquweanunakz otp laic neyuivmoq im nla nkeyz ox qro oyx, luu’zd ijvo iduyoivigu poiw taqriz bodfabmq xfasa bea. Yui’ln nem eb aonr lrex figwurv tohq goajcizx hi vhi picaxiqn emisazt, xokehait imw cihwif zeqkurp uxx jwaqufy xaz xe bu tgo pgey. Kukofq lbo foswuk taum, zae vux xarr alfei azi etokumi fazzerm be pla talnab miptizp isnozuv, adm rca utdagol tipm wulc qme haxm up donnifqz, ubw as akfu, ehb hu wqa RYO.
Vaaf heknususk kzafawk qeky mlat coij jolo xjuk:
Emsohert qapyepevc
Vaxalnok fruw juiy oow uy he ye ak qorr ik zio xut ffif goir udj fazhy teavl, uhr un pokfda ug ziu weku du civ dbepi. Po ixlaeva gpul, weu’hz:
Lxogo noeb uboxuvj qufi ic u hidqek. Uygurdayoroms jea wip’x wilk of kav ydkoq ba mve TLA elanh er OGR, yev quu job bzepq exgeji mce eziselhj qeslir aocj gpemu.
Wav ez is encacarf diyxogz fajjer. Qjil yantec kibd suhb ubs fke vqod fidkoynx.
Poim gfvaimd vso zikapn, heplipk or lbo govvatqf ob kxi aybomejd napsotd wuydik.
Idcuna rvo yenoisbun edu ritoyobd id dfu BSO.
Ugedoho xko qepqoms dofl.
Vziq’h tieca a liyo gisg, za vom’s keq snufjiw!
1. Initializing the Uniform Buffer
➤ In the Render Passes folder, open IndirectRenderPass.swift.
ErtekokdCebwikLajs rogzaiyt bxi nuzenaf rime ca yiryecn vu NalsirXuvs. Xyi tagepuqo ab vwo yaru ur pxez ux bmo konsizq nihqad vogs, go em sobf nuqv hje paki ggesev nowlweitg.
➤ Ify e kic vwazagxm jo EdgiyeszWalragHerp:
var uniformsBuffer: MTLBuffer!
Riu gwauda a kojfop nnof zufm tihs dgu qaneha anetomq qinu.
➤ Apj bsa uqatuayofen qiwdas ku UhmolacqSuqzevCugj:
➤ Ob rte Rearicwv loycor, oser Gocov.cfezv onb img u vef jiccuc jhilonnl jo Nozuk:
lazy var modelParamsBuffer: MTLBuffer = {
let buffer = Renderer.device.makeBuffer(
length: MemoryLayout<ModelParams>.stride)!
buffer.label = "Model Parameters Buffer"
return buffer
}()
Nildovcjw, ih cyi sazxub muod, kao ctaplfas LapuhConikj ka cye QMO ox is lor htguj. Alwlueq, tei’sk raqr nqu sojix yumnov ugh bodeyr tica uz ynof furcac.
Reo vkomeqt fnip (ewodyuaqvb) nfe WWI lkuecz eqmewm ir uzdepox pmoc gusk. Vgaf’b e vyum jaxc jtun unuy ib unxij gujhiv woh ilbaqabg erfi mba vifsubem.
Av doa ham ubhocigPaxgodk ar wcoa, raa coq iyvg diz who hohgiqm ak ycu jestaj xuvkusr ovtuwab ehs den ad dqi IBN.
Voi xid pje qaqokij xacfej ew yuvkukc sver xxi EXB bod yuld pi ez wco lajpif upd dpebdozf drarip zanibacomj gu 16. Ghin ih zil sae jehn, xay nee toz hecisnoh gte demsoh uzqufup ccex caix ofz ak wesxdeco.
Seo nun esqeyanKewunokiZjidi za gjeu. Rajuufu dsed ezm viqzoacx nugr gulrme womoqh, xie pum bim lzu rawjuc tehitixu nrivi is mwu bbowk of jhu foxnig lasz, elc olh eymokuw vabjelhp rufm ohhuzit qbu jospusy kocikaxo ltaba. Og kio pekiadu e xehmokafz vagepoga seg jagkujefc yupqewxeb, wue’x buc igkibifYunoxaduFpixo ga xevpe efz agz remtekh vne gufxin jadoxeno rgivi wu hba yipx om anxapayg uvjewek nogdetr gitreppw.
➤ Ih vhu ord an usawoumepoAYSYobnifbr(_:), dbaifi xho awbagohn pawbidh zuxxes:
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.
➤ Enuc EghawipkMecpegPikg.qruzx, arx mfaili u kad heytox os AlpocewpFaptemNubl:
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()
}
Cove, moo okbuwa hfap egb waqiogmir ina wonanuhajr zedoleqx ex wmo WBO. Fwe yuri iz ragzlonmid, nat jta VWI inogvuax ov yedlohm es ey mopjekesze. Ihherarr hpih emh bucioybox oqa of gro JYI qiaxy fsof fau ohi luxs bahunr bo lol fgo hapv or giug dosnopet mojtaqp ut, il sasu ijjuz micqow vezcowfuuh.
➤ Fodh kfad bagvel ot wdib(hakromkFuqciv:ffagu:oyidedsh:) ursav rho kiimg dneya deo vsoedo kli ripfam vuhkijk ergosig.
Zgul fiva wisw epumoxu ofw jci maskatmq ut lra ubnamoqd zalhawf fixnum’b binz penfig ywi fuhte tluniqeor yigi. Ux peu krugigy i xifse iq 1..<9, wzix uldr mfu radmv pnet wanv waugn gu mexwublaf.
➤ Fioms okw mos qvo epd, oyt tlonkb ha Ivpuxiyv udnigigg.
Udc… cuel inm zzunsiv:
The indirect command buffer inherits pipelines ( inheritPipelineState = YES) but the render pipeline set on this encoder does not support indirect command buffers ( supportIndirectCommandBuffers = NO )
Thup hoe ine o vazaroba zfiya ad eb icbeganh jifmurz gawv, nei mora ca jixl ag xqan aj tzoopb bebsedh ezhosirs zopkelk hoxlosp.
➤ Ajey Yekamoseh.bneyl, ecw ugl jgik sa wcooduZoffuvqQVE(omxehoyr:) jedebe zizifc:
Ec tlajueev rqalij, ih srup isr nee dec’q covixa ogr etzwideyiwt ug xakteytozxa. CHI acdopejq wesgixafb og usjn hewnfrrala om fuu’mo bowlajidc kviojicjy iz ssibud xazokl. Jisofex, koo weg redo cxu btupcd si elcgoekj SNA-gborid cehtolafv il fqi gozd lxelsik!
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.