git.iko.soy/metal-metaballs
on master help sign in
Refactored metaball info from being a texture to a buffer.
40cc173vinivendra
PixelImage/MetalMetaballRenderer.swift+7−18
@@ -1,5 +1,3 @@
1−// TODO: Make shader ignore metaballs that are outside a certain area of tolerance (eg ±100 from borders)
2−
3// TODO: Metaballs from texture to buffer
1// TODO: Metaballs from texture to buffer
4// TODO: Use async dispatch to free up main queue
2// TODO: Use async dispatch to free up main queue
5// TODO: Refactor update into 2 calls: metalRendering() and textureToImage()
3// TODO: Refactor update into 2 calls: metalRendering() and textureToImage()
@@ -62,7 +60,7 @@ class MetalMetaballRenderer: MetaballRenderer {
62 height / threadGroupCounts.height, 1)
60 height / threadGroupCounts.height, 1)
63
61
64 // Create up-to-date metaball buffer
62 // Create up-to-date metaball buffer
65− let metaballInfoTexture = metaballTexture(metaballs)
63+ let metaballInfoBuffer = metaballBuffer(metaballs)
66
64
67 // Send commands to metal and render
65 // Send commands to metal and render
68 let commandBuffer = context.commandQueue.commandBuffer()
66 let commandBuffer = context.commandQueue.commandBuffer()
@@ -70,7 +68,7 @@ class MetalMetaballRenderer: MetaballRenderer {
70 let commandEncoder = commandBuffer.computeCommandEncoder()
68 let commandEncoder = commandBuffer.computeCommandEncoder()
71 commandEncoder.setComputePipelineState(pipeline)
69 commandEncoder.setComputePipelineState(pipeline)
72 commandEncoder.setTexture(renderingTexture, atIndex: 0)
70 commandEncoder.setTexture(renderingTexture, atIndex: 0)
73− commandEncoder.setTexture(metaballInfoTexture, atIndex: 1)
71+ commandEncoder.setBuffer(metaballInfoBuffer, offset: 0, atIndex: 0)
74 commandEncoder.dispatchThreadgroups(threadGroups,
72 commandEncoder.dispatchThreadgroups(threadGroups,
75 threadsPerThreadgroup: threadGroupCounts)
73 threadsPerThreadgroup: threadGroupCounts)
76 commandEncoder.endEncoding()
74 commandEncoder.endEncoding()
@@ -127,7 +125,7 @@ class MetalMetaballRenderer: MetaballRenderer {
127 return image
125 return image
128 }
126 }
129
127
130− func metaballTexture(metaballs: [Metaball]) -> MTLTexture {
128+ func metaballBuffer(metaballs: [Metaball]) -> MTLBuffer {
131 // Create Float array for buffer
129 // Create Float array for buffer
132 // Exclude metaballs far from the view's bounds
130 // Exclude metaballs far from the view's bounds
133 let border: CGFloat = 100
131 let border: CGFloat = 100
@@ -144,19 +142,10 @@ class MetalMetaballRenderer: MetaballRenderer {
144 // Add array size to start of array so metal knows how far to go
142 // Add array size to start of array so metal knows how far to go
145 floats[0] = Float(floats.count) / 2
143 floats[0] = Float(floats.count) / 2
146
144
147− // Create texture
145+ // Create buffer
148− let size = floats.count
146+ let buffer = context.device.newBufferWithBytes(floats, length: floats.count * sizeof(Float), options: .StorageModeShared)
149− let textureDescriptor =
147+
150− MTLTextureDescriptor.texture2DDescriptorWithPixelFormat(
151− .R32Float, width: size, height: 1, mipmapped: false)
152− let texture = context.device.newTextureWithDescriptor(textureDescriptor)
153−
154− // Copy array info into texture
155− let region = MTLRegionMake2D(0, 0, size, 1)
156− texture.replaceRegion(region, mipmapLevel: 0, withBytes: floats,
157− bytesPerRow: sizeof(Float) * size)
158−
159− return texture
148+ return buffer
160 }
149 }
161
150
162 func updateFrame() {
151 func updateFrame() {
PixelImage/shaders.metal+6−7
@@ -5,22 +5,21 @@ using namespace metal;
5
5
6kernel void
6kernel void
7 drawMetaballs(texture2d<float, access::write> outTexture[[texture(0)]],
7 drawMetaballs(texture2d<float, access::write> outTexture[[texture(0)]],
8− texture2d<float, access::read>
8+ constant float *metaballBuffer [[buffer(0)]],
9− metaballTexture[[texture(1)]],
10 uint2 gid[[thread_position_in_grid]]) {
9 uint2 gid[[thread_position_in_grid]]) {
11
10
12− char numberOfMetaballs = metaballTexture.read(0)[0];
11+ char numberOfMetaballs = metaballBuffer[0];
13 char metaballArraySize = numberOfMetaballs * 2 + 1;
12 char metaballArraySize = numberOfMetaballs * 2 + 1;
14
13
15 float sum = 0;
14 float sum = 0;
16
15
17 for (char i = 1; i < metaballArraySize; i += 2) {
16 for (char i = 1; i < metaballArraySize; i += 2) {
18 for (char j = i + 2; j < metaballArraySize; j += 2) {
17 for (char j = i + 2; j < metaballArraySize; j += 2) {
19− float2 metaball1 = float2(metaballTexture.read(i)[0],
18+ float2 metaball1 = float2(metaballBuffer[i],
20− metaballTexture.read(i + 1)[0]);
19+ metaballBuffer[i + 1]);
21
20
22− float2 metaball2 = float2(metaballTexture.read(j)[0],
21+ float2 metaball2 = float2(metaballBuffer[j],
23− metaballTexture.read(j + 1)[0]);
22+ metaballBuffer[j + 1]);
24
23
25 float2 metaball1Vector
24 float2 metaball1Vector
26 = float2(metaball1.x - gid.x, metaball1.y - gid.y);
25 = float2(metaball1.x - gid.x, metaball1.y - gid.y);