Up to this point, you’ve created projects that had only one render pass. In other words, you used just one render command encoder to submit all of your draw calls to the GPU. In more complex apps, you often need to render content into an offscreen texture in one pass and use the result in a subsequent pass before presenting the texture to the screen.
There are several reasons why you might do multiple passes:
Shadows: In the following chapter, you’ll create a shadow pass and render a depth map from a directional light to help calculate shadows in a subsequent pass.
Deferred Lighting: You render several textures with color, position and normal values. Then, in a final pass, you calculate lighting using those textures.
Reflections: Capture a scene from the point of view of a reflected surface into a texture, then combine that texture with your final render.
Post-processing: Once you have your final rendered image, you can enhance the entire image by adding bloom, screen space ambient occlusion or tinting the final image to add a certain mood or style to your app.
Render Passes
A render pass consists of sending commands to a command encoder. The pass ends when you end encoding on that command encoder.
When setting up a render command encoder, you use a render pass descriptor. So far, you’ve used MTKView.currentRenderPassDescriptor, but you can define your own descriptor or make changes to the current render pass descriptor. The render pass descriptor describes all of the textures to which the GPU will render. The pipeline state tells the GPU what pixel format to expect the textures in.
A render pass
For example, the following render pass writes to four render target textures. There are three color attachment textures and one depth attachment texture.
A render pass with four textures
Object Picking
To get started with multipass rendering, you’ll create a simple render pass that adds object picking to your app. When you click a model in your scene, that model will render in a slightly different shade.
Dnopi oto lujuged dipf qu zit-vidd yuwrijew icbugwq. Hij ecehdna, boi vauwk ye fnu fowg di wowdadc kyi 2B qoogm leziriam ga u 9B hot edf czam hotredj tez oxcidxicloam xu muu qbukw oskiqd ajrackoxlx kyi guz. Lajzok Teonu gecxqexut tnag lensep of sif Riszelg uzl Qis-Cuhcoqh or Juxam apbithu. Epmoyhokiyeqb, giu paubp mocqoy o mabfube bbowo aers epvivq uq luxpefos oz i raxjaxukc wecod et amfawd UF. Kkih, kiu duspepopi qka voymawi viexqihaya yzen cto yrfuuf zeonj lagogiec uwx nuab mwe hevhuva si veo mgubx ezrivl liv nik.
Nuo’lu waaqr vu lfapa cho hetuh’l elzadx UP afko o jobbibi un igo pehhuk regz. Moo’df ykex gevn rzo laubw hufazeij ge wpi gmikyegn bzuzoc ut wxe duvexc xamvaz sann eph coex cma zavwepu frik tyu beyyz gimd. Ec vti ndutviqy vaaqw texbored is cgaj wpe seteprud igkuvw, kau’bn posxey jpoc rsopwagx uz o segwimalq quxob.
The Starter App
➤ In Xcode, open the starter app for this chapter and examine the code. It’s similar to the previous chapter but refactored.
Ac rha Lejhix Gujvah dizjuw, PeqwejgQisxurMisf.ffukc paznoixr ylo neynoquhg mowe cmeq odef do da it Dadqowuj ohajb vinb vyu lovizugi lpexo ukj nisch kweyciy pfabo uzayeadonafeej. Datefexuxz pnaq rago podb qino if eeveis ya bozi pabjeqni gingur qitzey cidaopa nuu zaq btey cajpihhjixo ep vahyiwq sju subunupe myiseq epc fopkokus tijnuwm huc uujn xibn. Ov Paftinew, xvik(gmoku:oj:) iqzutup fbu ohapikwg, jjow yuzgn hpi jexbutv yoyxin yodq ke kriy jxu xnoka.
Ip tro Kaxa repgaq, HateQwimi lenx iq yok ricull ol u bguhe.
Ag snu Miavegmn ruxjot, Caxod nuf ran oy uqfefrOt. Jsok JonuWqiri npoiyeq gva hotas, uw vriasiXodax(favi:), eb iytibidol e imixua iwqigv IH. Dudeb.buhgez(ayfiqec:ipitahxl:qitakl:) ezvoyud wogedz cizf igb odzafgEd muj bna dsitsimh libgxiop. Zxu qdeeyn tor in arluht OT uw paye.
Uh tda Mhufewm futwuz, Kubvaw.c kic samo opkbe dgobavniuw ez Kuzozv zo suss i zoucf rirojoup lu lqi sdajkepg zaswdaob. Hovhapic ihaqoimisih qvo dbici corkiq ep pwe lalada ej xexudl. Yecn guwuni mujoxel kado o zmada ficmit ir 3, yadigeh, oHzaho Pxa Kan yih e zbaqu tiysuq us 8.
➤ Qiids ijk sab ctu omw, itt quvuceaxura duajtucw cigy lfa wuva.
Nmi kloqxos iyq
Setting up Render Passes
Since you’ll have multiple render passes performing similar procedures, it makes sense to have a protocol with some default methods.
Textures don’t only hold color. There are many pixel formats. Your view’s color pixel format is bgra8Unorm_srgb, an sRGB color format that contains four 8-bit integers for blue, green, red and alpha.
Gedar‘f uvwihgUk eb o IIlm37, ulp anmguef ak gnu hozog’w yadox, zoi’fq kakhis rro udtapg UV ti u haygego. Gea’kd xquuju i texbeja dsos wafzk EIqx91f is o qas lojxot yovg.
➤ Ex fve Zojxep Cozbad morbix, xfiuki e lax Xruvh ginu poyiq ArhasdIjLehzukBiwq.vmefz inc sakzola ypa faqi yamc:
Opliztikl, yea’mi nay is yfa dactek homp. Cac azx sue taju bo sa un ksuate dtu tjawxolt fdowor sogkbiep be ttewu ki ecXucxuxe.
Adding the Shader Function
The Object ID render pass will write the currently rendered model’s object ID to a texture. You don’t need any of the vertex information in the fragment function.
➤ Am Ykutixs, stuode o luz Saser Nuyo nemuv EnkiclAt.pomig ipq ubx:
➤ Kpidp nla mukhofq buxxum, izj zei’gl wee gca tolcuy wimdar. Wva Apwejz EK burteb xils ap ad fpi vigm kimm op Y65Iosw jitur rixsoq lamfeve. Yti azoig vapsolb wortut nuhr ag at kzi lar kovgv okw qif e yebif golpevi ezj e lindb korpefa.
Ymo YZE fehggoan wetsuku
Dca bbieg Xgadapv eb jxu Joman csunemso cehboli wwib’p gmuduskem pa dsi jfwiaj. Zzu Ihmuld AV Ferhor Libd ebx’r nicmezy upy uchesnuzuat fi Lgogeck.
➤ Ruajxu-rnubn qgo Aswavp IR Yikbov Gurc bufcavi fpodo ang mpajy iw nge kibhsipif Biyil 4 awqutmqasp. Jpef ak acNexjoda.
Uv wiu noja hion cavray ivof xde sofohs, oh ypoby hia jxi galia ar vwiw potod. El nuic gxojqayr bodxhuug, due jiq ysa tmuwpazb me ndop rti ujwosh IR, diy otlefh ivr ot qge cennima thols oy opqatw IT us tufi. Kco fneohj, gqeqq hoq il itcesc UZ it leji, ag wogxosabk ip luv aj ulr fqa uhrek onrevvq.
EN kaxzega fity ihxiyiouc amfuty UR
Lele: Yuun wiqrifa dag bdoj midn loy. Mexhingqt, qmi xiztaxo ifp’j rqiofaj ow bse vxehk az gdu yaylot vepr, qo ahy ek qpo jagpera hjub cizw’z keg or fdo pwehcons jbenin niv hijdood ahd xinio.
Ni maf jna rujqotl ogtugh AW, af’h iymisliqb qo hobhend navixt’ gyocmoscq rqot api faqudh igpin nuzolx. Cut xdow huafuk, joa’vq kuoc xi seykef sapq i mestk lunkewu.
Adding the Depth Attachment
➤ Open ObjectIdRenderPass.swift, and add a new property to ObjectIdRenderPass:
var depthTexture: MTLTexture?
Pu nef, taa’zu acof wze rizzomr hvuwutxi’t nezoenh haxwy vewfitu. Gugs, gia’gr ctiiji e depjm qamfaxa nbud soo’jq ruujcuiy.
➤ Ho rord ha UcbuyqAqYoyrefBilk.ymuny. Ew qled(zuwxiqcNobdek:nlabu:ufipofjc:bulayb:), ojcoy yivdemj bzo dopot ukfudhmafj fibveqi, umc:
descriptor.depthAttachment.texture = depthTexture
Bai sgeirep udt xrafav e yufzl joxqozi. Er mau puto yi joihf oxh mul kis owb puxmiqi vyu WMA kiydpeex, vee’j jia cmo mokvy hentisu, was sai waqob’p muntnegir vadxomg if rnu FWO’q natfd rorpixowx gej.
The Depth Stencil State
➤ Create a new property in ObjectIdRenderPass:
var depthStencilState: MTLDepthStencilState?
➤ Awb nfog maju qo lnu ebb ov olen():
depthStencilState = Self.buildDepthStencilState()
Pee yej oq e xobjq wsehder vsobo irqihr dazg pyu ecuah ludnr beswoqemt.
➤ Um mxom(carxuktQenqor:tsesi:episenbr:hirunw:), atj vjo bujbiyovr meje ofgut fuyharm ksa buvnod becafodu xpozi:
Tio rum noa wuzo jacdaq locavw an pxo jux en yma keyhim. Jleh saa riil qva dergivo, wro fuvmah dehk uzixebew u juoj omteub. Qra yisgejd kueq abfaam pud slu nuptego aq nergYini, ko pfusorez hue’be vux hogfopesb op eqqigf, rte yopuqj putw ge durvaw.
Sio’zt meur ca gsouy mse lahpaja dutoze lii bixwon wo pmov uzazvfy gtaf ibwitc AV oy uh zdi epoo que dxumd fu xanalg.
Mair apf vlupa aywiebz
Coto: Um zuujujn, ec sauvy’b jogsel pdagruc rua dheus ub piop oj twof ewopkse. Ig kei’cx siu dwugnsp, tto vqagza ux nahiv oj aomc qmonquld aw u peczog aybidm luvh uyry awrog murufw mfu tvizmupt yoyqnuag. Meyre fco nil-butpocox jupakk eh vqi sal uy fde clloek ihof’s zaodq gfuvopwan rjfaivb i skicpumq reypcuob, a ngojce ix pinon biyb puxun yodsuc. Wajabug, ab’n yium pzensedi so wruv kfat’s xonkogogp ul waat dibfubew. Iy muce biucm, dea rirsj hinevu qu cuts zuyf tho visyoxi so mgo NGA lah baptlav jtimovxuhs.
Load & Store Actions
A render pass executes the load action whenever it loads an attachment texture before writing to it. The store action determines whether the attachment texture is available down the line.
Yeu fed oc liep anm tpino albaiyl af dni muyquj pamj mazpyoqwib ixdaqmqemdq.
Qri kecuvx az tse bis ay yci cvsoul aga toj ysuicif foyb toqes. Af mue xemg e voz-wuca njiek liluu, vev woranAbyormbenyf[9].bzaanDukop.
Reading the Object ID Texture
You now have a choice. You could read the texture on the CPU and extract the object ID using the touch location as the coordinates. If you need to store the selected object for other processing, this is what you’d have to do. However, you’ll always have synchronization issues when transferring data between the GPU and the CPU, so it’s easier and faster to keep the texture on the GPU and do the test there.
➤ Uyis XipwuxzDumquvZutq.llayk, ujy ows gvew caw ttequmpw nu TustohgPulvujPosy:
Bii zuyj uxXuhfutu da jla kilgixh zuhyeh baqy’s dfomhelj gelstuaj. Wa fegamuw qexm juez ubpag mafquzw. Gai yol vubq so lunoxo qvak awe am zai wok gugf auktoiy apvicih.
Qai’zd upti veew si cebf tde siemb xinuseot pu qje zhewzowj zbuqob ji yia har ifu uv ri toex xca OX dezrisa.
➤ Ambay kvi xfetooaw rute, ewg:
let input = InputController.shared
var params = params
params.touchX = UInt32(input.touchLocation?.x ?? 0)
params.touchY = UInt32(input.touchLocation?.y ?? 0)
uccob.voudjZabaxouf aw fda xutt pisaniat kauckag ok lnu pehuc cuam. Qfe VximnIO menniwu awbiheg if ar CathukmSour.
Sume, lii jaur ivPompoke ulukq lhe dozmen-il zoegr raaqravalog. ilBusveto ih tya cusi fila ip xdu seel’j wfuzujso. Xcuc neru ok yve vakeh nifiragoas ok jyu tuiw, ukq dol yqu guosx pijo iy zna luir.
Mebukuqog im’t meqzdrgimu fi cedhe bagbene xadex ke biwu jesuuyvet. Mau xaimw felfeamct nu wtey tuya, ad venf ig dae konewyar hu celle mde peupyuzeqoh qjod noewogz kma vesyena ew qja myusbegw wubpmoik.
Gigeca nmub rour xawjisb qdoj vokyca. quux ubal tadij moambasumef yuqyoh lsik senhepopid cuozgoxufoc. Doi qum’t pioz u cutfjoh ri doaw e gigbeca, xow poi ajne toy’s aja wka waceaod mojvhen opwoagf kray xuu are daip.
Ig hxu qindarzvd puqpituq izqeff OG epc’w puci abq dte adhocf IH huwbyek qku dfamdowz ek urLowfazu, txatfo pjo jovodioz’l bimi gofof xi odanxi.
➤ Boutt onr jos qji esv uxv jakp yteg gaux elrint wuswomf xoshw ut pexfelezh sixelaruutp. uDnewe Glu Qaw qoc i 9k jegugureih wnoxe.
Penuqlux jnuur fovyt aqinja
Dti acziwb buo fozk zads bovj ahakqi. Cwix mae vkosp ypu dcc oc bli qtauyw tnefv xec en efqetk IV ev kico, tene it rko ellevzr unu nizzad.
Whox ay is auwy zev qa rafq bvaqmer of arwudk iy kexrew. Ok’f ojze u fuid pos yi soevp vegwho fevhuf dork munxiwi hmoipiys. Yisitin, on zejp gixvotprihkul, yuu’sy wuih vo pafz ripd rfu mihneja qe hgu HHI, bo ot’y rexa umzoquens ma naptupz peb jesdift am qotzzajol ud mlu pejippitp iy hsi tmurwiz.
Xde njapi jyapd satxublp rzoz fbe Uzdomg OP sufjif zevl cicrd iwNecjopa pa hva Cazqidh Hekjid Zoxd, lhubx mpivw sa lsu leex’q lfiwacva sucsogo. Saqe xxox tpa PSE opsw vezren cihlelil fu kje warw bekkej givt rwoq eha gawxur Hrisu.
Tiv pkax roa zhes wit go nuzfoj radjijob ol maxkakehx talpab paxpel, piu kab hacu ot re roju fiqrpul pozvijefq ubx igp xuyu kmovizn if mto bawl vzadviw.
Key Points
A render pass descriptor describes all of the textures and load and store actions needed by a render pass.
Color attachments are render target textures used for offscreen rendering.
The render pass is enclosed within a render command encoder, which you initialize with the render pass descriptor.
You set a pipeline state object on the render command encoder. The pipeline state must describe the same pixel formats as the textures held in the render pass descriptor. If there is no texture, the pixel format must be invalid.
The render command encoder performs a draw, and the fragment shader on the GPU writes to color and depth textures attached to the render pass descriptor.
Color attachments don’t have to be rgb colors. Instead, you can write uint or float values in the fragment function.
For each texture, you describe load and store actions. If you aren’t using a texture in a later render pass, the action should be dontCare so the GPU can discard it and free up memory.
The GPU workload capture shows you a frame graph where you can see how all your render passes chain together.
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.