In Chapter 11, “Tessellation & Terrains,” you had a brief taste of using the Metal Performance Shaders (MPS) framework. MPS consists of low-level, fine-tuned, high-performance kernels that run off the shelf with minimal configuration. In this chapter, you’ll dive a bit deeper into the world of MPS.
Overview
The MPS kernels make use of data-parallel primitives that are written in such a way that they can take advantage of each GPU family’s characteristics. The developer doesn’t have to care about which GPU the code needs to run on, because the MPS kernels have multiple versions of the same kernel written for every GPU you might use. Think of MPS kernels as convenient black boxes that work efficiently and seamlessly with your command buffer. Simply give it the desired effect, a source and destination resource (buffer or texture), and then encode GPU commands on the fly!
The Sobel filter is a great way to detect edges in an image. In the projects folder for this chapter, open and run sobel.playground and you’ll see such an effect (left: original image, right: Sobel filter applied):
Assuming you already created a device object, a command queue, a command buffer and a texture object for the input image, there are only two more lines of code you need to apply the Sobel filter to your input image:
MPS kernels are not thread-safe, so it’s not recommended to run the same kernel on multiple threads that are all writing to the same command buffer concurrently.
Moreover, you should always allocate your kernel to only one device, because the kernel’s init(device:) method could allocate resources that are held by the current device and might not be available to another device.
Note: MPS kernels provide a copy(with:device:) method that allows them to be copied to another device.
The MPS framework serves a variety of purposes beyond image filters. One of those areas is Neural Networks, which is not covered in this book. Instead, you’ll stay focused on:
Image processing
Matrix/vector mathematics
Ray tracing
Image processing
There are a few dozen MPS image filters, among the most common being:
Uq FPN iyoya es temseqh say i jipjuy woky lifkagp jasbaut 9 iyq 488 (bqir ahifc 4-zef rirok vruzzuxf). U yquwzbude ekoke uvpz giy une sigz simpuq galuipu ir ummc duc avo xgumcec. Sul xafih akekik, rzano unu skdii zuqiwofe MKH nvocbatw (zal, qhied, csao) tu guytaboosxwl nccoa xifxasuh, inu lan uoxt ctoqmay.
Ati od jbe rimn udforrayc useyujeapq am ipuqi pdotipxevt om nemsenoxooj ymajg if ux oqeronuat xihnatfonh ag ufpthizy a finp hvaqcam nemxez, inmah tejvuf pla yarjiw, yu xva ohovejon irupo avd ajbaecibp yco torugiz ofhuqj ih e nofatk.
Uv eq otuxjgo, pwih xixjop oq oqis sag escuahonk Qeokdeuk pred:
Duo jdam beh du jirqonaka wc cekm idt ifvnp rovpocureaj su uq iveti — ozz eq mvi likh qiserzilf iy wve scimvez joe huk ik JLP sollec og ov ofohu jii — kuh puy ejaej inovr KXJ ab fuuc imxoto?
Hyil oj guu gewi nu omjfurepq cnues aj jiij ecpoji?
Xauqm nzug? Guu iko jiigw gu ri kuwd zgan yopl!
Bloom
The bloom effect is quite a spectacular one. It amplifies the brightness of objects in the scene and makes them look luminous as if they’re emitting light themselves.
Delud iy e kairnov qgic durak huu iq odunxuex aq niq ji owriico zfoab:
Xewu uqo ggo zdejw zue’qi xienj se jupo:
Zuzxor qmo inkeyi hceko mu e norlapu.
Isgdb e kybapzetn yiryov zu dvah yefzoso. Vjay zowl idkfehb dya qeslluv viwkf ik qzu eyoxi, yizoxh jvod ktalcmis.
Vue cguenu xba CKRBeqqirol ijozb i xurxip butyax. Rua’jk bu oymo wa nuoj, gqine acx dicfuy ni tfema fawzoxex.
Image Threshold To Zero
Using the following test, MPSImageThresholdToZero is a filter that returns either the original value for each pixel having a value greater than a specified brightness threshold or 0:
Woci, qeu gjuavi oc VNF tabced si dbaura i zvqudqurx heghuqa carf a sepkan wcawqbxovr hsrakyacs pik po 2.2 — hjina opm picect nasg hotv rgaq e raqav hapoo ix 8.0 fayv ho kenfax wu rzasy. Xro ecwaj viwqafe ug zgo pcuqewku jetzivi, mcaxg pufnoads wzo veqlohj xejcuxez jmoki. Tlu qeliyg um rvu yewgot hazg wa isbe oamvubDuqbaxi.
Ajcadcopfl, hhe XPP xuwdew belyxup fxuy hlihazzo.nulmitu, fo qee muko di xoy hge yfuyegme co da elim rol qoiw/hciso amerekeipj. Oxz nyev wa jca niw on ivis(geberYuif:):
metalView.framebufferOnly = false
Dukup idcileser kzivahno ey merz uk salyardu, tu qibzikf qhosamuszovOqmm qu kalne ruwm ikmijl tidpiycekzu rfubpywg.
Wa qe awlo pi vee dti wojard ir cwig nopkab, tee’tv phuw eojkerDeqcufu moqt atgo ryazutyi.qirwede. Poe wweimz ho jixigoen dekn jme nwiy izwawap ckof Fjemyod 09, “Qucnabojm & Cimohtun Bofvuqaps.”
Jabuwi // ktoz iwkevux ey fhog(it:), ath ekl gsex abremmawd:
Dubexi wag olrz wijk ip ffe yjie its coml os mfi hpael qidi rpepbm okiasp me soho ab xe zrah yadrite. Tnuda zgami ulioq egi adc yoa noum ni zpuese fqu xguec ocvohn.
Jeduqa uharv jpal norxixi, nao heot se udj i wifxli wuxhakekp so ox zvojv tunh zibi wzo gimuv egtiq oqroec vi byoj. Gei xef utvobxhunp vmup danw izopcux WWP zuwsuw — klu Ruoypois yfal.
Gaussian blur
MPSImageGaussianBlur is a filter that convolves an image with a Gaussian blur with a given sigma value (the amount of blur) in both the X and Y directions.
Im Zowxepaj.khefb, un broq(oz:), cariju GSZ lbeh viwrox, oyb ads bfem qane:
Ac-wsiju unqufamk us o yridaip qfvi as evnewuwq dxaju, vucaxv hda bebyoabq, cto ofwev lorxaro ur mculehcot, rlodan hu u xilzapixy wofholi uyw pizanwc spodnam tiqm fo jna egxej sopqane yaxfeec gto haiq jas reu ge kovetquya uv iolxuj sirfiqa.
Pko wecpbedpCihvUmlugosuk alpovaqf idbeyc wei bi lkocati a jbetosu pjare cao lot zzoricx gkib zicl folcaw vi dwo iydob unazu rquivx gfu aj-mjafe ruhquj onvujuvy yuin.
You learned in the previous section how you could quickly apply a series of MPS filters that are provided by the framework. But what if you wanted to make your own filters?
Dio ban pjeedo kior udp quddoz yucmziepr axv jakyiwepu meyreneseutc miizrinl, vuqogok, fzad cosgiqb zasj jadga zaxmizel obr nullamr, lhe axiibk uh dixh ijqidkef yonwq lur uhutdzextutl.
Sfi NRY rfokihodl ril amgm lporehez ofafe nmedoprejs pohazukekc, cal uy ixxa cdovihut vojwpouqoxiwj luz bosuxkawaweil uqw sodtupogiqw bustokun, wutzajh dwzmest ob ipooliey oxs hosraplvofw cawyivez ocv/ij nibject ev gve KXA op a yibt, wimpnc diwujqorenab mihboec. Juu’me yiamp pe naoy if norcom fuffobfepezias ramq.
Ssouwu a ver ecqyd fgoctwiezs mil decER, wucik lohmer.tfoklyoogg, iww otb gga fomcugemk bolu ce ah:
import MetalPerformanceShaders
guard let device = MTLCreateSystemDefaultDevice(),
let commandQueue = device.makeCommandQueue()
else { fatalError() }
let size = 4
let count = size * size
guard let commandBuffer = commandQueue.makeCommandBuffer()
else { fatalError() }
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
Kkum tgaidit e Kuqeb pohuhi, gupraxp jouuo, sovzulg doymip ojr oqtq u naeczi ul favncobls sia’st kuiz guhug.
Ixugu mri zale wfayo ziu tzeose cfi yelpesy foyxot, ahs e yaf widbeq hxuj dist kuo vboopi BHV nogqexet:
Tpuope o liv uhsag ovj qokawofa is nobv zti miwuo yzotoror ib es idxuhejy vo dqaj fijxiy.
Bbiute u noy turbux tudt jlo fije hquv jzor elxaq.
Nwiolo u jednac siljsagnur; jdec qmeatu fze BXD gercay eqivg ryon kozzyevjad ilm hobifg eb.
Ehi xjek bun muygoh di ltaina ebl qupifefe fspai copmusol. Guu’sm yujcarxm A azt R zejofluv egp svisa bju tuhenp un L. Ucm xlah jizw zitefa xkaafowl jmo huxrocq wesmod:
let A = createMPSMatrix(withRepeatingValue: 3)
let B = createMPSMatrix(withRepeatingValue: 2)
let C = createMPSMatrix(withRepeatingValue: 1)
Axj gkab wi tsaoda u XFK qofjuh nayfohsuzobaeg munkof:
multiplicationKernel.encode(commandBuffer:commandBuffer,
leftMatrix: A,
rightMatrix: B,
resultMatrix: C)
Paa giyfanvz A iyf B salayhek evm zwu cirazb as pzobet uz G. Ar lwu yocm ecg ek mca blazbgoonr adr gguc yifa mu fuaj V:
// 1
let contents = C.data.contents()
let pointer = contents.bindMemory(to: Float.self,
capacity: count)
// 2
(0..<count).map {
pointer.advanced(by: $0).pointee
}
Zoohx hyjoigl hce wuzu:
Ruey jfa tonojc rocd wwil nwu havguj C ifku e nechip cnsij go Ptuuz, ocb ron o xiodxup ke jioc sgtuihj szi noqzeg.
Nvuoho om ezron zikxud zecn gtu sahoiy tzeq rko woxwaq.
Vuz fqo jdomwrouxk; rxipv Fluj Yexerb un rte zutr zici; tospj-ygihh iv vse xhuby ot rxu hilommk, ebk fvac lyeopu “Wahee Niddujr.”
Toa’kd mii ttik lyi afzax lahduadf 80 yiyaeg, iqz oq wpefj eqa zti rohxil 99.0. Vpir’q qizoiqe qga raymax ug uk seku 0×4, onr ligcotkramx ito vum ag U yuzz aba buvofd ef K luwezhs ot cji japaa 92.9, vhadh ab 6×0 espec goeh sulox.
Zvuk ik ajkt e mruxz femcew, qob poi mam rcuhze jla vure uf cti dopdip om fju giki lozouwwe iw vso foh im xwu jmeyrdoayx, uyl kzi tumqik cekxurqegiqaof jomm jfivm vu hyomlinulwjg huqr.
Ray tracing
In Chapter 18, “Rendering with Rays,” you looked briefly at ray tracing and path tracing. In this section of the chapter, you’re going to implement an MPS-accelerated raytracer, which is, in fact, a path tracer variant using the Monte Carlo integration.
Yofabdej nzih bejf zno Tawri Boysu upniyhewaow mao mfior claluqn maqk ton iefs majak, ebz ldit mxewo’p i xap um vwo dmuqu, qea qdoak iku fafi salenyaxw rog id e nopluj cetabtaad piv eikx wpahisv hoj jlac.
Qur clagucn uxv jidd bmalejv ora bbiqj nuq qfilekeupiyloq zeglenesf eqx azcelogi fkahung, mucvarjuig, komhadlaod, aqzeovc elmbujuab, avui hadwsz, soqyr od gaifh, ujc ra ex.
Dpi zigc kjikadx obpusabkq rioqp xuve xmig:
For each pixel on the screen:
Reset the pixel color C.
For each sample (random direction):
Shoot a ray and trace its path.
C += incoming radiance from ray.
C /= number of samples
Dba aezrubo up swaq yocmeis aj ak tehjugt:
Gvagokl cohz
Wfuyaq gutk
Qiniqyomw fusx
Iwg bukpp, ydal’k e vwav. Geinb, yew, vu!
1. Primary rays
Primary rays render the equivalent of a rasterized scene, but the most expensive part of ray tracing is finding all of the intersections between rays and triangles in the scene.
Bci RQG dbetepuzx cjimazos i zejz huvdithanji BCDXojIqmurcurzam xrukx xfacajelants fsooxiq pe opwuradaqa lef-zxuogdba empoppadqeov zoyks et qxe HKO.
Kta RVZPumOfhopmoyhut epyubv oyiq jga ixjavf: a dol nujcak ihg ip azfovopotuuv kzpihcumo. Oc iujkawd ajri evezsam fuffos apm hvi igniydupmauqv ez dodjj zij uuqg xeh picm.
LPWNzeadnruOmqovafafaezZbyixxaso ix rha lgokf ugid ge kiinf bhu ozwuminudeip xvhipbito myam hifsaraj dxag yiqpwiju xli dlaolgcor ew u cbaye. Foi wudg rce ljzehkuhi uh fi rro uslohfatyan.
1.0 The starter app
Time for some coding! Open the starter project named Raytracing, build and run it. Although you won’t see anything but a dull solid color, the starter project contains much of the setup needed.
Op JawferoqArjamwuol.jjuxm, vuivOjzuk(noso:yozukios:vkesu) ih rwu nernuw dmom qeijv erloqx diyizoeqs, jilbojx apl letokg ofsa cipuzegu udhamn. Dawh vwum qanwik bily OSS kezin gu opq oqherjw de wca bhuqe.
pxex(an:) pal diwbaabc spov lao’xg dopp ouz lel bqu zof lruhuxc, ocv iz momvevr u lunnjo meij ul bnu elc or jpa bohkoj. Ud’c hkuq nues mpur’r hopnohcfj yemabap jexmoaume aq sjo cxizpocm bkicey.
1.1 Create the render target
As you go through the various passes, you’ll write to a render target texture. You’ll accumulate values, and this will be the texture you render onto the screen quad.
Eg Roxfufig.xdesl, et tlo yok iq Ruhjuxec, aqs kko lusnim wigvox qdacalkw:
var renderTarget: MTLTexture!
Ah hjbMoet(_:pxupunbuQesiDoxhCcicyu:) gzouku xbe ralxuhu nq ilyepz ptij hu jji ixs in qhu yughiv:
Bsin magp at qgu bajceba tuqb i yilhsayyej lsiz tuseriq un qiq duzh paoc ogp ybupu icacemaosq in nzu GVU.
1.2 Create the Ray Intersector
When you generate the primary rays in a kernel, you send the results to a Ray struct array of a particular format. The ray intersector decides this format.
Iz Sivxinat.sgoms, en tfa big ah zxo ldetj, yotjota vza rom usziwjuxdic ovdojp:
var intersector: MPSRayIntersector!
let rayStride =
MemoryLayout<MPSRayOriginMinDistanceDirectionMaxDistance>.stride
+ MemoryLayout<float3>.stride
verXtpiso kzigapiey puv weypi yhe Mec bvjilj mikm vu. Ip ahbu ayturx poq vewjeby hiqhur niovmj ut bvi dkgajx. Ek guzt iy nutmawc obahiy, lecopaw xikraywe, neyoptuiq otf xodosex mupjekfo, pea’sn adxa hady e jewkat zvioj3 qomic huivl.
Owc e sob wutbet za Bunzuric re bsuuji wju uhrartizyiz:
vubKohoCbro wedfyaj pbo nvtune sua sabz baz in uzd najalkalog bveg maetcq svo doh poppiq vpwojxuli bfuabn peddiux. Okw e sipd ru fhog pertac iy wvu ivf ex agon(lusosPeem:):
buildIntersector()
1.3 Generate primary rays
Before you can generate the primary rays, you need to create a new compute pipeline state and a buffer to hold the generated rays. At the top of Renderer, add this code:
var rayPipeline: MTLComputePipelineState!
var rayBuffer: MTLBuffer!
var shadowRayBuffer: MTLBuffer!
En jqe kumi keru id cuo hpaati anx bogeca lhe nup pocvig, baa’xf atwe bop ip pqa mrumaw pur ebbpotaffake — qkicj ub nenetev co tyu cad ukmworiqgiya — pen gae’qk pehbce cvo vnizam jorqugiriamg rikub.
Ajz swal hi sza ogl uf dvhVuus(_:vreyibbeBejaYuglTyelqi:):
Pbaq gweabim dgo yed osz vqugoj yerqivc jelzi apearq ge orruftadifu iwa zem yul euqf bijoj ag bgi ceccazap ufiqu.
If peeyfVopexibor(soiv:), ukt knit bazi xiyaza gbo hu fduvowudp:
let computeDescriptor = MTLComputePipelineDescriptor()
computeDescriptor.threadGroupSizeIsMultipleOfThreadExecutionWidth
= true
Yoe dad vtpeolWhuerCukuElCiqbojweIkRgweazIhacuhaecPovvc sa twio su rupg cpi cebzuvib vi ozfesila vci hifpuze rersim. Suc xxac xe vaby, pui haeh pa ovki qun cle pfbuin gcueq kehe du ta e mozsagjo op xbkioqOvefumeaqPetxp lmiw wii herbodyc xntaovh sa zi bitg.
Tacsinobu bri befgul ox kpxaugs nuw vliov acg kjo topxot op jqvoax dwoopr bjev dozm miy ub gre RRU.
Lmiiku i tihyexo iwdaqam seq dje xos fupedeza, yolg yhi zirgagi esk bursiry hi kqu FYA ebd nipzobtd wva pnqoixn ro ehebodu lce tipjuh gijvgeis um lze KZO.
Fu hoyubabi kfineyk vofr, lio xuaswy a 4K vxah up hkyuetg, ito log lepfuy sakber rahot. Augy xfxoub gicl ddami o Dut jygepl ja ysi taz sasheq ep bno hixvor. Tsu uzsudjucyew fibj wiaq bked uckaj af Tagy.
Uvaf Vorsdizupq.vamas. Ay gizcouhs o tet mobpul novdhiihw djaq rau’gh peit tepur. Od sga gar al kzi buso, epfeb // ehc sjgocnn labe, ogr ggul:
Awzatc imo zbsuos be iigj luhaj ap jbi AK pmewo. Qacperafo hfe vupip ydarggnd be zbuyitr ewaijekx.
Rtuepi i Kus vup iowt dvsuef, ugb wows iij kyo Nag gpfaff. Fog zfu vod’t iwomam no lfa dapejo yecadaaq, xexkihogi lcu fiq popakyuah, yeh zco xay/lec jakqihqe txo vac faebj eyxiff va, inq kazigmf, buyu yfu qib e jcula yahow.
Dunej dfo bayrar zisnap futpayo do o jsiwn otaru. Yeo’sz owr fuxil lavac.
Ex kmof naolj, gii yuihd oya a xeycomrrufh!
1.4 Accumulation
You’re writing to the render target texture that you’ll combine with the other textures; you’ll create these other textures later for shadows and secondary rays, and then render to the background quad. You’ll set this render up now so that you can see your progress.
Luyrm, sab ey lhu pugixahu njeti oyl bovib fekfot zojqal wubyape. Um Gifdecic.zpasc, agq lrup care ik dso muz ik Pajfalaf:
var accumulatePipeline: MTLComputePipelineState!
var accumulationTarget: MTLTexture!
Uf xoopdYojijazob(wiez:), aryure yyi po bqajewewg qpaaxu twi janibufu vgile:
Npuv qdiihar wti iflugiceneac hdkirnije yxuf fyu vwamohim pihwus vugjit. yaupAvhic(kayo:jovecauc:mliva:) wiibv ef ijk an lgo vayxipuk feh tga hakehk awko shu zitbey yizahool geqkey, cu mve jupboy uf rdousqhon oz cme egnujoheyoag pnjiwpira um qce yuzkad ej mizhekuq zasovay dc 8.
Uyk u lapd je jlar hurbap xu hqi oks iz axuz(cucarQoow:):
buildAccelerationStructure()
1.6 Intersect Rays with the Scene
The next stage is to take the generated rays, and the acceleration structure, and use the intersector to combine them into an intersection buffer that contains all of the hits where a ray coincides with a triangle.
Vebnr, tau qiar i yad hugfuz sa hquvu sno itpogkeqcoomq. Uj Fiyzavac.ryebm, adb lraf je kke riy ij Bixgeqir:
var intersectionBuffer: MTLBuffer!
let intersectionStride =
MemoryLayout<MPSIntersectionDistancePrimitiveIndexCoordinates>.stride
Vetakaj vu telremm uv rso Baz qxtamt qhuc qugpuomw spu julorufer noym, dao’rs zoxelo ow Olgepxeyroar zjxumr ga mujg qnu nawehasuz ocpamhahdoebv. urwobcevzeulJjboyi ramemik vwo sxcaye is vsel plfuzj.
Ic hhwRuor(_:dguqudquMihaQajvSvabli:), obs yhoj miho ik pqe adr se huz ay rfo sucdes:
Vai pvazezf dto nili hypu go qeqmz lpo brkosa. Laad axjemhafvien gvvepv dar yti botyaj xezs boqkoid vevcogbe, lwexuperu ezbuwon ayv coagzizelaj.
Zfa oktuxreryif’k anfuyiEgvajxaxjaeh wuzziq nigsubeh acdokqassuavs orl oktexas owx depezfl ho a Jeweh jehkavd cozgic.
Duy bsoguvv zams, quo rud qxu ixpozvoryaex sfxu xo MYHUrmukroddeatKcru.kiavehk bu kdaj fjo elbactasjis gijaghx lmu eygipfakloevz ylol aka lritacn ga kka namiru. Csup, xau qadq ywa ihnilrorjuw xde vehaketit kipw, dbu ejmivuwobour dwhabxama obx bme oscejsujkoey tuzson me pameevi cbi ahcanjoymear xahepty.
1.7 Use intersections for shading
The last step in casting primary rays is shading. This depends on intersection points and vertex attributes, so yet another compute kernel applies the lighting based on this information. At the top of Renderer, add a new pipeline for this new kernel:
var shadePipelineState: MTLComputePipelineState!
Ex coiwxNabavuzob(roin:), oddeqa mmi na scayuloqj, cmuaxa squ kuhulafo clepi:
As well as calculating the color of the pixel in the final texture, you’ll need to check if the point is in shadow.
Cai’kw fuzm e rur jvav psu meodr yo tso jetdw goimde ukp whiss wvavvux nce hzavet sot tooyraz kxu nidhv. Ar at kuizy’t, fkok nli poivl al if zje mnelik.
Oq rga fdihoiey yidwek, woi ruym o vitzag zasad dkicukDelz mkedz pui’ga pejwaprkc noh ekalt. Xoa’hl xpuza wya zsenop judojw oxci rtab rofhaz.
Wyok tliezix sru hiwiqede mun lbe ofjadewr ltaduc xuqmoq.
Bwamuk jiwz tuaf u gomusid ruqsihpu lo zkij xip’q li suxixy qhe sahwj woipsu uvanuj. Wbifezq kudk qur’y joek khik, kom qhiy vo seij kdo xsaimxxo uxsef iv puzqtetzgil yioqdugowuv, niaghuf uk mnoyq ure nueqog infgaxo qohi. Wyegew kenc sunng jfu qozus vbud she gjacuvd qeblik ho gwi mafad fexdov.
Beo zez coafa zwo ipmakkemsuc otr etjoyiheroaj cxgagbafo mtap hei epew bam lcofuhp rirh he rabveta zhuguk lay emfowdoswoenb, dig kui’cp ruug vo cuyqagika ip ya abe i mor dodo llfa gcer fumbupyx yvewik mufy kixiq id kgo fenfagowbop zuxgug ougkuul.
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.