[all] It works!
This commit is contained in:
parent
dfa603012f
commit
8dcd0910f1
4 changed files with 73 additions and 66 deletions
|
@ -86,11 +86,11 @@ class Renderer: NSObject, MTKViewDelegate {
|
||||||
Vertex(position: Point(x: 1, y: 1), textureCoordinate: Point(x: 1, y: 1))
|
Vertex(position: Point(x: 1, y: 1), textureCoordinate: Point(x: 1, y: 1))
|
||||||
]
|
]
|
||||||
|
|
||||||
do {
|
// do {
|
||||||
try field.updateBuffers()
|
// try field.updateBuffers()
|
||||||
} catch let e {
|
// } catch let e {
|
||||||
NSLog("Error updating buffers: \(e)")
|
// NSLog("Error updating buffers: \(e)")
|
||||||
}
|
// }
|
||||||
|
|
||||||
let buffer = commandQueue.makeCommandBuffer()
|
let buffer = commandQueue.makeCommandBuffer()
|
||||||
buffer.label = "Render"
|
buffer.label = "Render"
|
||||||
|
|
|
@ -24,12 +24,8 @@ typedef struct {
|
||||||
float2 textureCoordinate;
|
float2 textureCoordinate;
|
||||||
} RasterizerData;
|
} RasterizerData;
|
||||||
|
|
||||||
typedef struct {
|
typedef int3 Parameters;
|
||||||
int2 size;
|
typedef float3 Ball;
|
||||||
int numberOfBalls;
|
|
||||||
} Parameters;
|
|
||||||
|
|
||||||
typedef half3 Ball;
|
|
||||||
|
|
||||||
vertex RasterizerData
|
vertex RasterizerData
|
||||||
passthroughVertexShader(uint vid [[vertex_id]],
|
passthroughVertexShader(uint vid [[vertex_id]],
|
||||||
|
@ -42,20 +38,21 @@ passthroughVertexShader(uint vid [[vertex_id]],
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
float sampleAtPoint(float2, constant Ball*, uint);
|
float sampleAtPoint(float2, constant Ball*, int);
|
||||||
|
|
||||||
fragment float4
|
fragment float4
|
||||||
sampleToColorShader(RasterizerData in [[stage_in]],
|
sampleToColorShader(RasterizerData in [[stage_in]],
|
||||||
constant Parameters& parameters [[buffer(0)]],
|
constant Parameters& parameters [[buffer(0)]],
|
||||||
constant Ball* balls [[buffer(1)]])
|
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;
|
float4 out;
|
||||||
if (sample > 1.0) {
|
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 {
|
} else {
|
||||||
out = float4(0.0, 0.0, 0.0, 0.0);
|
out = float4(0.2, 0.2, 0.2, 1.0);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -63,11 +60,11 @@ sampleToColorShader(RasterizerData in [[stage_in]],
|
||||||
float
|
float
|
||||||
sampleAtPoint(float2 point,
|
sampleAtPoint(float2 point,
|
||||||
constant Ball* balls,
|
constant Ball* balls,
|
||||||
uint count)
|
int count)
|
||||||
{
|
{
|
||||||
float sample = 0.0;
|
float sample = 0.0;
|
||||||
for (uint i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
constant Ball& ball = balls[i];
|
Ball ball = balls[i];
|
||||||
float r2 = ball.z * ball.z; // Radius stored in z coordinate.
|
float r2 = ball.z * ball.z; // Radius stored in z coordinate.
|
||||||
float xDiff = point.x - ball.x;
|
float xDiff = point.x - ball.x;
|
||||||
float yDiff = point.y - ball.y;
|
float yDiff = point.y - ball.y;
|
||||||
|
|
|
@ -26,6 +26,12 @@ public struct Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Point: CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
return "(\(x), \(y))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct Vector {
|
public struct Vector {
|
||||||
var dx: Float
|
var dx: Float
|
||||||
var dy: Float
|
var dy: Float
|
||||||
|
@ -39,3 +45,9 @@ public struct Vector {
|
||||||
self.dy = dy
|
self.dy = dy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Vector: CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
return "(\(dx), \(dy))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -46,17 +46,10 @@ public class Field {
|
||||||
balls = balls.filter { bounds.contains($0.bounds) }
|
balls = balls.filter { bounds.contains($0.bounds) }
|
||||||
|
|
||||||
// Update Metal state as needed.
|
// Update Metal state as needed.
|
||||||
// updateThreadgroupSizes(withFieldSize: size)
|
populateParametersBuffer()
|
||||||
// parametersBuffer = nil
|
|
||||||
// sampleTexture = nil
|
|
||||||
if numberOfBallsBeforeFilter != balls.count {
|
if numberOfBallsBeforeFilter != balls.count {
|
||||||
ballBuffer = nil
|
ballBuffer = nil
|
||||||
}
|
populateBallBuffer()
|
||||||
do {
|
|
||||||
try updateBuffers()
|
|
||||||
} catch let e {
|
|
||||||
NSLog("Error updating size: \(e)")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,6 +98,10 @@ public class Field {
|
||||||
let ball = Ball(radius: radius, position: position, velocity: Vector())
|
let ball = Ball(radius: radius, position: position, velocity: Vector())
|
||||||
balls.append(ball)
|
balls.append(ball)
|
||||||
NSLog("Added ball \(ball); fieldSize=\(size)")
|
NSLog("Added ball \(ball); fieldSize=\(size)")
|
||||||
|
|
||||||
|
populateParametersBuffer()
|
||||||
|
ballBuffer = nil
|
||||||
|
populateBallBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Metal Configuration
|
// MARK: - Metal Configuration
|
||||||
|
@ -120,23 +117,55 @@ public class Field {
|
||||||
// private var threadgroupSize = MTLSize(width: 16, height: 16, depth: 1)
|
// private var threadgroupSize = MTLSize(width: 16, height: 16, depth: 1)
|
||||||
|
|
||||||
/// Create the Metal buffer containing basic parameters of the simulation.
|
/// Create the Metal buffer containing basic parameters of the simulation.
|
||||||
private func makeParametersBufferIfNeeded(withDevice device: MTLDevice) -> MTLBuffer? {
|
private func populateParametersBuffer() {
|
||||||
if parametersBuffer == nil {
|
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.
|
/// Create a Metal buffer containing the current set of metaballs.
|
||||||
/// @param device The Metal device to use to create the buffer.
|
/// @param device The Metal device to use to create the buffer.
|
||||||
/// @return A new buffer containing metaball data.
|
/// @return A new buffer containing metaball data.
|
||||||
private func makeBallBufferIfNeeded(withDevice device: MTLDevice) -> MTLBuffer? {
|
private func populateBallBuffer() {
|
||||||
if ballBuffer == nil && balls.count > 0 {
|
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
|
let length = balls.count * sizeOfBall
|
||||||
|
NSLog("Making ball buffer, length:\(length)")
|
||||||
ballBuffer = device.makeBuffer(length: length, options: [])
|
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.
|
/// 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)
|
// 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 {
|
private func write<T>(value: inout T, to ptr: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer {
|
||||||
let sizeOfType = MemoryLayout<T>.stride
|
let sizeOfType = MemoryLayout<T>.stride
|
||||||
ptr.copyBytes(from: &value, count: sizeOfType)
|
ptr.copyBytes(from: &value, count: sizeOfType)
|
||||||
|
@ -206,8 +204,8 @@ public class Field {
|
||||||
NSLog("Setting up Metal")
|
NSLog("Setting up Metal")
|
||||||
self.device = device
|
self.device = device
|
||||||
// sampleComputeState = try computePipelineStateForSamplingKernel(withDevice: device)
|
// sampleComputeState = try computePipelineStateForSamplingKernel(withDevice: device)
|
||||||
parametersBuffer = makeParametersBufferIfNeeded(withDevice: device)
|
populateParametersBuffer()
|
||||||
ballBuffer = makeBallBufferIfNeeded(withDevice: device)
|
populateBallBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
// public func computePipelineStateForSamplingKernel(withDevice device: MTLDevice) throws -> MTLComputePipelineState? {
|
// public func computePipelineStateForSamplingKernel(withDevice device: MTLDevice) throws -> MTLComputePipelineState? {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue