[Metaballs] A Metal graphics pipeline that builds and runs but crashes
This commit is contained in:
parent
b52e16debe
commit
463246df36
2 changed files with 115 additions and 18 deletions
|
@ -7,3 +7,102 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,16 +9,10 @@
|
||||||
#include <metal_stdlib>
|
#include <metal_stdlib>
|
||||||
using namespace metal;
|
using namespace metal;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
float2 position;
|
|
||||||
float radius;
|
|
||||||
} Ball;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float2 position;
|
float2 position;
|
||||||
float2 textureCoordinate;
|
float2 textureCoordinate;
|
||||||
} VertexIn;
|
} Vertex;
|
||||||
|
|
||||||
// From HelloCompute sample code project.
|
// 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.
|
// 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;
|
float2 textureCoordinate;
|
||||||
} RasterizerData;
|
} RasterizerData;
|
||||||
|
|
||||||
//vertex RasterizerData
|
vertex RasterizerData
|
||||||
//passthroughVertexShader(uint vertexID [[vertex_id]])
|
passthroughVertexShader(uint vid [[vertex_id]],
|
||||||
//{
|
constant Vertex* vertexes [[buffer(0)]])
|
||||||
// // TODO: Nothing really. Just pass on through to the fragment shader.
|
{
|
||||||
//}
|
RasterizerData out;
|
||||||
|
Vertex v = vertexes[vid];
|
||||||
|
out.position = float4(v.position.xy, 0.0, 1.0);
|
||||||
|
out.textureCoordinate = v.textureCoordinate;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
fragment float4
|
fragment float4
|
||||||
sampleToColorShader(RasterizerData in [[stage_in]],
|
sampleToColorShader(RasterizerData in [[stage_in]],
|
||||||
constant float* samples [[buffer(0)]],
|
texture2d<half> samples [[texture(0)]])
|
||||||
constant float2* size [[buffer(1)]])
|
|
||||||
{
|
{
|
||||||
int index = in.textureCoordinate.y * size->y + in.textureCoordinate.x;
|
constexpr sampler textureSampler(mag_filter::linear, min_filter::linear);
|
||||||
float sample = samples[index];
|
const half4 sample = samples.sample(textureSampler, in.textureCoordinate);
|
||||||
|
|
||||||
float4 out;
|
float4 out;
|
||||||
if (sample > 1.0) {
|
if (sample.r > 1.0) {
|
||||||
out = float4(0.0, 1.0, 0.0, 0.0);
|
out = float4(0.0, 1.0, 0.0, 0.0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue