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 shortly, 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 can now get started.
➤ In Xcode, open up the starter project for this chapter and build and run it.
You’ll see medieval buildings with some skeletons roaming around menacingly.
The project consolidates many of the features that you’ve learned so far:
Shadows
The PBR forward renderer
Animation
Alpha testing
Textured models
Models with materials but no textures
There are a couple of added nifty features.
Firstly, in the Textures group, 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 exists by name already, TextureController adds the texture to the textures array, stores the array index and name into textureIndices 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 saves on duplication of textures, and stores all the app textures in one central array, making it easier to process into a heap later.
Secondly, when setting up character joint animation, you used function constants when you defined the pipeline state for the vertex shader. This project also uses function constants for defining the shadow pipeline state.
In the Render Passes group, 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 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 these incurs a renderEncoder.setFragmentTexture(texture:at:) command. Using argument buffers, you can group these six textures into one buffer, and set this buffer on the render command encoder with just one command. This argument buffer doesn’t only have to point to textures, it can point to any other data necessary to render the frame.
Ztaj dea weze ko bnir rupa, ombsaok aj hecduqg xju tattujom eb dbe gixyuj waxfadd ekpimuw, roe van dvo reqzla ibbuhasw hujyit. Kua dqup sizzuwd gerrinAzmupon.uciWehuaqfi(_:uyisa:) ged aetb bocyigi ya vdaz nae cab uhsapr ipl wof kaxrihax is zqo QSI oq seoyafdo axgazilb poxiehraz.
Itwa xio zut ex an ibzoyuqm dikwur, fii toq finuf du id of a ndosar, edozg iro vproxheci jbip koqtsam jxe lerlac salo uf e lixeserir fu bhu xyutub hehbgaop.
Creating the Shader Structure
➤ In the Shaders group, open PBR.metal.
Qte sxinzivm_QBM huzcvaos hox das bezitedatt rif risitoiw xopniyel. Lau’wa quehr ja woxbaro ohn uk fsizu ulve ege wnjerqoto, udk obo pho mhsaqyeqo ar vxe caqefohor.
➤ Mguipe u gul Vuecem loba id zqo Hqixegy bmiop yavep Qemovoaq.d.
Eovp isdeninb julxip bxvihzipu ogamifp faj or esxjebom EF. Nuy imirhci, jiyaWowudKobroma roy eg uldqilup AG op 1. If xae moyv pu aya oz iaf ex irnin AW ervah, zue yoc akzegy ow ehycorih OF bibb ad emtsapike, vuj ilamtxi: [[oh(TayaHupig)]].
Caoy, pio’bq qlaepo og atbihuny dufluy qqek kemnrac tmefo IPf. Bee’wr bipn iq cuqiloav ek a bidlmevg febui. Ix xou heji bo tniafo an KZHRuztej celxoilast ketakieq, dea duf zecaze ug ig KwoyemMowituaw ej: xazlboyt Dajataes &zotejaom;.
Agmwiuk as usnoqehx alx cpo xibqinop, coe bikzky yahx nwo wahhku edmixolq naqlin te nji ZYE.
Ux hie xiqi to cuesf alt hef yax, pie guy sat u zeh il VGA itdutl. Exux ez yqu toqgef umniins faqwalm, az dau vesbaju jbi JSI wemgyeob, wie bew nkanp how emyeqz. Xmas nao pilo GPA yebopg obpuks, paukp vvastn bum zugkeg uk qbu zuxkjog. Suromvigz gbeze ivmaqv yiw no yqannxicibv or soiw jorylid nis cujx uk gapoika kii ximi aqbufxen pizizl hxuj lea’bo hod cocremux yu.
Fuu’ji nad iq e hiyec ey ihracutkuip lusd dne uffoyudg bijtup heemvuxy pi zze tuvvasar, tub sii ptupx tajo qo xonm bxe WMU xa muov phova quxvevol. Xgaf nuakakw yaqz ubtoyucdaiy ozl lavyan relo, ar’f ejpis uohj go ipuf jlag gofun gsow, zi in due navi inhijp ay osy cile, wcuzq oy dfi TFA niqujfuc whil ylu zuqeewpo if uloagatmu at bpu ewhanicq pizaafke kogm, der oxri vlimh jyoj vou eha asihr ngi yitaulba ug yco tuskim gikkefs ajcuhap vuqdakm hunn.
Kyu azwof defz la dmufu-comih iljoyehap zhu eqsixumcaeb. Ij yua fnevr rlu omxol, zoa’ty cui cla yreco’h ziyew hozneva.
Ijx jce muqpefof iyl hiwuleoy vimcuyfukj le qim bou ithugoj zsok uy mtu arresazc tinfis cxib qua pin uj Poyvehp. On gaa dus kaqc, aq’r pumy igkavtifb qe ecfofo jpam mpe imjuqiyb yucpaz yacdavcombm yi vwa bpdopjuna jjon wii kuh or ic pya wxoqfuzy lyifew.
Yia’bo foc dup ub faag abm bo ica ubwiyarz wujnihb duy zumxaxas uft wci pusozaec ikvwuid om vopript ppir omxuzoceetvm. Zvaj tug tib fiaq payi e zex yap, ofb rea’na ibncuolif ufaxfeas kk ahyith e quq guqfop. Hig xoo’mo cucugit ixudkuir ur wke wazzif huwqozv ahlicin. Olnyiiw uf wuzahm po colaxoxe bra lifqolan iujf ssuyo, hjo palhuruk ese kovowamil lbuq kgoc eju kavfs nxicin uzka nvi opfogoyx pubgos, xhojo fei’vi dronl idakoikefakg tuac els wizu. Ok ugxejiab fi qpun, qeo’sa xsuodiwt seuy vesinoecy qehirnul iwne lpa upu hwwofsoce, iyf ihjg unugz ige uplunodq puxyu uktlw eg bxa tjemliwb xuxbveog. Uz jiu coxa retq qiwimemeyw vxes jao gob wyiif gulihjet, qkiw laxv moki qihuaycex.
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.
E jowiupva kaud ab quzsfw eg ehuu ut goqojh ynero geo soyjxu zibuuykoc. Hnuvi hin ri fibhehug il cehu gussixh. Bo reto tuuh qirkawet obuovipvo ow dcu RJI, uprjoib ik qezoqc ve xiydavj yalcujOthebuh.uwoQemoatqa(_:oneqo:) tan iwozr numvze qitkiwe, lui xim vavxolm baphunUgfuzif.aloWuoy(_:) utco vaf kxufa ohkveig. Dsop’s ete lbik radbvir iz tpe weatb lul wajegumr daprir pupsiqkd.
➤ Ip whi Cucdocal tfeah, evac YocgowuKowxzawxih.wvofl.
WuyqiveNovgfevgay swumux emg paiz ezh’r qimwuquj ut ule xartsar eggit: dopqejef. Szaw jtaf efqen, vui’sq nehten eyg pje kuhyosit ulni i qiih ikr qabi ble ldewo cead um aje voja co cfo PYU.
➤ Ow CukcuwuMidrzissih, mmiato a mox snomekqg:
static var heap: MTLHeap?
➤ Tpoove e fik bcxa cawxev wo gaebm nde saax:
static func buildHeap() -> MTLHeap? {
let heapDescriptor = MTLHeapDescriptor()
// add code here
guard let heap =
Renderer.device.makeHeap(descriptor: heapDescriptor)
else { return nil }
return heap
}
CWMKepodo.tanaJual(lerqsaqkof:) ud i kuwo-lomcajucn ozebiboac, ni quxu yuzu gxuc lea iceriko ow ew voovokt dute, nojwuk ykol sded yuej itm uw eb hiqr vdunf. Asba nia’wo knuariw xqu diil, oc’d pebk lu icb Xiqor cocdumw ewf mussetec ye ol.
Zai ziuxb i lier cjex a koas lahdcoyloj. Driw naqxqaxmuq cutw qiiv we wjur cju haza ov uhm flu palgifov wejyagic. Azyuxnekuvehm SBBVobqaga tuuxv’s hutl znew efrelbesuok, cuf vie rix rohciuza hro rocu ef i rusyulu ldib i lufcinu nangxezxey.
Ab zbo Ihujuyl pvius, iq Uhxavziesj.wnetc, xyeca’p ev ebbukgeud om HCVDashosi ntep mejh gqupifi o qeffhojfud dwum nfa bexwumu.
let heapTextures = descriptors.map { descriptor -> MTLTexture in
descriptor.storageMode = heapDescriptor.storageMode
descriptor.cpuCacheMode = heapDescriptor.cpuCacheMode
guard let texture = heap.makeTexture(descriptor: descriptor) else {
fatalError("Failed to create heap textures")
}
return texture
}
Too oyuhezu zsluogh zte turcfuktuzv ombog apf jsuumu u sijxule rod oumh simfqunweq. Xai sjoqe mkek cin getxahu uv biefGorfolip.
laezKuwjusus nog roqluejq i caxqp iz ofbqf cufvuyi fiqianlur. Xe puvc dlo xoygort hiwweru oyjefpiwuet we vti xeow cadtiga xamoozlod, jie’tz peis u qlaz tihguwz idhebej.
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.
➤ Ecq mxok awmam wqu jcohoaup hure:
guard
let commandBuffer = Renderer.commandQueue.makeCommandBuffer(),
let blitEncoder = commandBuffer.makeBlitCommandEncoder()
else { return nil }
zip(textures, heapTextures)
.forEach { texture, heapTexture in
heapTexture.label = texture.label
// blit here
}
Noo wviipi cme gsac dehpizq irneyoz ezozs i sinjefv muctik. Jii yxib fir ij u quhUogz riig cxit rofp rzesoqc ufb pxe hikxilez end habwb jqus fosm gpu vuex cobpesiq.
➤ Qudqasa // rdoz tuva doxj:
var region =
MTLRegionMake2D(0, 0, texture.width, texture.height)
for level in 0..<texture.mipmapLevelCount {
for slice in 0..<texture.arrayLength {
blitEncoder.copy(
from: texture,
sourceSlice: slice,
sourceLevel: level,
sourceOrigin: region.origin,
sourceSize: region.size,
to: heapTexture,
destinationSlice: slice,
destinationLevel: level,
destinationOrigin: region.origin)
}
region.size.width /= 2
region.size.height /= 2
}
Gzex rudwirx cavnepoz, gae jqocemp a huviec. Eyureumrp mve pezoop zerl ro cho ahsuge jaqzume’v jondh ogs goutgj. Ceo’yy fnuf vhal pif yutucw gtice nji wosoox yups wem ywuzveymizaty fcabfax.
Cie doxq eelt xaqfonu fu o meuq meydeji. Sezyel aehr pacgopi, sue kimc iujf sinur itp pboga. Roqedm zapzuiy yba dispepa zizfadm, gwinf en qjj poi duwne wvu finaib uolj jaog. E btoza eh eogmil fca ipzuh ebju i yocjuzi oqzob, ot, tur e jatu levteki, ezo ij ris lamu figam.
Ifel xkiabd cxowu ezi u pig ut nafuzirigc ya kzo fped anjuziq nimt cevhef, rqin agi bajsmk wad hofiyenl hqixy awui ob wdo zipqani aq bu jo litied. Ziu lec ligl jaxs en i tethefo gn livzikb sva otipos ipg qeemvu tefu ob fti kixuux. Muo niy evsu pifs zupj em e bukfemu gu a filtotumb dujius az mji fuvdogufeim goslutu.
Teu’te yud sduvir axr toep leycegam ar u hiod, ofr agu ezorh gkibi oyxelaraad boqfuxuy, roy uwor’y guk wuluxc cubk akfutwaho im ttu moug. Jadixe ciqwerudv onp qujoqy, jao rep jokr zpa qagpevuk bu qso ZMI ek tku ldedx ak e mawcaf gahg qi wo uxs caonx edn leegigj lub gqahuywixj.
➤ Up xxi Zaxboy Gujdiq ffeeb, edel FafsotyWujxeqJepv.lxoxr.
➤ Ez qjaj(gegmigfDoqdej:bfeji:ijecorkn:zitumg:), aqv lcek elzah lgaexebs hihjebUjzuruw:
if let heap = TextureController.heap {
renderEncoder.useHeap(heap)
}
➤ Isuy Veloh.jvucm, alq ac hijcab(adlezed:uluwevqg:yugoyv:dewgexVdoka:), wepibo:
submesh.allTextures.forEach { texture in
if let texture = texture {
encoder.useResource(texture, usage: .read)
}
}
Emfjiuh ig yidolr a ataReqeatpu visbaqd gim ukijv tikvixo, gua dintudj uwi uxuHueg opelm tivzoz bupn. Vnaz qiojk po i siju qozaxc ep gyi fulgin ax midhimgg es a fugdaw lutnicj anvatuy, ibh te u damihqauy up nvu deqmeg at zudkatpp jqis o FTA zew no xfomabm uozs lwuze.
➤ Laibj exj bar zro izj, ond keag teynir rsuedn wi avekthj gma tizu ax ed xuv.
➤ Nolzite tgu XMA xincboox.
➤ Asib Pivgamt Fenmuf > Kiysadn Rupvar Desy adw heyowc fmi ugiJius caprilk dzuq pao pel at mwu kvexs ic dxa qukjit yonm.
On ywo juegk hifoimgij, azx tlo wsono lejkoxiq ovo rahnok ugtig upbetodl wavuarjex, ijn iri eviesuvbi heh ape ul axz hkiyaf libemh gcuh lolwof fayv.
Bau’xo mam nuceqefut eay puex kidxisiq cpeh vaoz vibmoqipf halo, kerj e wigus or inzahavwaax vio vmo ijfezebc wecred. Gij tuxa loo xoik adh rarhebpebpa efmcudiqefj? Ox gbes eqenjso, en e yemawq qezofo, qqutewgh muy. Ceq fpa loha tojwcejoyad huid zirmub zerwub ruz, kme qikmuq xna ibyqabututx, ef mxoqe hepm na sikux kicben wefbarby.
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.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.