[all] It works!

This commit is contained in:
Eryn Wells 2017-08-05 13:38:46 -07:00
parent dfa603012f
commit 8dcd0910f1
4 changed files with 73 additions and 66 deletions

View file

@ -86,11 +86,11 @@ class Renderer: NSObject, MTKViewDelegate {
Vertex(position: Point(x: 1, y: 1), textureCoordinate: Point(x: 1, y: 1))
]
do {
try field.updateBuffers()
} catch let e {
NSLog("Error updating buffers: \(e)")
}
// do {
// try field.updateBuffers()
// } catch let e {
// NSLog("Error updating buffers: \(e)")
// }
let buffer = commandQueue.makeCommandBuffer()
buffer.label = "Render"

View file

@ -24,12 +24,8 @@ typedef struct {
float2 textureCoordinate;
} RasterizerData;
typedef struct {
int2 size;
int numberOfBalls;
} Parameters;
typedef half3 Ball;
typedef int3 Parameters;
typedef float3 Ball;
vertex RasterizerData
passthroughVertexShader(uint vid [[vertex_id]],
@ -42,20 +38,21 @@ passthroughVertexShader(uint vid [[vertex_id]],
return out;
}
float sampleAtPoint(float2, constant Ball*, uint);
float sampleAtPoint(float2, constant Ball*, int);
fragment float4
sampleToColorShader(RasterizerData in [[stage_in]],
constant Parameters& parameters [[buffer(0)]],
constant Ball* balls [[buffer(1)]])
{
const float sample = sampleAtPoint(in.textureCoordinate, balls, parameters.numberOfBalls);
const uint numberOfBalls = parameters.z;
const float sample = sampleAtPoint(in.position.xy, balls, numberOfBalls);
float4 out;
if (sample > 1.0) {
out = float4(0.0, 1.0, 0.0, 0.0);
out = float4(0.0, 1.0, 0.0, 1.0);
} else {
out = float4(0.0, 0.0, 0.0, 0.0);
out = float4(0.2, 0.2, 0.2, 1.0);
}
return out;
}
@ -63,11 +60,11 @@ sampleToColorShader(RasterizerData in [[stage_in]],
float
sampleAtPoint(float2 point,
constant Ball* balls,
uint count)
int count)
{
float sample = 0.0;
for (uint i = 0; i < count; i++) {
constant Ball& ball = balls[i];
for (int i = 0; i < count; i++) {
Ball ball = balls[i];
float r2 = ball.z * ball.z; // Radius stored in z coordinate.
float xDiff = point.x - ball.x;
float yDiff = point.y - ball.y;

View file

@ -26,6 +26,12 @@ public struct Point {
}
}
extension Point: CustomStringConvertible {
public var description: String {
return "(\(x), \(y))"
}
}
public struct Vector {
var dx: Float
var dy: Float
@ -39,3 +45,9 @@ public struct Vector {
self.dy = dy
}
}
extension Vector: CustomStringConvertible {
public var description: String {
return "(\(dx), \(dy))"
}
}

View file

@ -46,17 +46,10 @@ public class Field {
balls = balls.filter { bounds.contains($0.bounds) }
// Update Metal state as needed.
// updateThreadgroupSizes(withFieldSize: size)
// parametersBuffer = nil
// sampleTexture = nil
populateParametersBuffer()
if numberOfBallsBeforeFilter != balls.count {
ballBuffer = nil
}
do {
try updateBuffers()
} catch let e {
NSLog("Error updating size: \(e)")
return
populateBallBuffer()
}
}
}
@ -105,6 +98,10 @@ public class Field {
let ball = Ball(radius: radius, position: position, velocity: Vector())
balls.append(ball)
NSLog("Added ball \(ball); fieldSize=\(size)")
populateParametersBuffer()
ballBuffer = nil
populateBallBuffer()
}
// MARK: - Metal Configuration
@ -120,23 +117,55 @@ public class Field {
// private var threadgroupSize = MTLSize(width: 16, height: 16, depth: 1)
/// Create the Metal buffer containing basic parameters of the simulation.
private func makeParametersBufferIfNeeded(withDevice device: MTLDevice) -> MTLBuffer? {
private func populateParametersBuffer() {
if parametersBuffer == nil {
parametersBuffer = device.makeBuffer(length: MemoryLayout<Int>.stride * 3, options: [])
guard let device = self.device else { return }
let length = 16 // A Parameters struct in shader-land is an int3, which takes 16 bytes.
parametersBuffer = device.makeBuffer(length: length, options: [])
NSLog("Making parameters buffer, length:\(length)")
}
if let parameters = parametersBuffer {
var ptr = parameters.contents()
var width = UInt32(size.width)
parameters.addDebugMarker("Width", range: NSRange(location: parameters.contents().distance(to: ptr), length: 4))
ptr = write(value: &width, to: ptr)
var height = UInt32(size.height)
parameters.addDebugMarker("Height", range: NSRange(location: parameters.contents().distance(to: ptr), length: 4))
ptr = write(value: &height, to: ptr)
var numberOfBalls = UInt32(self.balls.count)
parameters.addDebugMarker("Number Of Balls", range: NSRange(location: parameters.contents().distance(to: ptr), length: 4))
ptr = write(value: &numberOfBalls, to: ptr)
NSLog("Populated parameters: w:\(width), h:\(height), n:\(numberOfBalls)")
}
return parametersBuffer
}
/// Create a Metal buffer containing the current set of metaballs.
/// @param device The Metal device to use to create the buffer.
/// @return A new buffer containing metaball data.
private func makeBallBufferIfNeeded(withDevice device: MTLDevice) -> MTLBuffer? {
private func populateBallBuffer() {
if ballBuffer == nil && balls.count > 0 {
let sizeOfBall = MemoryLayout<Float>.stride * 3 // A Ball in shader-land is a float3.
guard let device = self.device else { return }
let sizeOfBall = 16 // A Ball in shader-land is a float3, which takes 16 bytes.
let length = balls.count * sizeOfBall
NSLog("Making ball buffer, length:\(length)")
ballBuffer = device.makeBuffer(length: length, options: [])
}
return ballBuffer
if let ballBuffer = ballBuffer {
var ptr = ballBuffer.contents()
var idx = 0
for var ball in self.balls {
ballBuffer.addDebugMarker("Ball \(idx)", range: NSRange(location: ballBuffer.contents().distance(to: ptr), length: 16))
ptr = write(value: &ball.position.x, to: ptr)
ptr = write(value: &ball.position.y, to: ptr)
var r = ball.radius
ptr = write(value: &r, to: ptr)
ptr = ptr.advanced(by: 4) // Skip 4 bytes to maintain alignment.
NSLog("Populated ball: x:\(ball.position.x), y:\(ball.position.y), r:\(r)")
idx += 1
}
}
}
/// Create a Metal texture to hold sample values created by the sampling compute shader.
@ -162,37 +191,6 @@ public class Field {
// threadgroupCount = MTLSize(width: width + threadgroupSize.width - 1, height: height + threadgroupSize.height - 1, depth: 1)
// }
/// Copy metaballs data into the parameters buffer.
public func updateBuffers() throws {
guard let device = self.device else {
throw MetaballsError.metalError("Missing Metal device for update")
}
guard let parameters = makeParametersBufferIfNeeded(withDevice: device),
let balls = makeBallBufferIfNeeded(withDevice: device)
else {
throw MetaballsError.metalError("Couldn't create buffers")
}
var ptr = parameters.contents()
var width = Int(size.width)
ptr = write(value: &width, to: ptr)
var height = Int(size.height)
ptr = write(value: &height, to: ptr)
var numberOfBalls = self.balls.count
ptr = write(value: &numberOfBalls, to: ptr)
ptr = balls.contents()
for var ball in self.balls {
ptr = write(value: &ball.position.x, to: ptr)
ptr = write(value: &ball.position.y, to: ptr)
var r = ball.radius
ptr = write(value: &r, to: ptr)
}
}
private func write<T>(value: inout T, to ptr: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer {
let sizeOfType = MemoryLayout<T>.stride
ptr.copyBytes(from: &value, count: sizeOfType)
@ -206,8 +204,8 @@ public class Field {
NSLog("Setting up Metal")
self.device = device
// sampleComputeState = try computePipelineStateForSamplingKernel(withDevice: device)
parametersBuffer = makeParametersBufferIfNeeded(withDevice: device)
ballBuffer = makeBallBufferIfNeeded(withDevice: device)
populateParametersBuffer()
populateBallBuffer()
}
// public func computePipelineStateForSamplingKernel(withDevice device: MTLDevice) throws -> MTLComputePipelineState? {