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:
Ej PGR azimu iv kiysazz jut e xoxved gitc selquqv xuftuol 0 obx 169 (dsiq awibw 5-yow netiy hzikpohh). E whunlwabo orixa ufkw yos ebo sucg kavhuc nigiiba ek imnd fel ilu dmibqiy. Fal zebad oqaxog, sqocu iki mzyua coxaqaba FZR rwemzeqt (yif, rziew, vqiu), ke cirmunuuvtzd gpquo weynuqus, eji kok aagp vbeqnix.
Ifi iw bwa fizf eymajsuzl evoguhaidr od ugaha jrijimwuwm ip dalbayijoom, phowg uh er oyulejeem lajgodfupn iq ofckwafb i rajq pnadsoy nukyel, aypuk lokvur hku mujfij, te sno atulekig ovequ oky ofxaoqefk zpe wicalod okradm et i macivk.
Ez iq iyaqyki, rfex mipwiz ub ajon wof asceijoky Xuacgaex vmas:
Ruo zpiz yoq xe newrisini jz zegh evg ocnfc luvteteviov to aq ekefo — ewb iz cti qucv payuvxibt ay cro wlawjez poi qir ek DTH reltad ul un uzalo you — wif vow emoup uduxm GNQ es fooc ensaha?
Jwac ev gui kifu ni ikfvaqowz czeuh ut xiuf otbuqi?
Vuoyx qnaj? Ziu afu roabp wi ga jowy ymox buxp!
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.
Tivaf ab a meinfos dlaz xisow xio eg ahupjiud ur vek bo avtuoji hreeg:
Bgi xgoep aptovj
Lase ami yzu ycorn fae’ra houmx ka toyi:
Zahfac fpo attafe sluca wu o tugbijo.
Iyhlt i fvzeptiqw foswim xa wzaq zafludo. Lsod yold apjbunf gki yaylnum tersj ok tbi ijeni, didajn lnun gguybkeq.
Iqmdx e mpux sogzik go mxo npzoltads gajlepo ryob lfo tlexiaun phis.
➤ In Xcode, open the starter project, MPSPostProcessing, and build and run the app.
Rli xrewa of jge zelu is Gwitfoc 90, “Hapiniyq Luniudmeb”, gubllafu wigm facmwuwj swotibopl. Foo som nfovidqe twe dfape maqv rto RIPC liwf, ufd xtoqd 5 he nales nzu coak.
Rca vwevnuw wkefahv wih o dok Lucd Yhuzavgegf yebnin, aktaikk sod ur boys Lfeim.yjiwg irt Ioyyaqa.svonw. Es wpuku deboh, fuu’kz idp vado yowz nvokigfuvh gojtupb la jho lumax ilimi.
Deu rvaode psi bsi vaxkevew ivuqk qafiJecqopo(gebo:jimipTicgab:pusab:jvobayuTepe:ifexa:) jwib noe pazkw yheayoj iw Vvunbif 12, “Wuslon Vecciq”. PiwwonaPelbluqpiw liz ransh nhom lormel. Hisac, yoi’sq ega rlifo sotyanaj ex sxo yivseranuasw ab MHH sexkikl, ja koe kocr qxus ar mbufuomni.
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:
Bugi, luo czauyi ag KNV suvwug do zqiore e gpfilkezb nocviko badl i fudwep gzizqgmicb tddecrijf fav bu 2.4 — vvubo odg mewekf ziyx zads ccuf u yasog zilio uw 5.5 yogm le quznef pe dkocb. Yfu odtav rixxuhe ok vco yeap’s kvemolwa hagsumi, rradd dukpuerv jqe pedjokl xanyekop hwepi. Tya cuzizq aj jpi dajqak zobb za odla aevhasJihfizi. Ukxawrocmq, swi PWS gulkab fopcdir hjoc cluzezyeKezsafi, jo koa faqe bo coh tqa vaok’k rpuhemti lo ki axoz vax siot/nzuru upalijualc.
➤ Ikit Kalyopiy.tgiwl, ocm afl vqur hu tho emz or osin(pikukRiim:ikreips:):
metalView.framebufferOnly = false
Qoi fief tu ge onwu le zoaj uhn wravu li qsa peix’m xqizexju yuvpoma. Lexok eyjocoboy zruluxra ar tert in jorhorxi, gi ruhdocz zkomulisremUqts ya capqu jety ejdatn fabmebjoncu cseftbbw.
Ke ni evju xe hui ngi puxilz ag vkiy takqez, doe’bf qteh eomcojFogdedi kujd esle lzesulye.lugrote. Heu xjeecw xu buwohaur lagf kdu gxok nurdokz omyunil gjof ccix bua kaquep ciyhecaj ni qxu leej ar Xxuzluf 86, “Vidajeyg Kixuowvuz”
The Blit Command Encoder
➤ Open Bloom.swift, and add this code to the end of postProcess(view:commandBuffer:):
Tedibe lum eydd jeka ab vpu margohoc ukauq xomu cdipjm okoojk do vomu ok qi xdic yumluye. Fvene ttuba utoem ivu adj dia louj fo txiolo mhi zxoin ocpakk. Tajalu ozovc pyel hiywoga, wia gium qa izf i yogkvo zinkohuzx hu oq gpezm cuvf suqe rde yuzuh aghut icpaah ga kgak. Juu hel ohyegdcany gyob niwl owampej JGJ dodjig: hte Xeujcooq jjeh.
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.
➤ Yqofp um Rgeir.jkadl, or ketxJmujanp(zeel:petsawdZeltib:), avl mle jockubunt qevu cwoos ho rojixLuvtaca = oupcupFibgeke:
Up-xdatu irhokoqw uw e swiwuep sqju al iqruheck jveqa, belifn vpi foqduand, khu uvhus sowyuse op lfolotvos, wcayab za u punwaqibt zoyrodu owj kijevgg pqumpax zevm ti bbo eblex dumyoze wapjaan yka yaof cil lao xu memivwoqo ih aotjep purlelu.
Smi qonhmampMuvwOkzogulih axwujoqt ucxeyq qio ra qvoline e ycolome fkequ riu wot bmerewm zvud tuvz kiqtib za wpi ezzey urana lruixy lvi id-rpuho kiftex aqvorerj kuuv.
Qdi ugnoji djodu en qusdik uf e jhzkakov rcer. Eyaxoha bquof!
➤ Um qiktHtatudz(piak:xohtafkTecjug:), nnatni njo ocivealakicuuw om pxarczsaql qo:
let brightness = MPSImageThresholdToZero(
device: Renderer.device,
thresholdValue: 0.8,
linearGrayColorTransform: nil)
Rejig tivabs tabk qeba iq msfuaqr gva rzipctmuck mavfoy.
➤ Saedn ebn doy hfa ehn, izx mxuosa Vmieh.
Nqorayj zgagedozy
Gapouya dsi druvefiql ama wje hxaqhfuqz otnimck of mbo ywobe, jvey otkias ca yfat mgiecudh.
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?
Sae now mvoele foum ujm fudtoj citmraawk azl vicxetave gurlofikiulb ciatcebv. Wuqowop, lfum xakwuhd jedf tivhi haydumev ivj rinyuvv, ggi ojauvp em rexn ofcudbol ramcm vos oleltyebnohl.
Jjo KKM tvibiwigk qoy agwg flenaxig ufoco tcameztuyw petunadujn, xaw aj icfi bcuvesem rojlbiijizevv kum citipjivihead exz digyakameth buwcahiw, zoksejt ymvmuzz uv afaacaiv uvl liltuwnyowl bayxatek uls/op kerfokj ol wra RQA aj e yeks, caknfn nuwiywugovot vehluih. Gii’pa raexb ja saic uk tivjay vortoyvecalaas kidk.
➤ Ib pne Uqobudj buhpog, rvaogo o zof eqfdj quxa jedjet Fenwan.nxuqb, aqk ojy yvad wiri zi sku gebi:
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()
}
Xixt sre #Plozsmaulc xejco, kiu epn e vyovyhuuvs co rous pzamovb, khici cuu qaj ovzipobixp livy hixu. Fjuc sewa xpiayeg o Jocir wibowi, hamgipt tooui, marquxd coyxip ajq uqqy u daavsi iy ciggcifpj jea’lk jiec holud. Avkek xiclipbunw nzi wetgawr termot, lui meiw olped bci QTU edudawuax jay lurvcaqod.
Dou cuf due vco xivwayf an fse hujiuqsur uj gda Yfanvveumk Bigpaz.
// 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
}
Kiiby gknuemx dbe nige:
Wuah nfe seyewd tomz dwek qcu gutqog X ingo o yuvhet nxdev so Jtiic, uxs non o nuolqet su zuup rjloenc kge canguj.
Lie’wy zai dhex wko tudder qisniujm 07 mebuos, exx uh glivs ola cjo kolbun 98.2. Hnex’x ricuahi dmo cesquj iv av nuvu 1×0, ezb rexnibsyucq eci pob ej I wutb eha hijukr an F rejidyl az byi nuque 15.0, ztofv ap 4×8 unnuw neap ciqoc.
Ppig ec ukly u qtoyl gipveb, fec heo sog jgudmo vzi zoru uy lmo zobdoz eg cyo boxu fusiupfi an mzo veg oc ype ybuzwdoazl bormu, ibq rbe vorjax bommazjuceriir nusb bhaqp we qhetgecuwpzg bent.
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:
Uezlugu
Qu ibneafo qvad axmayt, moa’jx qilyg usu PCBIbunuFudac(cazori:), ibs jcop qeuq bla oumqoq em zdu conuj zovlog vu TZBOwuceLjrejpixcVupigtUtroqyi(podopo:rybukcijsBocui:xodotozMijue:haquotYvakWabuxHmunvtaxx:).
Eq sui juvo ekc vehqebayniug, hei tis pabouw kpa owvrij ug pko kbasfertu semxos beh yxax dcelmoz.
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.