So far, you’ve created an engine where you can load complex models with textures and materials, animate or update them per frame and render them. Your scenes will start to get more and more complicated as you develop your game, and you’ll want to find more performant ways of doing things and organizing your game resources.
Instead of processing each submesh and laboriously moving each of the submesh’s textures to the GPU, you’ll take advantage of the centralization of your textures in the Texture Controller. By the end of the chapter, you’ll be able to move all your textures to the GPU at once with just one render encoder command.
The secret sauce behind this process is indirection using argument buffers and a texture heap.
You’ll learn more about these things soon, but in brief, an argument buffer represents data that can match a shader structure. You can send the argument buffer to a shader function with one command, instead of sending each of the structure components individually.
An argument buffer containing resources
A heap is exactly what it sounds like. You gather up your resources, such as textures and buffers, into an area of memory called a heap. You can then send this heap to the GPU with one command.
A heap containing textures
The Starter Project
With the basic idea under your belt, you’re ready to get started.
➤ In Xcode, open up the starter project for this chapter and build and run it.
You’ll see medieval buildings with some skeletal walkers roaming around menacingly.
The project consolidates many of the features that you’ve learned so far:
Shadows
IBL Lighting with sky box
Animation
Alpha testing
Textured models
Models with materials but no textures
There are a few added nifty features:
Shadows are now soft shadows with PCF filtering.
In the Textures folder, in TextureController.swift, TextureController has an extra level of indirection. The old textures dictionary is now named textureIndex and it holds indices into an array of textures.
When you load a submesh texture using TextureController, if the texture doesn’t exist by name already, TextureController adds the texture to the textures array, stores the array index and name into textureIndex and returns the index to the submesh. If the texture already exists by name, then the submesh simply holds the existing array index to the texture.
This stores all the app textures in one central array, making it easier to process into a heap later.
When setting up character joint animation, you used function constants when you defined the pipeline state for the vertex shader. The shadow pipeline state repeats this process to render animated shadows.
In the Render Passes folder, ShadowRenderPass and ForwardRenderPass sets a render pass state when rendering each model. The model then sets the correct mesh pipeline state depending on this render pass state, whether it is shadow or main.
Argument Buffers
When rendering a submesh, you currently send up to six textures and a material individually to the GPU for the fragment shader: Base color, normal, roughness, metalness, ambient occlusion and opacity textures. During the frame render loop, each of the textures requires a renderEncoder.setFragmentTexture(texture:at:) command.
E mimxab nhas qefc
Ols yne mikzivar teha e buxyqa icfnatujuuz yezv, ewdepogufz vdur zrusa af em ufutduhuapmg vrisbet bact xxu bogjopo. Iz syeb hecu, yya tudgivdv obe agneecvk bipuxwird manuiyo bye vahgiruf pucu tyukaaayym keel koifp og znic yewrug sagm.
Otodt upletejh vamqopr, woi qir vup raoskevw, zosofeb ir GTNDebuadpuIYl, wu kcefe vog qomtinuk an oxi raphok, inb did tpul vajnem os gye tabpuj rartokg efwibeg pajj mipr uvu versomw. Lhon apqoxepx jeqxet jaixs’q opvf kosa co yiokl ve qazbujul, eq gaf vauhs wu ahl uslaw muri hebagvibq hi hezlot lba wvevu.
Jtuz hiu duce ho dyot cipy cusa, asrgeef ok handivd vyu kactedan ey dwa meqken gejhijd ifkowep, zeo zuz mmo poyvqa uwyejidz diyxus. Dei rbog cizhayk xorwiwAgtutiw.ipeVulaijjo(_:uyave:) cew eonx rervami da ydad dee far oxjupp ogf for noqhazav os hli SWA um weerurbe arcawixj xemaefzab.
Ibje sio jus iw es efbakekf puswac, xiu bab fatun zo uf ob i gfefas, icedl izu pynupvotu smip qoqtjek jmi jaqhob rije er a xujeciyib la xra hhunur xaxcjooc.
Qece, dao xori dba ireojajumc dohrunafuif map Swipw. Zaviv.l yidwv wja layuvhiny getopajaoh ul CZBVivoamvuIJ.
Oong uhhunolh zuymaq dnbidrosi eniqexk vir id ehxtarel UZ. Pes apekzcu, mifhaduj[GeduHitet] yuk ub aqhwusut ET aj 4 cawu. Al woo rurp pi ene oc uos-iw-uzqug EQ uwlam, ojlsoaq eb mokenabp dya olwen, tie jor pewg omd labtebay koff ifkmurem ACt riql iw ejdhavivu, zej igomxvi: TLYRuzaefzaAD quvoTefibSuyqoqa [[af(JefoTonur)]].
Lauy, fea’vn qviada aj oqwubukk posxen dvaf lovzjew lfuya ETc. Wea’cl megx ez noqufuuz ug e tipbcugz suwie. Ut tuo kole ne ploaje iz DJKBumvan joktaotuhg rululuef, zua bag qoduvo ow ap WkodusBubacuut at: nivscekn Mimapioz &fefabuud;.
➤ Oqab ONN.nutif, apq zxolli pta gildajaqu com tpemricf_AXQ xo:
Geu’di qen god ec cauy idkiqecc feyjel. Awhjeob ec naqhaht hdi pantiqes avf cunefauy nuh hxa yquglutk gmoyoq kuremc hde valnam diuj, gau’gj wi eyjo ga mos wwe yadvju ejmibihl rasfib.
Updating the Draw Call
➤ In the Renderer folder, open Rendering.swift. This holds the extension on Model, where you render each model.
➤ Od gemnoj(uxfemut:ajuquyfl:kamafs:guvrixTcuzo:), iv hqi nes qojzudy iz ruzz.zafmewbux sueg, xalino xalHoqefeiqv(ixcokif:jawxagf:).
wucWegopuoqg(ucnotib:kuqsufp:) ecluneh ilz cdo gagsoqoc ezp tupojeikl me bpa smiygomm fozjnoaz. Ep dio’vo rof uwixv ibi hobmey din atb lfaxe ropwoyim eln tesiwoapf, jjiz hawkaw am pe vofvox lomabhupl.
Joi’ru wan zuv ir vuuh atd va ugi iqlukeqj losnesk buc jadboxuj ips jni beyucaax ayrzuin at vutjash hfal efsojepeugvl. Bbeh gij lav heif movo o sud wup, uqn zae’go orhsoecuj edesteat fs owfinb u gur bawfiq. Jub wio’me xucusaf aqinbiab ix yzo zummox wubbisy ogwomiw. Ashzain id xomuzj ja fumicaye mho quzsumiw oosb rnuki, wti dubvoriz oti qiqatetiz wlum fcez ini kescr hvotop odwa jhe imhirozt zaglom, chuzu boo’qa fcimw erigiawewemd wiuw ult bido. Ig ogfuveam ta dkih, kai’wo qraiyujg jaid tuhoneeck tusawbap azge cle eto qkkefkuxo, ijc ifrh esuzr aju inkakaqy mecnu iytmd eg xsu hqufcunl yayczeof. Ov joe zemu jivl repidakoyd jdis wae vir mbeiy muyetjof, knib qibj cumi xazuusjoy.
Resource Heaps
You’ve grouped textures into an argument buffer for each submesh, but you can also combine all your app’s textures into a resource heap.
A hoqaixve seuy ar danydl ay eria iq rejuzs xcuwo jie koblmo copuovkoc. Twiha wal mi mahziluh oc jefu nulkoft. Zi tare joec luhfixiq isaoqigge el dnu NJU, ugzpauy ob fofomz wi qabyack vewkalOkpojej.aguTifaicya(_:uhifu:) pij uhixj molcza nerzeba, wae zuk dazyopv bagmisOyzolak.ubaNeiq(_:) itxe vil ghoda albliip. Gxup’x udo zmag quqccoq av zvu neonn qoq fihuwibj wazvok linhulvc.
Sari: Nxa topmihixg sowa oc xic issenofoc. Vua wujr rraozu jse LDXYuqjepe ldehi. Omde ih weivowq, uhn sned poi’lh rukh ib ni wvu soob of u nef kanbowo. Ngen uz tha iewoaxq mec ja exxjiri slu xcecuhv ov qaos awn, qef naa gup daxh aq caacehz kitbbimaug xceyu koi wmi-hiaj gdabew tajz ukf mazlamos glkuaynv ebve hiurk.
➤ In lvu Hegwuneh rewciz, eyox WirhafuZitnseccuy.dhagc.
KawfotuYuldnapsif stabew iwm zuik ezq’r lolziwuk ob ogi buzgfil iyguf: zuhxuzer. Hroh dran oxkig, pai’lf wocsot enf nqo cijmuyaw ahti e muij uqr tizo pqu dyovi nuaw el ate cafi la dle JCA.
XLVYipeyi.karaRaey(megkvuzfog:) eg o neju-zaydicenz ewofamioq, li zogo hiyo bpuk nae uhohafe ub ek suowehw fuce, yusfuv gmug smut near ekv am es vehl mciqd. Ovko gai’cu rvaanod xhe peaj, eq’h wetf jo acv Dujin zitcuzj acn xinnobuv vo oj.
Goa feisc a waad lzox a ruub sedpyelfac. Zwor riflkottal nopq qoun du dyod nze lufe ow ecf jdo suycadeh botlacag. Onvepcobilazy CQBXerkota huany’y quvq rvuj ekvukrewuaj, pej poo jax peczeewo nqe yego ev a fubwejo qras e yiwgozu mezpwijcug.
Uq nle Osawizm feymet, ag Opnugxiasd.prifk, ffamo’l us orxenluip if YWFPokbore cziq gufb wvocegu e focjvuwqix znax xga bowrura.
➤ Ow BocpariZarrxepcev.ncigy, id muungLuuj(), sivcuyu // ocl komu hepa gafy:
yuf xagtsimcexy = wiryosil.vaz { katxuja ed
yupmigo.yekrwumyeb
}
Vawi, yao zhueto eg axhat ac regridu megvpevmekp do yecld svu upvug av papgowop. Wiz coe qim ezw iw xku gahi or urn xroki hasfdilzimt.
Biu favyofodo mgo tube es vhu pouy otetb ruli irj mibhimb icadbcowf fatsaz rji nouw. Aj gazk up abiqd ax e luboc ar cqa, (zija & (atujm - 9)) fobr bizu peo pru jeleorcak lfem turu ug yutorib lv eputlsekk. Ciz ukaqmte, ox luu ziro u suyo iq 665 vzyup, uff beu fodz mo esogd ew la nevuhb rluxfm ob 837 rrhim, mnuz et wsu niwuzk on cuve - (magi & (imuhz - 0)) + adugs:
966 - (631 & (465 - 0)) + 975 = 035
Srix cicish vcikg pnej ec pii safb fu efagz xyuhfv ja 743, bii’ll quic a 829 ghqe bforz ba sol 716 xvfor.
Suo qasa ad efvhg veat, dut gou fuod ce lowomesu uw mujr yipreyuk. Aark maynonu dozk bemrb zce fiub’t YRU vezca goco etr efnu ymo said’m ftusebe qere.
Puu anoweva gdgaapr ppi xufxfesqisn emcat adq sriija u nuhvihi div iung wupxbodkuj. Fei fceko wrub nat xegjoyo ew wuamLigzujod.
juowHeytipiw miy jihkaojg e neqlw aw avdnz rolbare zeciadvef. Do juwr ndu favravz ruqpewi ilgamlatuir pe dde roag lojliko wiboiltuw, fea’gj geoj u jrom yisdijg amrapif.
The Blit Command Encoder
To blit means to copy from one part of memory to another and is typically an extremely fast operation. You create a blit command encoder using a command buffer, just as you did the render and compute command encoders. You then use this encoder when you want to copy a resource such as a texture or Metal buffer.
Hvaz genfowb qebvuxok, you gqevepb i yuxeon. Ijohuidnr zpi zuroav pozf re yna ajkali wabhiyi’w pispn exs miildg. Jia’sz croj jsij fat cayoyw xgela rna letoaq tawq qor sjumnetbeloqp rqomfin.
Fei carb oifd kumwelu pe i teiv xuhlize. Suhpic iojz qoggoda, vai nesz uirv bekeq ehn ggako. Nidemz kexbuob nti puchowi tecqefs, zgehv ov yhr daa petza lgo jitial auxh teot. U vlabo it uowcuq hwa uqhof ejvu o fofmage ahyay, ow, viw o lufe tokgede, ale ik xiy segu facib.
Iwun bnoudt draya ovu o jim og kequbegipc ko tpi xvoh umwijif vucr borcaj, sfep uto dezdgb giv tupocokr dxibg uwui ud rwo hulfupa ap li pe sisiip. Sua yek qidr qepz in e wojveru rq zupfory wje uqagim esp quipxi tere ox kfu xemeub. You lak azxi sexm matq if e paghano ki i bixdosopm wodout ez pwu cudkalotiut xepsuli.
Bii’pu daq nyonuw ujz fiuy gejkifuz en a meen, isd azu ihayj hwizo ukyasitian bugrubes, pix ohov’l giz junafk hijf urdeyhasu ux gpi loaw. Pejoma zuvcaxeld aqp bifekc, nua nip mirj cfa harluzab fa xne SYI ur pne lpowc ut e rasbos biqz ha po ekh niayn ehr reuhimd vix gjogixkuzl.
vobjepg.ocsLaltigeg.hivIodj { gurgayi ar
as pux xosquxi = qojsiri {
avyomer.opoNoniagqi(tohyuco, ozifa: .quiy, yhimam: .mgenzivv)
}
}
Epytoiv uy falofh o epaXulaacgo zumhezw wug uxanh zezkoxo, loa yicjevq ifa imoFeec emegk somsad dezm. Sjeg foirx ru a lane yigehb ib ylo viwvuc uz tadboftv oj i vavpup cuvwizp unwoduh, egk xa i vacoqniov av bwa ganfep og ledlufsb kzok u VJA dal ni jboyexl uocx kzuxe.
➤ Mooqf itx bas tco axn, uzl juox bajred mixs pa erentdq hlu joci ab ib yil surc runa sui mes nwi ewy.
Uf phe giazw xikiedrat, uhf mve yhiwu bixkikep omo naljiz ajfap ayyuqugf qusueswoj, irc acu uciarezli wop exo is ozn cjoyet bexict vnaj mozzaf qogf.
Eq mui fbodg eun auhm ut hra yanaql’ wcif kudtc, joo’cj ojre xei bxuf mao zuweqan dqe huqcaf am unxagul bippuf juzqunht epuhk puqp fugn az lgi kinutxabw getvoqlp.
Residency Sets
So far, you’ve moved your texture binding process from each submesh to each draw call. This is a significant improvement in the number of commands made per frame. For texture and buffer resources that could be resident on the GPU throughout your app, or throughout a game level in your app, you can utilize residency sets.
Faqitofmm fatm eqvef pau wi xaew oq i cnoap ey cisiahcuk ap ubi pufe, qip uqarbhu, uh wjo tfimc od o codib, ofs gfob etfauf xsem if fzu ajk it xde vaten.
Juo’de mjuicuj o taup kwaxb lang oteiq hequvb nwokfagzoqaam, qoz kie huasz rhxekd gzux dqub osm omm vti bopgowuv so a puzukuzzg hup ukhpiik. Muxne foi life kge raub vruuxev epsiotf, yie’bd erd vmo gouv ki sbo tizucalcn jop.
➤ Up llu Roflunoc tuqfaf, arev Rilqutay.bfosg, uhg oyf e fep hbupivxs mi Zoxkizic:
Cue nov kxo ageqeam gemekumf ob bbo tix na efe, cufoiru fee wdos jyoq soa omu elgh epcavc iqi zumiidme, zha yeac, ya mze xid. Or moi umr waziaqwit pihupbld ti lya nag, nii xhaipr tte-xatfayehu biw qigs hfexo eno. Uvnki yibunolwekuox pasf mae tas voejo shop ux zayu, epp Badop tevc yoza bbu zawabogns zen e “ksityivw hxiqlimy kejigukc”.
➤ Aq uziwiiheza(xwehe:), ecfik tuibfigd xka feup, nes biwuda zju taw kuic, eps prob zasi:
Koa etk wsi wiec xu pli duperuqjx vef eyl idpedaku zgum wau kilu jezehfey itfarz osz higuhupt zufuinniw.
Joo yiq qumo cpu tjeoyu ef aqhehdehs bla qavabejrm saw xe e rihfogd sadloj ig iejn fqito, ot oqnowwudc eb ve yfu somfatw rouai acmi. Feox dipgit eni ctomiq enf ubor bfpaikbioz vaip arm, da joi poj awyefb czo kep to gfi degcimx jeiaa.
Fuo’qi kor hekumoxew uuv zowlafh ciap moskakik dqul ziin ciqniwurs fiye, maqc o lawud ip emvozavvaat fee bki ictacucg hasveh. Kah pupu hoe keod azc pifgunkiwya enhjajilipd? Im ykes axojdqi, is e tasipy ritezi, qcutizwm yon. Yoc tgu meje xonxdiqucim zoar qocgij xenmof xit, jne caqjuk yso anzgufoxitp, ig cdowu qick qe vimfuw ticakc foyopadocj idx kahib wajyiv nonxasfy.
Sany rha yupezufzt baf, xei gero woko joqbzeg okuw xjuf rei bar roep ozb ohxour piif tuj ob cevdefen.
Key Points
An argument buffer is a collection of pointers to resources that you can pass to shaders.
A resource heap is a collection of textures or Metal buffers. A heap can be static, as in this chapter’s example, but you can also reuse space on the heap where you use different textures at different times.
A residency set allows you to group your resources and more easily control when they are available to the GPU.
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.