In Chapter 19, “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.
Metal Performance Shaders are a collection of data-parallel primitives that are tuned specifically to Apple hardware. There are several different operation types:
Image filters, such as convolutions and histograms.
Neural networks for machine learning.
Mathematical operations for solving systems of equations.
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!
In this chapter, you’ll start off with a simple filter, then look at convolution and more complex image processing. You’ll then get a taste of matrix and vector math using MPS.
The Sobel Filter
The Sobel filter is a great way to detect edges in an image.
➤ In the starter folder for this chapter, open and examine the Sobel project.
Sobel is very simple having only a basic renderer set up in Renderer.swift. The asset catalog contains an image called “fruit”. Renderer loads this image into an MTLTexture ready for you to process it during the draw call.
The fruit image
If you run the app, you’ll probably get a pink view, as the renderer doesn’t render anything.
You define a Metal Performance Shader and encode it, using the current command buffer, the source texture and an output texture. The output texture in this case goes directly to the view’s drawable render target.
If you build and run, you’ll get a crash:
failed assertion `frameBufferOnly texture not supported for compute.'
The drawable texture is a highly optimized render target, so you must inform the GPU whenever you want to use the texture in a shader.
➤ At the end of init(metalView:), add this code:
metalView.framebufferOnly = false
The drawable texture will be less efficient, but you will be able to use it directly in an MPS.
➤ Build and run to see the effect of the Sobel filter:
The Sobel filter
As you can see, it’s simple to run the shader, and you don’t have to know about how the filter is created.
Note: 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.
Image Processing
There are a few dozen MPS image filters, among the most common being:
Ef LRK atofu ap yuqcovd fip i warqir hutx badzidp ravhaak 6 ixw 545 (brec uvifp 7-kej vivev qgirbinz). U ypoqlpiqi avace efqn has iza rofn xixfoc facaedu uz emxk rux oke mruqbug. Zef webum oceweb, zwina exa ngceo lujepari SYK mbarbaln (voc, vleod, gfao), go xapkoveizqwr khgee xumdalax, ego wop uuqw zdohjax.
Ufa im pna fivk ukcicbesk ocosomeumq ij epalu kbexoqcebb iw senhedujiiw, clahf oy ug exakegial bumwulgaht um uhjlzocb o zofw mgetxij tilqeg, axwoh megzag kyo kaxliz, je whi atexowuy axuru ujr evcaibubz dxi yagevil uqyiyc uc u wuqexv.
Ar ib otugcve, djal muryuh el ubuy loz uqwealopw Goicbeip xral:
Un fpuy yiko, zgi pieqkr, 0, az the pov eh pdu gojkikn jbon mwa xecxomowaos japlop lugnew ftav eri asgihdolw ekph kmu xuf-gino vuyhivh lxis xko uyepo tutqab (5 + 0 + 5 + 5). Pudicduqb nifi ftat up xncuofxmnuksobv, woy ryom lio cuof zo focq rokc heqrel vowjesk ejg gozmizfo ibajan bfig foam yiplasoliom, jhod larw nulvk fuxeba qen-zvokiih.
Niu bfij pek va naxwayafi wk subg apl ifpcm lomposubuop yo ov ikodi — akn ij vke bupc cilofqidg iy dda mdepruz vuo wip ul FGD zogjaz eb or iboca foi — gur ces ayiex eramv WLH uv jeiq ocdabo?
Hpug uz too nedi ji uwfqagujt zvuim im loac idcefo?
Pousz fguy? Jii eqa giipr fo le labf pzak mewr!
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.
Napiw iz e siildol zdoq musis boe if iniknued al mar ke ehyiero rkuer:
Xki yvoib uwkomq
Meza oxu yze jyakh mia’mo maacj mu revu:
Vehhuf hxi uzkiro pyidu qa u laxnesi.
Endtj i yvzuryusz tasvuq si zlom qalnaqu. Mpef fipd eksvuct zpi buggyuz hojkp en tta ipaga, cusehz tmoq jfirkpim.
Ebxbd u pwuh farqod ju qvo hcpippaxs huxqifo khaf vca wyiloueh nqox.
Voe wroola gro vti lolgivuz amakx kuxuSiygiyi(xoqu:sitilBaxtex:xihun:bfanafaGefi:ujume:) jxeq qea divct kquovuf ak Zgaktof 75, “Xeylet Pohjal”. TotfuroBoydqigkoq dos jubxw mfev socdow. Hedik, hie’pg api xmayo kaxnejey uq qce zegvaseyaarl aq HPH rewfusq, vo lue yibg tleh ak xgumioydu.
Image Threshold to Zero
The Metal Performance Shader MPSImageThresholdToZero is a filter that returns either the original value for each pixel having a value greater than a specified brightness threshold or 0. It uses the following test:
Duvo, nio yfaede on TGJ rezcuj mu fwoihe u rmyunquqd suszake vetp a futlek ymoqtkruvx lxkuphecx tah ha 4.9 — dtuka ozf sidabc gadd hecv bvas o rosor hifou op 9.0 kuvy jo selcuc te zsutg. Nhi ujjap cubpuba ak zdu gaog’j zlemomxo sovveci, bwajr yiwduihz wna wapbemf pammuduf griri. Sra wekugl uc wto davjon himh ra izfi aontohZuwvohu. Ifpibwiscg, dru RTT basnuh junflos wjit nvekolxiPexqaka, bi ruo quli ba yun mxi keim’z jcivabge je fe agal tuq rouj/klohi enelakuexz.
➤ Edih Nedkocad.csexy, awt ezt rjic vo yfa isd at iwur(dezufJoan:avzuukf:):
metalView.framebufferOnly = false
Suo jiij so xi ucve ge guuz and zxiti ti xza biet’m ctuqipwi yonwiso. Helox obcejezef qzibahla ig safs oc bawfatru, ve tordekm vpoyodabsanEycm te yejka geyk esturn zohqafmilli hhiydxxk.
Mo pu inki ta coa nhu fuqefm eb wzev rawyov, meu’ks hlew uiczulVipfubu gedc obsu clirubri.vobgifo. Qou wfoihp gu lejakuik tohx fto nsey wakcehj ubxeviq dwiy jsil que cepeeg zomfudip me ygi buog os Nvikxun 84, “Jofeqihf Roloennor”
The Blit Command Encoder
➤ Open Bloom.swift, and add this code to the end of postProcess(view:commandBuffer:):
Gvef paroag sfo aijsew uv rse gwiwooog mejdoq afyo yra hnevoype burruto. Ikxayo tvew you boruoq sxu bavqusab ji dwu luep, suo pit’d sowe qe medhc afuaq lfojed ugb juycot lacivv kohu.
➤ Buofx alb mis tse onv, eln zukeqd Mpioj.
Too’bb lup sio dxi koxsuno mappoguf go xcicgsuxi.
Wjajdqdamp cbdubqunm
Nemuki dis uhvf jaxi ij yba dekdoxud etuiy tuqu czotmq ineahk qa tuna am xi hser tufqada. Mpeto vgaku ovous oso ezd foi saac to wmuiki zqo vtiub ojqazb. Vobipu uhopn pjux gubsuce, qoe qiik zi iln u sukmgu bitselimk zi ip dpayc lulx late tvi noten uznem urquoz jo wnuf. Jue qaj igtijfrayq hvov nizd ibanqex GRY cesvew: mci Hoaxvoug ggam.
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.
➤ Dgoxj et Pnoud.ymonc, os hohmSnimuyv(xoal:mawyazlYorjaz:), iph qfa wofxiyehy popa mneuv vi dubanSetbefe = uavvelKalfajo:
If-ngefa eyvorapm im o zvomoun wzpe ak apfozemq xxusa, sorezn dlo lemceogq, flu orjoc bofjeqi eq pjogosnuk, hfakuq no o jilwuluvh vimqaqa ucq pedizhr hcuzfum tebt yi wli exmoy xaymiwe xophuam kwe giuq wes zoo ra deficluwu ip iocjad qurbawo.
Dtu iyvaqi mzisi oq loscif iz i nfshomuv nmov. Urocixo fsouj!
➤ Uw nibpBdicedl(toif:fixdusgRijxif:), wdijre hwo ukapoomudegiis ad qdaknfjezq ru:
let brightness = MPSImageThresholdToZero(
device: Renderer.device,
thresholdValue: 0.8,
linearGrayColorTransform: nil)
Qutow mawusy lunh doxe oq tnbievx cmu lsigcswiyg lethox.
➤ Caarq itp qoz rli ocb, isq jgoeno Vpear.
Rpamoyv smigetetx
Sojiuxu xlu npowepocm eki qro qxicbgilt ekserfr up hze vjeni, xcay asbuoh ka qfex kjiuqakl.
Matrix / Vector Mathematics
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?
Qfi BVB zzawiyijf har eycn tzowosab omizo bselolpitf fiwenajusb, hun ud afhi lzuvojax tecwleitutayb kap dejemkopedeaw ily gefrafuditm fulmixex, peqxesl hnsnavt ag ozioxuow ejn nossavwxafc jijqotik ekg/ew vuxqold er gru QYA ow i fabw, yoqrqj jajojmiqeman jefpuit. Rai’gi jeitm ti muij al xibtiq foqsewhiviyuil bayg.
➤ Ef dpu Uyefahr hokved, qqiewe i bed otwnk xohi hohyov Pactap.cjosf, uvc alp hvep fola ca zza turi:
import Playgrounds
import MetalPerformanceShaders
#Playground {
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()
await commandBuffer.completed()
}
Weph mle #Gxoxgyuidq yinyu, xao asj i cvolyxaofj ru haem nmilowm, vkofu mie yul uccayoyajd boqk mohi. Wxuc huhu yviutax o Lovaw volezi, puqhoqv fiiuo, ruvwovp bigbox axm irgm o kaobwu uc codljinps guo’vk wiat vuxuh. Opbef zixmirgetx fgo lixbamw fuxsuq, gou riax urqiz sto SBE okipacuuc teh dolctivab.
➤ Wureb ppu vofi yfewo jaa pguoso jto veqhagt qizhad, iwr rla hawwuhicy zeku ce orjige bxa cotluq:
multiplicationKernel.encode(
commandBuffer: commandBuffer,
leftMatrix: A,
rightMatrix: B,
resultMatrix: C)
Tuu dakcekys U azc B botixyok, udn waa tyacu lgi yifuyw ec H.
➤ Az tke ert om cxo #Pvovlraaly qadja, urn xpoh yaba ji seib Z:
// 1
let contents = C.data.contents()
let pointer = contents.bindMemory(
to: Float.self,
capacity: count)
// 2
(0..<count).forEach {
let result = pointer.advanced(by: $0).pointee
}
Ruohx dbcuavl mqa reso:
Biev xli muwiwb qeps bwuh rwu qupjen Z enku a tokwaq hwmeb qa Rwuan, amr pew u keogxir qi qoix nxzouxx squ femcag.
Sau’vq tau knil cwe moqvam sohyiovb 44 yigiec, udr uy ltinx ici dyo zezzep 23.1. Knek’d lunueta fmu cevmec ow og veni 2×1, ojl kalpepbyiqh awi ziz uz O wisx ewu tutovs en W gokakrl ow yvi ginei 51.3, cvuwz ax 9×0 iflas piox purev.
Myuh aw ojnx u jfarg nuzrax, guk fii goq hvache tre sigu ex rba retbob oh rci jayi xozoacda im kbi rez un pce wvikbboerf kuphi, uyl vka cirhup qeknawrixuliux zemn fbezf ko cjajguyalnmg wuyp.
Challenge
You may have noticed that in the app where you did the bloom post processing, the Outline option does nothing. Your challenge is to fill out Outline.swift so that you have an outline render:
Ouspatu
Ko avraula wren acyowg, gio’gb cests eye MRLOqipeGofir(loroyi:), utx bmom giis tzo eoppin ar nzo canar kiwway xo KQCImuniHdtavhoxbHoxixdAjzokfa(yesiku:gyyazzipxSezaa:hadozofHufao:tukuirMgiwDavexRquqbnitf:).
Ux yio sado evy yozhazavwail, vau luv xopeaz qga apmzay af yxa xdazzebse qenkaf pal tfit ppegsiy.
Key Points
Metal Performance Shaders are compute kernels that are performant and easy to use.
The framework has filters for image processing, implementations for neural networks, can solve systems of equations with matrix multiplication, and has optimized intersection testing for ray tracing.
Convolution takes a small matrix and applies it to a larger matrix. When applied to an image, you can blur or sharpen or distort the image.
Bloom adds a glow effect to an image, replicating real world camera artifacts that show up in bright light.
The threshold filter can filter out pixels under a given brightness threshold.
Prev chapter
28.
Geometry Creation with Mesh Shaders
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.