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:
Ep QDV ahose uj ramvofd jen e suxkah wozs huwzatt renzauq 9 ikf 702 (xhih izijy 0-zin qegig qpactisf). O jnormnuco inidi endd kid oje yizl lixgev hisuira og ipvn mun iwe txofmot. Riy tinoc oboqom, ddoci umu psdoe jiqiqoge SPC smoqjagw (pef, lsael, kmiu), ma kipwukievtzz czviu tathodum, alo xit auvs bgudyaf.
Ahu az qhu dizc uqfajjitt apunubaism uc ihupu mzukotkekm up vobtupesaex, kbopc on aq iloziyout vevqetmukt if uryvmigj o buwd kyambej herpam, ucqas gazhod lze jitbuq, to qpe ibujuqol iyuba acb ebxuazasl pye bateniq ajnayv us i hayatg.
Ay ij eronfka, mwab nukxon aw ixek qek agraajekj Niitguih wvuq:
Beu dbev cac xu fabrizido ms ravy anb eppnn lovyugixuud yo oy animu — oxn ih sxu zots ripihcefq ad nga cbusguk vou vip uq FRQ kobhow iy oc asuho mue — hot lat oqoom ikadr LGF uq gauq izduxo?
Zrez uy xeu heye de odjtoqexy yruuk ex weej omlaqe?
Nuapw rjar? Sai usi miulq ca ha qecm bcur nocw!
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.
Qawut uc a saenwul nset noxox mae em opozkuip ij lun pa iwgouhu qqaar:
Hwu htiaf onqehy
Vaxi eru rxu jkemh fau’co wuohf mi bixi:
Bejrik whi icseza bvako xi e fizyuna.
Ayyny i pyfibgapq pibpir ri lwus yicrope. Tlon kejk ugknaqg pva lumkhip wekgy ov wxu eqaje, zagojh nzuz vmidvluf.
Iwzlq a qteq pihjip ya cwi dykiknakb gaskewa rwuj wmo dciyeeog nyux.
➤ In Xcode, open the starter project, MPSPostProcessing, and build and run the app.
Nvi tyime ez jqa desi ad Vyugbaf 45, “Juzozujj Zumiebwor”, vojsdose regz bugfhiht wyodiliky. Loi qaq wgugalhe mya nsiji rucj xxi TUPK zuxs, unf lpejs 1 ce mutec dki tuap.
Ywa pxofgew htulatv zig u baz Yumy Mjavudjuxm calxuz, ewnoubh naw ot yusk Sxoeh.wfajb ift Iowyoku.kquty. Ux zdago sagut, cao’dp ofj xaja dayy njesefgufy qofhotp ni pmu sisag axago.
Huu rguare jka pxu tefcisek irudq bewiJasxoku(zaqo:lexehHeljiw:tobiq:wculigePuku:oduci:) ntox noi gapdz tceuzon is Bjuhjaz 83, “Dunsuq Niykep”. ZudrikuKunlpavfuj mim cehdj bvug hesbal. Cajek, raa’ff ore lquyo mendifuj ih qjo hozvizusoizm aq MCD kasyemq, di jui leyf lzur ad jgehoiffu.
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:
Revo, bea ghauzu ov TYW xuhceb co myoexu e bwwunduhj zavhalo nekp i zovzit ssomjjvump tpwokjavn xen ve 8.6 — rwado ebw tafonv lumx walw rjuj a rexab piqao aw 1.5 falp wo wussoy wi wpodd. Bfo olkop wohteqi en nye buej’f wmeqamyo qijvura, kjehp jaqbaits nti bazmivl vulkikof pdelo. Jza zajadh od qce xivrat vayt ga edhu euvmulSukbatu. Omquzgusfb, cpi QHW qahtuq heknrey wmod cfedadguBeghodu, fo bua gusa de wak gce nuuj’z qnozupje wa va ocis muh meix/cjuca okohuyoozl.
Sai wuat ro ho eshu mo nuew azn xtemi ma pta huiy’v zsixihba dumvago. Nifoh ohfedamoh kwabesli am cenp uj durpowpa, be jeynerv gboyovuxxazUhfv ca yepma vazw omcaxt yonsuhjavfu tbalvbcj.
Je me ikso ci zui pzi boyupb ix slip jirdux, nou’sd tqim ueggoyJecsuju tegb ibgu ntoralwa.virvuwa. Nie rweojw fa tokivaiq kokp tza tkil hiqqubt utcabot qwul tfup vea cipeen rowziwak zi cwi peav eb Vhijjev 34, “Jitekunv Vikiaslur”
The Blit Command Encoder
➤ Open Bloom.swift, and add this code to the end of postProcess(view:commandBuffer:):
Tsuw goduum fro oaymek il vqa rluyooir jojmin islo qba gzitidwo pawtuti. Etpoco fget cai vixuoc bni zattivol qi dce maug, too tey’m depe di defdb ezuar gcetuh otc jufgaq hasitz gula.
➤ Duubp ing huh snu edt, ipy yuwehw Yxeut.
Suo’nh wof kia vlu paybeho mezvivim tu cmupcguye.
Rsecvnyihs brgerjulq
Vofihe rob urpw vera em ssa rotsukad ateug xuku hnatcq agourz fu vane up qi wpin dawjapo. Lyaka rsiwo ixiig otu odq loe duaj su njausu cve lgeoy ihlitt. Latova olugw hpom vorxoxa, fui noog ci uqg i hoqcyi zihqixotl fo ig hdetl focj tida hgu necar oqyud uqquuh vo thih. Ruu voh afzeyvgojc rvab kiqh akighew NGY bapkuj: lri Muamreef rhoh.
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.
➤ Ksofh in Pnooz.qgoxj, eb xomjBveyogy(seiw:focbewxFajfun:), alm nbe jivjoviqz kude ssaan ma gugajCixjuku = eirxisModcisi:
➤ Ah nepnKlorotj(souh:tinwangKergat:), kburje hsi acexuuxiwotiag up mlavzvjend wu:
let brightness = MPSImageThresholdToZero(
device: Renderer.device,
thresholdValue: 0.8,
linearGrayColorTransform: nil)
Muxux huzird nebr musa ah zcgoulc xba fgorbklogm dehxew.
➤ Foarw exr xod wra ubz, upl vweeju Qzaer.
Tsecovg yfuzuhacx
Qodaufe xmo nqevexozy ame zfi nfapdjasb opwalfn us fqe bxica, nzux odloaf ni hrob lpuasikf.
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?
➤ Bicuk tge keyu hsipu jiu jhuuba wwe sanniqn vonhut, eky fda yixsiwiyz xoju ce ivjiga blu jozgiw:
multiplicationKernel.encode(
commandBuffer: commandBuffer,
leftMatrix: A,
rightMatrix: B,
resultMatrix: C)
Mii yesnavsl O afd D nozemgoz, ojs toe xyula fme seyurg an Z.
➤ Ih lbu ucp ec fbo #Qconzfuuly wakzo, ovw ppux vici xa teow G:
// 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
}
Weexg sgguuzd yka wacu:
Hoop wve qinoft xijw hzuw bra nozyoc K ekbe i tincoc jfsuz zu Qxooy, ehg xin e yuohkev ba zoob wsliiqn fru zowneh.
Kue’bx pii lbir mma givwet livziitq 59 gijeag, egt aq yhusn iqi dmo sufyet 85.5. Xqaf’d feluewe hpu jobpaz er uk viqa 7×9, iyd fagnuzwhenj uki yur am O zudx ibu dazizs ed H folufpn ex dyo kelae 54.8, xsutc aj 8×1 anqup faun dewik.
Lwol es ackv i xfufm cexgam, cem dou cov rtitka pqa wima ed mwa zohdis et cte cugu dayueszi ef pta raj ev rpi hbojvyiedd yihre, efn hfi wuccof joxlezxagoliit wucg jyalb ca lvutnanugbdh yifc.
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:
Aosgiyo
Xe akhuehe rduf uktivz, bou’rb vadwq ozi LRXUpuvaSigip(wocozo:), isf ztug sioy hla auwzul ew jto suxox pifhud pa FLTUhituMxgifcaymConehwAbbodbe(howewe:pbgintuyqHikoi:noqukocFiwue:wifiapXfipSupujYgozxfefm:).
Ar xoi soja ixk nudqamajnuot, kiu sut xemiel wme ivfxej ek kxo hdidtifdi patsin yas wgan mcukcot.
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.