Skip to content

Commit

Permalink
added Parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
gadirom committed Jul 28, 2022
1 parent 056718b commit 8f94829
Show file tree
Hide file tree
Showing 9 changed files with 416 additions and 79 deletions.
27 changes: 20 additions & 7 deletions Example/Example/Example/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,30 @@ struct ContentView: View {
VStack{
MetalBuilderView(librarySource: metalFunctions, isDrawing: $isDrawing){ viewportSize in
Compute("particleFunction")
.buffer(particlesBuffer, offset: 0, index: 0)
.buffer(vertexBuffer, offset: 0, index: 1)
.bytes(viewportSize, index: 2)
.bytes($particleScale, index: 3)
.buffer(particlesBuffer, offset: 0,
argument: .init(space: "device", type: "Particle",
name: "particles",
index: 0))
.buffer(vertexBuffer, offset: 0,
argument: .init(space: "device", type: "Vertex",
name: "vertices",
index: 1))
.bytes(viewportSize,
argument: .init(space: "constant", type: "uint2",
name: "viewport", index: 2))
.bytes($particleScale,
argument: .init(space: "constant", type: "float",
name: "scale", index: 3))
.threadsFromBuffer(0)
//.grid(size: $mSize)
Render(vertex: "vertexShader", fragment: "fragmentShader")
.toTexture(targetTexture)
//.viewport($viewport)
.vertexBuf(vertexBuffer, offset: 0, index: 0)
.vertexBytes(viewportSize, index: 1)
.vertexBuf(vertexBuffer, offset: 0,
argument: .init(space: "constant", type: "Vertex",
name: "vertices", index: 0))
.vertexBytes(viewportSize,
argument: .init(space: "constant", type: "uint2",
name: "viewport", index: 2))
.primitives(count: vertexCount)
CPUCode{ [self] device, commandBuffer, drawable in
let l = MPSImageLaplacian(device: device)
Expand Down
24 changes: 7 additions & 17 deletions Example/Example/Example/metalFunctions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,24 @@ struct Particle{
float angvelo;
};
struct Vertex
{
struct Vertex{
vector_float2 position;
vector_float4 color;
};
// Vertex shader outputs and fragment shader inputs
struct RasterizerData
{
struct RasterizerData{
float4 position [[position]];
float4 color; //[[flat]]; // - use this flag to disable color interpolation
};
vertex RasterizerData
vertexShader(uint vertexID [[vertex_id]],
constant Vertex *vertices [[buffer(0)]],
constant vector_uint2 *viewportSizePointer [[buffer(1)]])
{
vertexShader(uint vertexID [[vertex_id]]){
RasterizerData out;
float2 pixelSpacePosition = vertices[vertexID].position.xy;
vector_float2 viewportSize = vector_float2(*viewportSizePointer);
float2 viewportSize = float2(viewport);
out.position = vector_float4(0.0, 0.0, 0.0, 1.0);
out.position.xy = pixelSpacePosition / (viewportSize / 2.0);
Expand All @@ -47,17 +42,12 @@ vertexShader(uint vertexID [[vertex_id]],
return out;
}
fragment float4 fragmentShader(RasterizerData in [[stage_in]])
{
fragment float4 fragmentShader(RasterizerData in [[stage_in]]){
// Return the interpolated color.
return in.color;
}
kernel void particleFunction(device Particle *particles [[ buffer(0) ]],
device Vertex *vertices [[buffer(1)]],
constant vector_uint2 *viewportSizePointer [[buffer(2)]],
constant float &scale [[buffer(3)]],
uint id [[ thread_position_in_grid ]]){
kernel void particleFunction(uint id [[ thread_position_in_grid ]]){
Particle particle = particles[id];
float size = particle.size*scale;
Expand All @@ -84,7 +74,7 @@ vertices[j].position = position + float2(size*sinA, size*cosA);
vertices[j+1].position = position + float2(size*sinA23, size*cosA23);
vertices[j+2].position = position + float2(size*sinA43, size*cosA43);
vector_float2 viewportSize = vector_float2(*viewportSizePointer);
float2 viewportSize = float2(viewport);
position += particle.velocity;
particles[id].position = position;
Expand Down
127 changes: 76 additions & 51 deletions Sources/MetalBuilder/MetalBuilderRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ public typealias MetalContent = (Binding<simd_uint2>)->MetalBuilderResult

public final class MetalBuilderRenderer{

var commandQueue : MTLCommandQueue!
var device : MTLDevice!
var commandQueue: MTLCommandQueue!
var device: MTLDevice!

var passes: [MetalPass] = []
var textures: [MTLTextureContainer] = []

var pixelFormat: MTLPixelFormat?

@MetalState var viewportSize: simd_uint2 = [0, 0]

public init(device: MTLDevice,
Expand All @@ -20,61 +21,85 @@ public final class MetalBuilderRenderer{
@MetalResultBuilder metalContent: MetalContent,
options: MTLCompileOptions? = nil) throws{

self.device = device

var librarySource = librarySource

do{

let result = metalContent($viewportSize)
self.device = device
self.pixelFormat = pixelFormat
commandQueue = self.device.makeCommandQueue()
var library : MTLLibrary!

if librarySource == ""{
library = self.device.makeDefaultLibrary()
}else{
library = try self.device.makeLibrary(source: librarySource, options: options)
}

//init passes
for component in result{
if let computeComponent = component as? Compute{
passes.append(ComputePass(computeComponent))
addTextures(newTexs: computeComponent.textures.map{ $0.container })
try createBuffers(buffers: computeComponent.buffers)
}
if let renderComponent = component as? Render{
passes.append(RenderPass(renderComponent))
addTextures(newTexs: renderComponent.vertexTextures.map{ $0.container })
addTextures(newTexs: renderComponent.fragTextures.map{ $0.container })
addTextures(newTexs: renderComponent.colorAttachments.values.map{ $0.texture })
try createBuffers(buffers: renderComponent.vertexBufs)
try createBuffers(buffers: renderComponent.fragBufs)
}
if let cpuCodeComponent = component as? CPUCode{
passes.append(CPUCodePass(cpuCodeComponent))
let result = metalContent($viewportSize)

//init passes
for component in result{
if let computeComponent = component as? Compute{
passes.append(ComputePass(computeComponent))
addTextures(newTexs: computeComponent.textures.map{ $0.container })
try createBuffers(buffers: computeComponent.buffers)

if librarySource != ""{
let kernel = MetalFunction.compute(computeComponent.kernel)
try addDeclaration(of: computeComponent.kernelArguments,
toHeaderOf: kernel, in: &librarySource)
}
}
if let renderComponent = component as? Render{
passes.append(RenderPass(renderComponent))
addTextures(newTexs: renderComponent.vertexTextures.map{ $0.container })
addTextures(newTexs: renderComponent.fragTextures.map{ $0.container })
addTextures(newTexs: renderComponent.colorAttachments.values.map{ $0.texture })
try createBuffers(buffers: renderComponent.vertexBufs)
try createBuffers(buffers: renderComponent.fragBufs)

if librarySource != ""{
let vertex = MetalFunction.vertex(renderComponent.vertexFunc)
try addDeclaration(of: renderComponent.vertexArguments,
toHeaderOf: vertex, in: &librarySource)
let fragment = MetalFunction.fragment(renderComponent.fragmentFunc)
try addDeclaration(of: renderComponent.fragmentArguments,
toHeaderOf: fragment, in: &librarySource)
}

}
if let cpuCodeComponent = component as? CPUCode{
passes.append(CPUCodePass(cpuCodeComponent))
}
if let mpsUnaryComponent = component as? MPSUnary{
addTextures(newTexs: [mpsUnaryComponent.inTexture, mpsUnaryComponent.outTexture])
passes.append(MPSUnaryPass(mpsUnaryComponent))
}
if let blitTextureComponent = component as? BlitTexture{
addTextures(newTexs: [blitTextureComponent.inTexture, blitTextureComponent.outTexture])
passes.append(BlitTexturePass(blitTextureComponent))
}
if let blitBufferComponent = component as? BlitBuffer{
// try createBuffers(buffers: [blitBufferComponent.inBuffer!,
// blitBufferComponent.outBuffer!])
passes.append(BlitBufferPass(blitBufferComponent))
}
}
if let mpsUnaryComponent = component as? MPSUnary{
addTextures(newTexs: [mpsUnaryComponent.inTexture, mpsUnaryComponent.outTexture])
passes.append(MPSUnaryPass(mpsUnaryComponent))

//init Library
self.pixelFormat = pixelFormat
var library : MTLLibrary!

if librarySource == ""{
library = self.device.makeDefaultLibrary()
}else{
library = try self.device.makeLibrary(source: librarySource, options: options)
}
if let blitTextureComponent = component as? BlitTexture{
addTextures(newTexs: [blitTextureComponent.inTexture, blitTextureComponent.outTexture])
passes.append(BlitTexturePass(blitTextureComponent))
commandQueue = self.device.makeCommandQueue()

//setup passes
for pass in passes{
try pass.setup(device: device, library: library)
}
if let blitBufferComponent = component as? BlitBuffer{
// try createBuffers(buffers: [blitBufferComponent.inBuffer!,
// blitBufferComponent.outBuffer!])
passes.append(BlitBufferPass(blitBufferComponent))

//create textures
for tex in textures{
try tex.create(device: device,
viewportSize: viewportSize,
pixelFormat: pixelFormat)
}
}
//setup passes
for pass in passes{
try pass.setup(device: device, library: library)
}
//create textures
for tex in textures{
try tex.create(device: device, viewportSize: viewportSize, pixelFormat: pixelFormat)
print("texture created")
}

}catch{ print(error) }
}
Expand Down
111 changes: 111 additions & 0 deletions Sources/MetalBuilder/Parser/FindFunction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@

import Foundation

enum MetalBuilderParserError: Error{
case syntaxError(String)
}

// find function "prefix some_type name" and returns range of "("...
func findFunction(_ function: MetalFunction, in source: String) throws -> Range<Substring.Index>{
let prefix = function.prefix
let name = function.name

// find prefix(kernel, vertex, fragment)
var s = source[source.startIndex..<source.endIndex]
var nameId: String.Index?
while true{
guard let prefixId = findPrefix(prefix, in: s)
else {
throw MetalBuilderParserError
.syntaxError("no "+prefix+" "+name+" function in source!")
}
// find "{"
guard let curlyId = s[prefixId...].range(of: "{")?.upperBound
else {
throw MetalBuilderParserError
.syntaxError("no '{' after '"+prefix+"'!")
}
// find name between prefix and "{"
guard let nameRange = s[prefixId...curlyId].range(of: name)
else {
guard let skipped = skipCurlies(in: s[curlyId...])
else {
throw MetalBuilderParserError
.syntaxError("no closing '}' after '"+name+"'!")
}
s = skipped
continue }
nameId = nameRange.lowerBound
break
}
guard let nameId = nameId
else {
throw MetalBuilderParserError
.syntaxError("no "+prefix+" "+name+" function in source!")
}
guard let bracketRange = source[nameId...].range(of: "(")
else {
throw MetalBuilderParserError
.syntaxError("expected '(' after '"+function.name+"'!")
}
return bracketRange
}


// takes substring starting with '{'
// return index after {...} expression
// or nil if there's no ending '}'
func skipCurlies(in s: String.SubSequence) -> String.SubSequence?{
var curlyCount = 1
for index in s.dropFirst().indices{
switch s[index]{
case "{": curlyCount += 1
case "}": curlyCount -= 1
default: break
}
if curlyCount == 0{ return s[index...].dropFirst() }
}
return nil
}
// takes substring starting with
// return index after (...) expression
// or nil if there's no ending ')'
func skipRounds(in s: String.SubSequence) -> String.SubSequence?{
var roundCount = 1
for index in s.dropFirst().indices{
switch s[index]{
case "(": roundCount += 1
case ")": roundCount -= 1
default: break
}
if roundCount == 0{ return s[index...].dropFirst() }
}
return nil
}

// find prefix (it should be outside any {...} or (...))
func findPrefix(_ prefix: String, in string: String.SubSequence) -> String.Index?{

var s = string[string.startIndex..<string.endIndex]
while true{
guard let firstCurly = s.range(of: "{")?.lowerBound
else { return nil }
guard let firstRound = s.range(of: "(")?.lowerBound
else { return nil }
guard let prefixRange = s.range(of: prefix)
else{ return nil }
if firstCurly<prefixRange.upperBound{
guard let skipped = skipCurlies(in: s[firstCurly...])
else{ return nil }
s = skipped
continue
}
if firstRound<prefixRange.upperBound{
guard let skipped = skipRounds(in: s[firstRound...])
else{ return nil }
s = skipped
continue
}
return prefixRange.lowerBound
}
}
20 changes: 20 additions & 0 deletions Sources/MetalBuilder/Parser/MetalFunction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

import Foundation

enum MetalFunction{
case vertex(String), fragment(String), compute(String)
var prefix: String{
switch self{
case .vertex(_): return "vertex"
case .fragment(_): return "fragment"
case .compute(_): return "kernel"
}
}
var name: String{
switch self{
case .vertex(let name): return name
case .fragment(let name): return name
case .compute(let name): return name
}
}
}
Loading

0 comments on commit 8f94829

Please sign in to comment.