[Metaballs] A Metal graphics pipeline that builds and runs but crashes

This commit is contained in:
Eryn Wells 2017-08-05 09:04:43 -07:00
parent b52e16debe
commit 463246df36
2 changed files with 115 additions and 18 deletions

View file

@ -7,3 +7,102 @@
//
import Foundation
import MetaballsKit
import MetalKit
enum RendererError: Error {
case MetalError(String)
}
protocol RendererDelegate {
var renderSize: CGSize { get set }
var field: Field { get }
}
struct Vertex {
let x: Float
let y: Float
let texX: Float
let texY: Float
}
class Renderer: NSObject, MTKViewDelegate {
var delegate: RendererDelegate?
private var device: MTLDevice
private var commandQueue: MTLCommandQueue
private var renderPipelineState: MTLRenderPipelineState
init(view: MTKView, field: Field) throws {
guard let device = MTLCreateSystemDefaultDevice() else {
throw RendererError.MetalError("Unable to create Metal system device")
}
self.device = device
view.device = device
do {
try field.setupMetal(withDevice: device)
} catch let e {
throw e
}
let library = try device.makeDefaultLibrary(bundle: Bundle.main)
let vertexShader = library.makeFunction(name: "passthroughVertexShader")
let fragmentShader = library.makeFunction(name: "sampleToColorShader")
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.label = "Render Pipeline"
pipelineStateDescriptor.vertexFunction = vertexShader
pipelineStateDescriptor.fragmentFunction = fragmentShader
pipelineStateDescriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat
renderPipelineState = try device.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
commandQueue = device.makeCommandQueue()
super.init()
}
/// MARK: - MTKViewDelegate
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
delegate?.renderSize = size
// TODO: Reallocate the sample buffer and texture
}
func draw(in view: MTKView) {
guard let field = delegate?.field else {
return
}
// Two triangles, plus texture coordinates.
let points: [Vertex] = [Vertex(x: 1, y: -1, texX: 1, texY: 0),
Vertex(x: -1, y: -1, texX: 0, texY: 0),
Vertex(x: -1, y: 1, texX: 0, texY: 1),
Vertex(x: 1, y: -1, texX: 1, texY: 0),
Vertex(x: -1, y: 1, texX: 0, texY: 1),
Vertex(x: 1, y: 1, texX: 1, texY: 1)]
let buffer = commandQueue.makeCommandBuffer()
do {
let _ = try field.computeEncoderForSamplingKernel(withDevice: device, commandBuffer: buffer)
buffer.commit()
} catch let e {
print("\(e)")
}
if let renderPass = view.currentRenderPassDescriptor {
let encoder = buffer.makeRenderCommandEncoder(descriptor: renderPass)
encoder.label = "Render Pass"
encoder.setViewport(MTLViewport(originX: 0.0, originY: 0.0, width: Double(view.drawableSize.width), height: Double(view.drawableSize.height), znear: -1.0, zfar: 1.0))
encoder.setRenderPipelineState(renderPipelineState)
encoder.setVertexBytes(points, length: points.count * MemoryLayout<Vertex>.size, at: 0)
encoder.setFragmentTexture(field.sampleTexture, at: 0)
encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6)
encoder.endEncoding()
if let drawable = view.currentDrawable {
buffer.present(drawable)
}
}
buffer.commit()
}
}

View file

@ -9,16 +9,10 @@
#include <metal_stdlib>
using namespace metal;
typedef struct {
float2 position;
float radius;
} Ball;
typedef struct {
float2 position;
float2 textureCoordinate;
} VertexIn;
} Vertex;
// From HelloCompute sample code project.
// Vertex shader outputs and per-fragmeht inputs. Includes clip-space position and vertex outputs interpolated by rasterizer and fed to each fragment genterated by clip-space primitives.
@ -30,22 +24,26 @@ typedef struct {
float2 textureCoordinate;
} RasterizerData;
//vertex RasterizerData
//passthroughVertexShader(uint vertexID [[vertex_id]])
//{
// // TODO: Nothing really. Just pass on through to the fragment shader.
//}
vertex RasterizerData
passthroughVertexShader(uint vid [[vertex_id]],
constant Vertex* vertexes [[buffer(0)]])
{
RasterizerData out;
Vertex v = vertexes[vid];
out.position = float4(v.position.xy, 0.0, 1.0);
out.textureCoordinate = v.textureCoordinate;
return out;
}
fragment float4
sampleToColorShader(RasterizerData in [[stage_in]],
constant float* samples [[buffer(0)]],
constant float2* size [[buffer(1)]])
sampleToColorShader(RasterizerData in [[stage_in]],
texture2d<half> samples [[texture(0)]])
{
int index = in.textureCoordinate.y * size->y + in.textureCoordinate.x;
float sample = samples[index];
constexpr sampler textureSampler(mag_filter::linear, min_filter::linear);
const half4 sample = samples.sample(textureSampler, in.textureCoordinate);
float4 out;
if (sample > 1.0) {
if (sample.r > 1.0) {
out = float4(0.0, 1.0, 0.0, 0.0);
}
else {