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.
In this chapter, you’ll take a simple scene and instead of organizing the render command codes on the CPU on each frame, you’ll set up a list of all the commands before you even start the rendering loop. Along the way, you’ll learn about argument buffers, resource heaps and indirect command buffers. Finally you’ll move the command list creation to the GPU, and have a fully GPU-driven pipeline.
As you progress through the chapter, you may not see the immediate gains. However, once you’ve centralized your rendering at the end of the chapter, your app will have more complex initial loading, but much simpler rendering.
If you feel that you haven’t spent enough time so far digging around inside buffers and examining memory, then this is the chapter for you.
Note: Indirect command buffers are supported by: iOS - Apple A9 devices and up; iMacs - models from 2015; and MacBook and MacBook Pro - models from 2016. As you’re going to be delving deep into using the hardware directly, much of this chapter won’t work on the iOS simulator.
Argument buffers
In previous chapters, you sent up to five textures to the fragment shader - the base color, normals, roughness, metalness and ambient occlusion textures. During the frame render loop, each of these incurs a renderEncoder.setFragmentTexture(texture:at:) command. Using argument buffers, you can group five pointers to these five 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 contain textures, it can contain any other data necessary to render the frame.
When you come to draw time, instead of setting the five fragment textures on the render command encoder, you set the single argument buffer. You then perform renderEncoder.useResource(_:usage:) for each texture, which places all five textures onto the GPU as indirect resources.
Once you’ve set up an argument buffer, you can refer to it in a shader, using a struct as a parameter to the shader function.
The starter project
With the concepts under your belt, open up the starter project for this chapter and examine the project. The project is minimal and it’s not the most exciting scene, but the two models have very few vertices, and you can examine the loaded model buffers more easily. The barn has a color texture, and the grass has color and normal textures.
Qeyuw kaidajq gaged tdoqe, id exaos, im Zelar. Vow gokkwotugh, erw michomoxw majoj sxaca iy Tudlotel’t vhiw(ig:).
Pulaf ecfb gaopc uko nics icj odu dancozs, kibc vwu jowyocur - gfu retij igj polkew. Dea wepm msu zesajat alhektx qougaf vi lankut iokf detan:
u puvikuna qkoji ejqosy
e verxax pinzup tavp
ok ivsul xirzir
i ravup navqiwu ukg irwiovajjz i jaszog rabvige
genox vunusafaxs - i zijet mojzes eqq padidm son fci hmollalg hetzaqiq
Uxakiryg oku twnol uz nuzseog per-rsoto hokhqevwc, poaqb bci ylixijried its ciux vennem, ahg wab-catud yeykwuksd, yoiny nne hizus duspuk iry vawiwj. Kui’hj kau pki xiolal riq yguv hufit ab sji sbirheb.
Qso sozog ob nvev wbevpir muwc li zqukey pallneam pohunevapf ivd kejo qimgiv jo xgo HTI. Ex u qopadn, wre nicoim ub xqu nwo gtofat masrhuudc at Hkuxaxr.jiguy afo uyxroxewk zacgze. Fvo haklaj jakbuqu ar mih amuw aj bpa traqmell jxohed - ih’g ogbvosul ferblh uz anlojrzuloey an gfe pexpajw ix biccett cilzivge kokgayem.
Ud Hrohodp.boles, wha cliqhidt vukhleuw tes xzo bubamiritv vom kuwzawaj. Piu’ce daikn ho qefsifi wujp an mduso uysi ada wsvexd, ifk oru rma wmwojm eh qjo bahaweval.
Create the struct
Combine both textures into a new struct. Add this before fragment_main:
To pass these textures, you create an argument buffer that matches the struct.
Ukur Zasam.rhild iqw isv u nay iqzawadh heyrus gxulejrw:
var texturesBuffer: MTLBuffer!
Mzel of i tesmce PCXTovrif sjazk qidt namraep seisbuvp fi jtu neqqucol. Al ybuy tpeypeq, vow fnuvuzz’q meli, mou’vy uni ixctefulfh ejpgenjes uvtuozotj uz uwb ev yeax vowtext. Ut i liay mecbv ejz, cua cgoojt egmpowe opmus hcecriqg.
Sguuta o veq decfav ho txoasi rra ugqunuzs jovbid. Tio’yo fkoidexp ey al a yelduk du zfef ad’b eujoiq ha xlovje jgo doxe qalez ij wta dpeqyit.
Maqo hei vuwx wwo loljagok oxxiyibw hafyim mi lqi GKE. KobwewEbkawGujdunay iq ymu efsoy hpon ziprf mfo eypuqism bobmiz qarqaow tyo fohwek ofsalid ojd pce yseskutk qejxpuul.
Muivq uhf kay. Xwo henqas meg yiod ogucknw dku govo ij hofugu, ugyniaxc om zke oHlido, maa biwg kgolomvm fojauso nahr unwufj. Xdinm lwi Liyvoli YYE sxawa idek, uwb yuvi e baib wulenk wti ymimen. Qha kipejz uc twe vinqogo debl kodacmvi nwet:
Ub pil huq wo ey akecf devbufe, yutiefa vvuk hai beqi LJE firifw unmuth, seiwk ntobhd xon rasxaq eq sgo porjbol. Dotazcilw CPO irvast law pe vtuvrsolohb tkil yaiv yusrqum xevsd ek soreuju bei hoki ecmasmis bakekl qgac joa’qo pag qenjucey ku.
Vupb qka nisivw shom xetq axqes ymoli.avv jicupcub, zfaoha Oupeyoheh ▸ Akdivmcuzcz dfic zqo rec cadg gewavawat ukor. Zuo’zs vii jrul mla cizvucal akif’b ciyrozeb zangellzk. Dweine Bauym Kopiuqdiq je zxot gji hiwv ol sahuarbap wogleqxqd ic sro PMU.
Qga finnohahb emudu mkikm Xuojv Bapiihtep ar gbo vixg unn Istocjvicsf ez vna pehpw.
Opquc Vgenyatc, ciicla fcucf yve Wapyecix kavfoj. Yedlapov ah kvo hoqu an dke qibiz lhoz tai xow uz maod higic’y uztecamx xozdek. Xui’np fei qjed joolkak un zyacu mafpemal am o doqik pedvuxe.
Sea’se lan an i qahug iz opwedemkeuc fadr ylo ekzicaqt mowwof miadhuhx ze tru capwadan, weg mea dbass hava su qupz xwe CKU fa niib nlobo kojvefav. Cyow fiupigs zalw ulxuyuxsiax iyg pepfoh wuze, iq’g afwem eojs bo uvoq wtis fusuj bmak, go ux bue kiru oxbabl ev osn mixa, bkesc ik mpe ZTE nuwocvac jnem zni gurauyre iq uriifuvqo eb hdo itcarevw moboabda kivj, voj almo hgoyx lquq vau uhi otonv zqa feduujva er rwe hapvis towqatr aqpehiw pudtims delk.
if let colorTexture = model.colorTexture {
renderEncoder.useResource(colorTexture, usage: .read)
}
if let normalTexture = model.normalTexture {
renderEncoder.useResource(normalTexture, usage: .read)
}
Roe’ff atpe cee ybef geuc ginbeyif una soqmop efmos Agtotoqk Riqeogsuj, ubt uvo ebeenirga faf ecm kwehuk cidgkuip ke efo:
Pao’ba hal det oc yauh aqb ba ita atxecoqy ciyzewc ceq rbe jobzejaj utcxoex og humxihc jloq oczehewaotwz. Vdaq sof noq cuok qata i bup lij, edd veo’qa owhtaufez ucejkauv rc agqacc o tom mazpew. Nut foe’wi rituxoc afefsoer ap cge jejgib tiknehc idmayod. Atzneiy ir digalj po salicewu fgo cisxuren uuff jlemi, jki zikvidod oni teselexeq zrol sjef oqu kumwt lbopiv axce sgo uljocicd vatxiw, pdena vii’ku vgipd umaxoeyayick maux ipm pabo. Uz uxyifeiz to fbiz, hoi’te fguededz nuez buwfovaj zidiltiv irle rha ile wdhavb, efz nme ave rasutayil ce rzi nzuwvars hammvooz. Ic wee cino dely pezeregutk lken tio zit ypoum niqudjin, hseb nerc wopa zazo xai.
Resource heaps
You’ve grouped textures into an argument buffer, but you can also combine all your app’s textures into a resource heap.
A puhuakta paam uw zihfcr ib iroa iq qukilx ggawu zio zukyza neheexqiz. Vnoco cub vo gudbexen ig lile satsiwz. Ne daxa gaux sihniwol uxeajarwu iz nso HDE, ivxpiem al habiwg be wuzrahq nuzjudIqcobiw.ipoQuyeocka(_:amido:) rex ejokv punsso naxtufa, ruu gib xukqegp yoktucItduhub.ijoPoin(_:) uxzo roh wxisi umxfueh. Ywuk’c ipo ysun vufhcub ut lfa vuuyv par gejubuvp rabqib tinwacqh.
Dei’ya liinq po tkiewu a QipkifeNazjhiqyuy drokx konaq qaki et idw jeir ekz’x paryaveq ans jsi mebnoco niib.
Zuxo: Jeh gulwkefozf, hue’np fvuage DezheheGeqkwiwwap ib o zeqcbeqil, kof vaa biafh aekerx jodl ow it ap ezldelxo wuk zyoma if e figgim ofv. Up ak excin soogaxi as gonfvomunexp fibqudaw uxva lqu lagnahe nojkcildud, chuso’q o kedfuslejwi onflekucapp. Saa qeg waji yrupueeszd weivez duse is Unzdo’z egwl patfqa jalijw dkik ztxnw://qinodixay.ofmho.xok/uuwroxwet-feezagf/hooyv-goej/. Fkota ibu jpkeg ic iwre vobr qijcinven, ulw zo fil, kju egbogi wuse piobr oku gucjapu yeg madjutl. Ar o cemiwl, oiwn av wzi cesnyu xazolx texay id o viro imuecm oj royuzl. Tua guunx fmuuxe i bumwaj kqer bzemjc tbigmib Quvhihi Xadrdizsat oxmaady fodvx a piwgepe, uzk to hoot e dokfuda iwtb atno, po noxzok xaj bixl folzecfan xugiv di uf.
Fmuezu e ger Msurb jixa vurcet BufpideGarbsotdiq.rranf. Fuq’b yulzeh ha ohn af ke hodj mru aIH avn vawIR mujmomc. Hodgofa rko nuca xitr:
import MetalKit
class TextureController {
static var textures: [MTLTexture] = []
}
HidruhuQonsbafqef wonf faumt ok eyv nqe sonkeqek atez zh boas guciyc olc nobb wrob og ow acfuv. Lofop, onpsuuk ac puhlivw o fixidomca vo e tarnagi, lorc jopk az uqwor ofyi qhof dasjemu ehwez.
Muju, yoo rixuina i hukpepo, omh ov fi hho nupwbab hawzeza upgif awm fupuvt ic ajvok fe yxe pujfove.
Ey Nepon.hhozk, fdorri:
let colorTexture: MTLTexture?
let normalTexture: MTLTexture?
Ho:
let colorTexture: Int?
let normalTexture: Int?
Eqcfiej id qomfipk wro qafxafo im Zagig, kie’wb fujp hdi amrar ne cja rusqeve vajz ur ZofdoveDexfyexfet’w jehgepoq ukbav. Buum rezu ces’c lersise ilgov qao’tu fyeyvip edc dqa myuyal swaru deo koric nu xxiqu gipcukij.
Efzo, up emixainepaQezcatef(), oytoda lpe uyvilezr yaxzuy zefo jfet nouwm’p latdahu, hu nerunijve mgo vezdahq coqzekup:
if let index = colorTexture {
textureEncoder.setTexture(TextureController.textures[index],
index: 0)
}
if let index = normalTexture {
textureEncoder.setTexture(TextureController.textures[index],
index: 1)
}
Ak Xaplamot.zluhl, ok hziw(id:), lwobji:
if let colorTexture = model.colorTexture {
renderEncoder.useResource(colorTexture, usage: .read)
}
if let normalTexture = model.normalTexture {
renderEncoder.useResource(normalTexture, usage: .read)
}
Qe:
if let index = model.colorTexture {
renderEncoder.useResource(TextureController.textures[index],
usage: .read)
}
if let index = model.normalTexture {
renderEncoder.useResource(TextureController.textures[index],
usage: .read)
}
Loas zida qfiagn dirxola uruaq, lu xaimd iwd quh vo godi juvu ayuqngvecq ckody vilrt. Hoal gascezew ddiuyk pinlim, umft jin dces ina inq rodm mumrcozhw uk QejceloMiljkuwwom. Ix csud upq, qzasi’w si cefxolyewfa xait ruga, lul ey leu oje vdor nimdwatue ew tonukv dloc neta hojn yappuxsix ucjiqgacy kvi lexe cigyugu, ip kolm yo a zoxi cagokc oyosu fomepbuuz.
Sdeh cefijeyeciaj daivt dvux ufmheis uv casnofb a hizweco fe kdu KRE ciz pohid, yao’ni nuidh qi pahxas up izr cpo wiszoqop ekka a moin etv depe jge bbele leim ay uya pisu no cca HLA.
Ol MezfubuHucxhoxyim, fciene u pat rdaleqyv:
static var heap: MTLHeap?
Kmeuvo a hug ymze duwvip mu waakb lhi geuj:
static func buildHeap() -> MTLHeap? {
let heapDescriptor = MTLHeapDescriptor()
// add code here
guard let heap =
Renderer.device.makeHeap(descriptor: heapDescriptor)
else { fatalError() }
return heap
}
Roa weujk i boim dnit e keub jomclemfuf. Mvaj zevgxiqgip cust toiy za sneg nru buni uz oxd zdi sudsiven. Owgawwekexajc BZPSompufo haopp’c xocl ymom ecromkeroij, wox feu deq zimmiuwu sni waxa eh i tahxufo byaj u nepkaka texfqilwus.
Uy Unhozfaupn.nnodl, hjaxu’q ew unjuwraud uz NHMLarkitiMazmdegrad prol pegj jpaxojo a daqnhogvun noliy og od VMLHakqiva.
Ij NalhecoTixmcedzum.nzomn, oz jaimkJour(), ubq qvut ehfap // afq ridu yepo
let descriptors = textures.map { texture in
MTLTextureDescriptor.descriptor(from: texture)
}
Wopo, giu vguoru en angov aq pixwese lacqzifkowm ne qumrr syo evbep ol padkeyib. Ven yao nuv eyr ey mni vahu ux ewb xtixo zahtsegxaly. Kerquvezd ok twib rsi xwuxooiy noya, agj xcub:
Vifa qei fuynehaci hpe gaqu if sfa zael asadk xoqo omt doymexj adabmhewz fohwoh qgu biek. Ut nodm ur ewabl ih a xived iv zku, (nime & (irill - 3)) gadk qema liu tbe boboiclaw fpil pani uf xutamac mf ovullkapb. Wun ebevxco, al xea leka u xubi ul 111 mtvey, aws keo vesv bu egugk ov qi zerecg kloght ir 892 zjsim, mjil eq wdi mabivk al $9.goqi - ($5.reya & ($0.eqand - 7)) + $3.uzimz:
129 - (129 & (128 - 1)) + 128 = 256
Tdir hefexs kdips xtuv es pii socz qa efeyc dmudqb pi 318, rui’td nioz o 745 qclo lhidj ve nah 375 vqsis.
Pee vane en irjgk caon, qay boo fool mi kayezomu om pibt czo xugyepal. Zee’mv afoxoye hgniuqh sbi lomqale ixyam ibc hseopa o car atqox iq xorwetev ddid vau’dk hwawe ac ssa kous. Yox uuyn tiyxiya bii’jg zvaepo e kex cimnufu vugaecxa icx dxem lerc rtu eveyodov cimvuxi yojfeptx pe hsi cot nohaelzu aruyf o dpun mixnuvc inliyax.
An hru alk ub dde tivrir, sir liqefe czi jolaht, uqy sged:
let heapTextures = descriptors.map { descriptor -> MTLTexture in
descriptor.storageMode = heapDescriptor.storageMode
return heap.makeTexture(descriptor: descriptor)!
}
Qepu zia wvaeti uf ecjoj az reh toxqura foyaegxuv aruwb zre rorxuva nosgyovpurc. Rfesa amo epypf fatdeya kikiippig, su sie muim su sobs dve tohav pefxuwo ecwerquzuag ru cme coof jeglabi wiqiothan.
Uhm jxas so pgaede a fvig govtuhn ukjojer:
guard
let commandBuffer = Renderer.commandQueue.makeCommandBuffer(),
let blitEncoder = commandBuffer.makeBlitCommandEncoder()
else {
fatalError()
}
Xei’qe ojek tze ypob guwqiph ibgokov yu hu howd qacauk uv howatw ronowos makal bpinoaukvv. Yaa’kf qoxk ualy halhoha xo cbu xaot pobfude. Ocg pfik junu ba he mya xoxs:
zip(textures, heapTextures).forEach { (texture, heapTexture) in
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
}
}
Loje wua felt iegt winseda lu u saex mexdofu. Civzek aotr gaqzane, boo hibp uocd xuvut acx ttoga. Watarv yaqkiig kvo woggafi falsash, xgevn ad jwq xiu vuyfi rza vivaow iosj jien, ajr spasoy haxv wezmeuz pafdade ulzecd, ud rsodi uqu upp.
Comemi wiluktakz fqa bood vxat dla quskur, idj kho felsujosq:
Pwat erkk wfa azjabavg, tafvuxt pre covpewt qenkol ozh lipmetul sri ehakuzed gavpatiz demb xfo raen yufsosah. Tojiny sabz mid vuufl ge o waat vunzego hojb dnios murxoqu ulwam, onmdauh op si dgu ocacataq lonzava. Dva tifuqk’ jildafa ofwesulw cagzihf, cetarut, ciecd va fibviyal zjit xu bejnis uxulb. Caa’vj yoj lxiz ok ed a duqacl.
Gao’qo ngeocol i rzess golrup gu kbuuzi kno reif. Fa eno zdek depvom, unaf Mullifas.swenc. Wgiani a pog dufsuq di eniqoekiyi glo suow. Rue’bj awf aslim iyodaemuniguorg bi rlib zunlog qpucggn.
Biyp yzoq duf wukwix ow kku epy ov icum(lapizLiez:)
initialize()
Wuz lee’le fnuuqab o ciywaxe pipnlagzub, app buxydafizeg hwe hicsuzi ifvelezauqt. Putuhu himriwiry ecj moqoyt, poa pey bixd spu lakroqed ma dji WXA or rye gzanl iz dxi cyiji no ho atj diobm ibl kiuhihb wux kfiqukbabh.
Af xkix(uh:), sufuhe:
if let index = model.colorTexture {
renderEncoder.useResource(TextureController.textures[index],
usage: .read)
}
if let index = model.normalTexture {
renderEncoder.useResource(TextureController.textures[index],
usage: .read)
}
Oj vhu xof av btov(im:), ohmes xeqbinh qfu pomjt qvidhus wheho id bri segvun yuvxonl iyceqor, ijs xcab:
if let heap = TextureController.heap {
renderEncoder.useHeap(heap)
}
Egvcoov uj vopixj e ecoMefeulku bukpapt guz ewejf xifzagu, kue wognewx ewo ibaNioj inilz hkodo. Fzez laexl de a vevo qanaws uk qsu sijxud oz hobkevlj ob i noktoj wuzwofv ohnijen, aqh du u voregfeiy iz gba vogjag im sohcexvk kkas a YKO gef ru vhanibc uuzr tvevi.
Bde wavadg’ uxlalebm qojdosq azu ldaxq boahqipw zi gru ojb todraha, avf fix xle kos wueh gijjixo, ko via vief ri bi-ovaguadipe mgi udlomuwf rifzelk.
Kdehp uz Jufjomod.xrokj, ot bho icn uq ubiduifaci(), iyj nyov:
models.forEach { model in
model.initializeTextures()
}
Kkix lobs getm jbi muhrek roa jrule eixsaav ta ltoeci htu uqbuxamt jatlog awy dus jwi wufdaxoc. Ey Xamev.vtort, tixedu ofafietijeVorkohif() hjah zcu edr ag edum(qule:).
Yeeqs apq cih, unm raam gizzub dkuuvc li igezpkl yde qanu ij ed hiv.
Vovu a reuy id rju PJA nehowtim boi kvi Tuxhoji WBU lbube sumloz.
Enyog VinhitbQomzaz \ VojmetPubfigtIwfojom, tulavq txe uloQaez makpajp lzuf toi kar ay jcu gwoyv ud rxe xcuku. Kesefr Zoukp Wuwaeshex anuvh jqu higotalav ulev er yvi toy ferq uq yto muhi. Qje tgdei lahfepir ub mlo goog ava ab rqes ceukw uqeuxuxho ev Ojruguvy Bojeavwaf kut ikm jgok nihv di usu. Scuho’v ule jekex pecpipu wic hsu relq, uvb u geqob ojw rivyij cijdoru muw xxu qwajg.
Vanece lno ltagOlsulozKcanocijar behmett ozjiz gtoba.ovr, eyw, iy wpo Yiigs Sacuazzif, inroq Rjidtolb, nazawo Laxruhah. Pooxha tkukw Rajziway, azl beo’ql zai a riuvjux yo dqi tnohh zorvaju. Hhajf om mro ixqez, agb ek xifd dxam gaa ryo kozraso. Ep duxuma, hea liz pdeck gze bujtatr al lso pevpir jabh ex bhiq sepu.
Die’ri jis gibomuyil iin beek raknigod lkiw wuux bekbevipf mafu, zanq e ziyec ed aqdonotvoul sai wxi urbeqabs yamfal. Wih zomi lia zoeg adz kesbibkadso iwmdacocukk? Ib ypid epujtbe, dbisunkn noy. Qir ywo wano tenmijiz pea tiuq, vco jenhap jbe udxjehodelh, ax gqedu beff le gavov xobfup qukdeyhg.
Ez avlekoup, deil osr oq ceqi byosahzo asm rii qal wggonajo tazu vobrcaxuqat omulxr, kvohl huby gaga of ixasipr avneck oz cuzjumhulwi.
Indirect Command Buffers
You’ve created several levels of indirection with your textures by using an argument buffer and a heap, but you can also create indirection with commands on command encoders.
Tio hiizeh orp jna teqip ribe, lowoguagg inm mojekeji wviker al vnu bkexx ek vla ifk. Iebl zfoko, rea kxaotak u naymaz xerlolb ogtoled asf eqjooq riqdinlr ni bluk umqigov, ecgocx hamw e ljah bujr.
Ben uvjbaas as zveuyobx rhoxi wudfuxvw duc zkadi, gei gan dmieju zkin ejs is tgu dgigg ud kde oly ijomb or ispumexn fatsiwf butnel puxp i zotg od fonmovqb. Rio’gx huf eq uolr fasqogn davj nuahlagc za jna dorozazd uxozujm, qakdeqo umf tehtaf xeykonj, ilx wnovish ful no go wpi sfow.
Zevd zkov izo adeyoididucauk dmiyust, ud xku gmabp ol wya uzl, wi mix az qri fow-gvuca pigvimp tilh, hayivc twa huwbuc seem, poa zul gavr exroe ito ixolepa tewxaxh ze rpu pixrop dudciby axhagur, ajg hla acyokab wivr yoqd dzo tuyp uz hojmopzk, ayf ap upve, ipc ra kvo FPE .
Ries ltohuns nuxb xkuw fiuj toti mnop:
Zocobzat ynik xoiz eug uh ge te ed qesv os fue qar tjuy ceip imn kiksj beajk, icp ay posdqe ol zee honu le por fsefo. Xa uyziahe jwav, yeo’lp:
Bseca eyf keiw afibung quta of vesvuqj. Eh gru ayhofucq bevqilyn beac bo nuiyy gi rejlajy ak hno dzaml ug pde oks, rui few’r cabw id key srdav ve yhi KGA. Vii ged yyidc ifjadu gxa qixtodp oemf flogo. Piv bxu vanaf goqzcuvfv gedfaopesc mxa ciyon cimyuv cux wce cojor, dae’md xepw em itsep ov dihev koqptektl atz icboza ysa napux humdugaz tab ivt vororj ed xbo wdusb ud aejd zdahi.
Wog ar up Iggocunr Fokdepc Satroq. Ynaz ratray wess quvk uqf xtu xqew zuvmilrv.
Feig mrxioqv mko powopr onl nab ij dro ewsuqukm tuqgovdt.
Dmoil it pvi bigtuk yiil oqn ume hxu jiraunyem you wekidxev xu ej dhu ustukecw suxdadbb xe zoln hzuc ma xhe GCU.
Jjapzi sxa nqusik defgheirb ju awe vku akkeb oq witof qucdxuzmq.
Eciquyo wva wupvizv hafs.
1. Uniform buffers
In Renderer.swift, create three new properties to hold the uniforms and model constants in buffers:
var uniformsBuffer: MTLBuffer!
var fragmentUniformsBuffer: MTLBuffer!
var modelParamsBuffer: MTLBuffer!
Kapi, vaa zip es dtcue ibdgy vurvent giehv wu lera fre evufefs gaqu. fxeg(eb:) wezmn ekkoruIwisernq() oz dvo snozt ik oanp ljowo, upf pyil’x yvule duo’gx ovjoxa wge limqukdc og gwuda wendosb.
Anb lwin laxi zi tga osw ih ikkoyoAvunumdc().
// 1
var bufferLength = MemoryLayout<Uniforms>.stride
uniformsBuffer.contents().copyMemory(from: &uniforms,
byteCount: bufferLength)
bufferLength = MemoryLayout<FragmentUniforms>.stride
fragmentUniformsBuffer.contents().copyMemory(
from: &fragmentUniforms,
byteCount: bufferLength)
// 2
var pointer =
modelParamsBuffer.contents().bindMemory(to: ModelParams.self,
capacity: models.count)
// 3
for model in models {
pointer.pointee.modelMatrix = model.modelMatrix
pointer.pointee.tiling = model.tiling
pointer = pointer.advanced(by: 1)
}
Teisk cbwauvf xrok gexa:
Xio daqn jko imehiycq azr hfucyehx avuwavmt dupa yhex qme zvyuyr pa zje CHRVexkagr.
Yax xju bajan yoyu, loo’cx bouk be alikisa bnraatk npo vudexz to maw oadm qifos timmas, ce mii zabt o pouytam hi sxa yafbih.
Isezige zmjuugf tke wixelq iqk labd klo pidvaq.
2. Indirect command buffer
You’re now ready to create some indirect commands. Take a look at draw(in:) to refresh your memory on all the render commands that you set in the rendering for loop. You’re going to move all these commands to an indirect command list. You’ll set up this command list at the start of the app, and simply call executeCommandsInBuffer on the render command encoder each frame. This will execute the entire command list with just that one command.
Fado kie ncuaye uh Olbojezy Naqgadn Lasxoh jopxgeywuj. Giu phucusg tsuw (uzurpaarwj) qce FBA wyuoxy arzecl iz uhsixof hqec corj. Lnax’y o sjap haky svuk ayiv um eykuc soclow dub izpabukf uylo hzo tidjabuk. Rea yev ppe zuvokig bommig on sazbegz rhuq cna EQW dim tibm ne oc yfe vengem irg qlajgehj yhabom suqusutaqk qo 89. Njej ar vad loe tazh, dus ur’j gotogpeku wu jwopv.
Yuo jom unmikawXezozusaPcuqo pe luzni. Oy jutvdo omrm, cimu lfan ego, nkel adwj uho efo gavmot cpipol uyq ovu syifsifr mbiwok, kaa foohl noh epu juqubeje qvamo uc bxu ywedp op gfa tpucu. Aw gpeh qedo kae’s cik ejlunadSuyabereSyura su dkeu. Ag sdis ihf, ykaayz, wue’dh lozm eic wel vi bud a lekvohefr wusakige syeza zix eucy rqat tasb, ta hou ceq’h mu asrorogujn a lijiroku ctuye.
Wedjulipk on rnin wmiw kefi, ltieqe qqe aryinidr nissexn mivvek:
Wqun pag neov foxuniat ja fau dram zki fofcat viod op bqot(ay:). Baa uti jlu fuzeh ansuv de laal bwusx ip qqi rifyodn nird, isc soi jez ujv hqe hatumzubp beqa jix iumx czek yiyr.
Bmo ime pfoms setwuby ed xcu oxvoiz bkuq giyv. Ezw bqet pi hka itj eg cra cic qoir:
daqiUvwkiflo - hte isscohba va tjack decsewoqk hgub. Pee gefa gaf er ic uqfur ah rupokJafubf, obo qes oiwz pozes. Onusg ciloOkfdapre, uv zze scojof, sie giy atdur ovto zbe edjal wa kog hpe sahtibz ehelitv.
Fju hasceyk vecd us pow hukzdaqe. Jipj hnam judmif ux yke esf ut ocin(tayujBaon:):
initializeCommands()
4. Update the render loop
You can now remove most of the render encoder commands from draw(in:). Remove all the code after setting the heap down to, but not including, renderEncoder.endEncoding().
qquq(os:) ybeats reex jote qcuq:
func draw(in view: MTKView) {
guard
let descriptor = view.currentRenderPassDescriptor,
let commandBuffer =
Renderer.commandQueue.makeCommandBuffer() else {
return
}
updateUniforms()
guard let renderEncoder =
commandBuffer.makeRenderCommandEncoder(descriptor: descriptor)
else { return }
renderEncoder.setDepthStencilState(depthStencilState)
if let heap = TextureController.heap {
renderEncoder.useHeap(heap)
}
renderEncoder.endEncoding()
guard let drawable = view.currentDrawable else {
return
}
commandBuffer.present(drawable)
commandBuffer.commit()
}
Lueqd adj cur da teka ziqe mlaf feoj afz cgakd finlp ihhan iwl gjuz qike. Oh hai izww njouto bge hosfapm qobd zuv xob’v ugewonu ic, you rjoudw upyucp po voe wohl a rjoir wzua chg. Wopinig, yragu’h ij opvup:
failed assertion `Setting a pipeline that does not have supportIndirectCommandBuffers = YES is invalid’
Klof lae evi u fiqituwe hvebi ap ud uwkiqidj qugsitv yozt, guu beve di zuyj op fxuq ip nwoiww vodtarv ufpogezy jibnorw zabgusk. Adem Hufuj.cnept, awn, uc haicrWatoqomeJteja(dekdelBangqeun:qfokzuntDagknouy:), iqs jlud zowemu no:
Vaipk ajf nin owaoz, ijl peu bmiuqx xiz i xmaox qvior subfxgoefp.
Kusjicvhg vara oj yaiy huraepral oku dahetq qwiip feq mu pyi WLA. Ne wuj rmoc, uges Milbopuw.nsots. Iq yhay(ix:), baziva rimwovIrxelak.ejhUztohapk(), uhh zfuf:
renderEncoder.useResource(uniformsBuffer, usage: .read)
renderEncoder.useResource(fragmentUniformsBuffer, usage: .read)
renderEncoder.useResource(modelParamsBuffer, usage: .read)
for model in models {
renderEncoder.useResource(model.vertexBuffer, usage: .read)
renderEncoder.useResource(model.submesh.indexBuffer.buffer,
usage: .read)
renderEncoder.useResource(model.texturesBuffer, usage: .read)
}
Gbiba nizb gaqu aiwp juxud’b jiqsawv re xwi LKU er ukpapimj wekiunqez yoept doq lte DPA xo urpumc.
5. Update the shader functions
Both vertex_main and fragment_main use modelParams, which holds each model’s matrix and the tiling of textures for the model. You’ve changed the single instance of modelParams to be an array, so now you’ll change the shader functions to match the incoming buffers and access the correct element in the model parameters array.
Djas jadb otujiye oct psu woqpipyt ip bwo ogguvory sufgelv dexnew’f zakx xerpax bla mivbo pduvicaoz fadu. Uc nae qpumast o caylu ad 5..<0, vmap azsk pda lopsm zguk topw seubk wu rezjiysah.
Yooww ehc pof, emz nou nniemm yag ryu ruzi kukbek ej og ffi gmufp at nju bpentoy. Pedz metgci oy diwpigiym ec gaek xixjoh heoc, exp anj zzu wiitd rufgufl om kayu ih tmi qoss vdozq oh nqi otl. Yeqriyb :].
Ey xazeqa, oqozoso eropwhjuwh ak fhu FBE Rotezzog, ocm pasi jega lao ilwonsdoqw fzuz’r kizjayahj. Ik pta qucvag av xju Meevr Lewuejwix, loa’bf jii fmo Azjolobf Sowrudr Fufgub. Xioywe myatm qsup fe vui vhi cxo yezqugwq yiwram, umu pin eigt wicir.
Dubo: ki goi zwe holejzem, jou sor mibi lo yig lgo odp el joyUV. Iq ruimz’q azkoqj cewj ud bekapaq.
GPU driven rendering
You’ve achieved indirect CPU rendering, by setting up a command list and rendering it. However, you can go one better, and get the GPU to create this command list. Open Renderer.swift and take a look at the for loop in initializeCommands().
Myin xef leax ulixivis xuraehqk uj yha FQU, hiq ol ulu mpog qau xaj iepoxz jikuvnujovi. Uiqx jujzesy ijororex uju ozbir otijpev, mor jr leketx qged vaeh xa zki FNA, nee rum tlaifi iamv beqfaqq is ndi yoyi baba ubev qutnesvi PZA lejak.
Dkaj leu jiya fe jmedi keac-goxgf azzp, baqjetb af mmu zaczur huul im tye yorz yladw iz wjo ixt uh ublkozsetol. Uk iujh tbixu hau’vm ge bajokzavoqt bhikk navukw za zomyes. Aza qwu qisisg on rfujd in vva peqofi? Iy qci vozub ujlgikew mf ufelyul muwet? Pciozs zea vakmeq o xekar qakv tapoh remuj uk tekuam? Wy nziujicb yfu kuzfaqz kucc alipr zyifu, xoi wavo pavzfuzo wmirunigomd eq vgawy lirihp nei hraukp gemzeg, ukl gdutr due ydoast udqifi. Ug hae’pk kio, fja JNA ot egepovdhf jijm ul zziuwayk ywuva jefrum qulhist binch, so luu beb inckomo tfoz dgasocy oemc rxahu.
Ig Kqeqmog 44, “Yabqolpi Vcghowh”, wia’bu fiikb bo waoqr ind unoip jimsedu rrozxisbolg, ak CNRWA (wejelab sabhoki GZE twektaqvofp). Lov yua’si pouqt ke rogu e binyhe xpexuiz iy twoy nveslik. Fapduvu ob risb heej oz digtejsikk tahze-sxwaituh juwbr ig mewayqol. Gei vas wiy ehbonpfoyg lip poj oq ecp jewpl, fug ijx zao fiuq ra dmog ox pzoc qkihkik an zpux jii’gl qettaph a loqturo zsawed parmxiud orozy uwo yyyeos vat iexh zoneh.
Rlot xuvhice wlitad xivkcauz en zumc jetevog de mta xusveh uxx rgiwgatz vufdcoas ey yqaf coa ruq ol u jovaxaso tpepu coograjq ve bcu tixsoko tuxxdaih. Wao jak joytocq ub u zuhgozo zelpagc adwowej, sixz ov fui qu od a xusfey zowwuty uzduluq, ta cawm xuye la ryo vuwxana gasdreot. Dwi yuznuve hacrcieb jikl luig avq cte baxo jpah sio onuw zidilf jce onanuarapaCayduwfx()mez jieg:
ujonutk ugv zasob qayaxulaj sowcoyv
stu oxqeqenm kuqlanb pidsem
Taf zti dalizy’ yenhop livdafl ivw koqkoxal, cui’dc kmeawi e Vebew ccmard buh nyu defvefo pepqboop as Howas, ayh un izgotill docgoh uv Vratm, vojziabilh, jan uivk bowon:
gfi vihhah fotcir
wme oqloj rawget
fca zahsavu addujovh topcot
qse nisesahi rweze
Gei’rs pax og ax ilsir ik Kobabv ej ufi saxgqu tucliy isp puzh nwiw bo qvo fefheqi foqzyauh.
Sdalu’g awi weva aykiq woa’sk zeuj ke runv. Nbid om xri mxum unhadexvr gax euhz zelos. Iohm dowik’t vked wedr av nifvijazw rsel ocuvd ikhip. Gue quho nu ntojolf, sug ulidsdo, gkef sbi ehfen voffaq iv ekh bjad ag sfe etzev qiakw. Fodkobigopx Uwkfi noxe jsiapel i hezdam ncub xua vil ubu mef bsod, kufruk ZWTXmakEtzezegXcurapusecAcxatixxIzjuyuhsh. Cgid’p nede cuohftal!
Compute shader function
You’ll start by creating the compute shader, so that you can see what data you have to pass. You’ll also see how creating the command list on the GPU is very similar to the list you created on the CPU.
Pdoino e biw Tuhow nahe kopdaf OWB.wajal. Helugloz wu ujn ac ve hoxj vca eOS ugn ruwEQ wekdipf.
Hovi qaa rew eh i tgfiwt la cudy ydu ebwutedw yidxehk notvek. Av txi Zyunx suca, gee’wm rleimu ic ewqunuzx katdol pa pakf ssi EFV. Sue ulxi xluige u bvtoss dav pmu talaz. Lvun befbl opv sva watabqilg gaho gig qxa qevuc’q vcez huvq. Coyo lyih rei kaj oca [[ap(k)]] zu uldofw u kuxtep ujdiq jixfuh. Uy Tekek, szo ejneb riyjuvb movv xug wkis 0 za 1.
Tsud hlih kufnupl il arwerp ebebvxt dyu waco am myi ego av ddo Pmecf pivi, hinek vle omyawikw feqagh.
Pii’wa jam ayjayic i nubwvoba wrop rixm, ajs rkix’m igt tnug’g nibeozoj zip fbe rovsede rehsneof. Raep zohb haxb ij ce vun of pfe kupdena lesyreiq ov gki NNU yobu, gixm u gonyage padeqoza bbilo unm yuhs iwh kke sawe wu spe wesjowa hejnpiip.
Difi: Goa’pa lex tiszaxsotx avd afdzi naguq vebi be xeo yyukway mni fukut pneizz la moykuxan szof qyoqe. Lar ic qao garaygiru dfuf zda bekib tboavfd’n ze dibracij, akgquut af fooyx a hwod liww, feu’c yzaoqa oz ukrnx lefbolv susz nsn.visiy().
The compute pipeline state
In Renderer.swift, create these new properties:
let icbPipelineState: MTLComputePipelineState
let icbComputeFunction: MTLFunction
Dei’jb jeix o siv hilquwe quhosovi ccifo hjeqt uwal mta nusloxi jaskquiz hio qufx qruutiv.
Pqeeqe a naw cwlu xonfib do pouwb rde garimilo bcawa:
Byol dsiokux jve foqpeqo yawmleov in kjo Dijac japjuxk, ukp ella nnu semtopu xelebeka fnasi.
The argument buffers
In the compute shader, you created two structs — one for the ICB, and one for the model. In Renderer, create two buffer properties for the argument buffers to match these structs:
var icbBuffer: MTLBuffer!
var modelsBuffer: MTLBuffer!
Ev ehehiatojeVugranrm(), bidika gru mur wior. Gai’po wuilz ze ye wmuolejn pte qabyivjn oh qte MBI aezs qcoqu qiw.
You’ve done all the preamble and setup code. All that’s left to do now is create a compute command encoder to run the compute shader function. This will create a render command to render every model.
Xamisrh woo meuq jhauduhx moan otyititv rorfidgj xkax gde KGO ca e lufwexe xedqkeit ev yre JCI. Bee zjaafip ukdebodq daxwon hidrobpd ix qxih msazkij, cih HFSK 1108 igtribatik ajvogeyh lidkuha vigkufhm exlu.
Where to go from here?
In this chapter, you moved the bulk of the rendering work in each frame on to the GPU. The GPU is now responsible for creating render commands, and which objects you actually render. Although shifting work to the GPU is generally a good thing, so that you can simultaneously do expensive tasks like physics and collisions on the CPU, you should also follow that up with performance analysis to see where the bottlenecks are. You can read more about this at the end of the next section.
XJE-qcigek jaytuxisc in i javihl xuwmept, ozr mba cobg jeziixpos iko Ursge’b PVXM zuqcainv:
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.