Update heights of plane geometry in a shader kernel

This commit is contained in:
Eryn Wells 2018-11-11 19:50:43 -05:00
parent e5678f291b
commit 3462fa2458
4 changed files with 80 additions and 10 deletions

View file

@ -19,6 +19,7 @@ let maxBuffersInFlight = 3
enum RendererError: Error { enum RendererError: Error {
case badVertexDescriptor case badVertexDescriptor
case badComputeFunction
} }
class Renderer: NSObject, MTKViewDelegate { class Renderer: NSObject, MTKViewDelegate {
@ -31,6 +32,8 @@ class Renderer: NSObject, MTKViewDelegate {
var depthState: MTLDepthStencilState var depthState: MTLDepthStencilState
var colorMap: MTLTexture var colorMap: MTLTexture
var updateGeometryHeightsPipeline: MTLComputePipelineState
let inFlightSemaphore = DispatchSemaphore(value: maxBuffersInFlight) let inFlightSemaphore = DispatchSemaphore(value: maxBuffersInFlight)
let regenerationSemaphore = DispatchSemaphore(value: 1) let regenerationSemaphore = DispatchSemaphore(value: 1)
@ -46,6 +49,7 @@ class Renderer: NSObject, MTKViewDelegate {
var terrain: Terrain var terrain: Terrain
private var iterateTerrainAlgorithm = true private var iterateTerrainAlgorithm = true
private var didUpdateTerrain = false
init?(metalKitView: MTKView) { init?(metalKitView: MTKView) {
self.device = metalKitView.device! self.device = metalKitView.device!
@ -94,6 +98,13 @@ class Renderer: NSObject, MTKViewDelegate {
return nil return nil
} }
do {
updateGeometryHeightsPipeline = try Renderer.buildUpdateGeometryPipeline(withDevice: device, library: library)
} catch {
print("Unable to create update geometry pipeline. Error: \(error)")
return nil
}
super.init() super.init()
} }
@ -121,6 +132,13 @@ class Renderer: NSObject, MTKViewDelegate {
return try device.makeRenderPipelineState(descriptor: pipelineDescriptor) return try device.makeRenderPipelineState(descriptor: pipelineDescriptor)
} }
class func buildUpdateGeometryPipeline(withDevice device: MTLDevice, library: MTLLibrary) throws -> MTLComputePipelineState {
guard let computeFunction = library.makeFunction(name: "updateGeometryHeights") else {
throw RendererError.badComputeFunction
}
return try device.makeComputePipelineState(function: computeFunction)
}
class func loadTexture(device: MTLDevice, class func loadTexture(device: MTLDevice,
textureName: String) throws -> MTLTexture { textureName: String) throws -> MTLTexture {
/// Load texture data with optimal parameters for sampling /// Load texture data with optimal parameters for sampling
@ -146,6 +164,7 @@ class Renderer: NSObject, MTKViewDelegate {
print("Rendering terrain...") print("Rendering terrain...")
self.terrain.generator.render() self.terrain.generator.render()
print("Rendering terrain...complete!") print("Rendering terrain...complete!")
self.didUpdateTerrain = true
} }
} }
regenerationSemaphore.signal() regenerationSemaphore.signal()
@ -175,7 +194,10 @@ class Renderer: NSObject, MTKViewDelegate {
let modelMatrix = matrix4x4_rotation(radians: rotation, axis: rotationAxis) let modelMatrix = matrix4x4_rotation(radians: rotation, axis: rotationAxis)
let viewMatrix = matrix4x4_translation(0.0, -2.0, -8.0) let viewMatrix = matrix4x4_translation(0.0, -2.0, -8.0)
uniforms[0].modelViewMatrix = simd_mul(viewMatrix, modelMatrix) uniforms[0].modelViewMatrix = simd_mul(viewMatrix, modelMatrix)
rotation += 0.0025 rotation += 0.003
uniforms[0].terrainDimensions = terrain.dimensions
uniforms[0].terrainSegments = terrain.segments
} }
func draw(in view: MTKView) { func draw(in view: MTKView) {
@ -192,6 +214,9 @@ class Renderer: NSObject, MTKViewDelegate {
if didScheduleAlgorithmIteration && self.iterateTerrainAlgorithm { if didScheduleAlgorithmIteration && self.iterateTerrainAlgorithm {
self.iterateTerrainAlgorithm = false self.iterateTerrainAlgorithm = false
} }
if self.didUpdateTerrain {
self.didUpdateTerrain = false
}
regenSem.signal() regenSem.signal()
inFlightSem.signal() inFlightSem.signal()
} }
@ -210,6 +235,16 @@ class Renderer: NSObject, MTKViewDelegate {
didScheduleAlgorithmIteration = true didScheduleAlgorithmIteration = true
} }
if didScheduleAlgorithmIteration || didUpdateTerrain, let computeEncoder = commandBuffer.makeComputeCommandEncoder() {
print("Scheduling update geometry iteration")
computeEncoder.label = "Geometry Heights Encoder"
computeEncoder.pushDebugGroup("Update Geometry: Heights")
computeEncoder.setComputePipelineState(updateGeometryHeightsPipeline)
computeEncoder.dispatchThreads(MTLSize(width: Int(terrain.segments.x), height: Int(terrain.segments.y), depth: 1), threadsPerThreadgroup: MTLSize(width: 10, height: 10, depth: 1))
computeEncoder.popDebugGroup()
computeEncoder.endEncoding()
}
/// Delay getting the currentRenderPassDescriptor until we absolutely need it to avoid /// Delay getting the currentRenderPassDescriptor until we absolutely need it to avoid
/// holding onto the drawable and blocking the display pipeline any longer than necessary /// holding onto the drawable and blocking the display pipeline any longer than necessary
let renderPassDescriptor = view.currentRenderPassDescriptor let renderPassDescriptor = view.currentRenderPassDescriptor

View file

@ -41,16 +41,23 @@ typedef NS_ENUM(NSInteger, TextureIndex)
TextureIndexColor = 0, TextureIndexColor = 0,
}; };
typedef NS_ENUM(NSInteger, GeneratorTextureIndex) typedef NS_ENUM(NSInteger, GeneratorBufferIndex) {
{ GeneratorBufferIndexVertexes = 0,
GeneratorBufferIndexTexCoords = 1,
GeneratorBufferIndexIndexes = 2,
GeneratorBufferIndexUniforms = 3,
};
typedef NS_ENUM(NSInteger, GeneratorTextureIndex) {
GeneratorTextureIndexIn = 0, GeneratorTextureIndexIn = 0,
GeneratorTextureIndexOut = 1, GeneratorTextureIndexOut = 1,
}; };
typedef struct typedef struct {
{
matrix_float4x4 projectionMatrix; matrix_float4x4 projectionMatrix;
matrix_float4x4 modelViewMatrix; matrix_float4x4 modelViewMatrix;
packed_float2 terrainDimensions;
packed_uint2 terrainSegments;
} Uniforms; } Uniforms;
#define kRandomAlgorithmUniforms_RandomCount (41) #define kRandomAlgorithmUniforms_RandomCount (41)

View file

@ -34,15 +34,14 @@ vertex ColorInOut vertexShader(Vertex in [[stage_in]],
texture2d<float> heights [[texture(0)]], texture2d<float> heights [[texture(0)]],
constant Uniforms & uniforms [[buffer(BufferIndexUniforms)]]) constant Uniforms & uniforms [[buffer(BufferIndexUniforms)]])
{ {
constexpr sampler s(coord::normalized, address::clamp_to_zero, filter::linear); // constexpr sampler s(coord::normalized, address::clamp_to_zero, filter::linear);
ColorInOut out; ColorInOut out;
float4 height = heights.sample(s, in.texCoord); // float4 height = heights.sample(s, in.texCoord);
// Replace the y coordinate with the height we read from the texture. // Replace the y coordinate with the height we read from the texture.
float4 position(in.position.x, height.r, in.position.z, 1.0); out.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * float4(in.position, 1.0);
out.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * position;
out.normal = in.normal; out.normal = in.normal;
out.texCoord = in.texCoord; out.texCoord = in.texCoord;

View file

@ -43,13 +43,42 @@ private:
uint mSeed; uint mSeed;
}; };
kernel void updateGeometryHeights(texture2d<float> texture [[texture(GeneratorTextureIndexIn)]],
constant float2 *texCoords [[buffer(GeneratorBufferIndexTexCoords)]],
constant Uniforms &uniforms [[buffer(GeneratorBufferIndexUniforms)]],
device float3 *vertexes [[buffer(GeneratorBufferIndexVertexes)]],
uint2 tid [[thread_position_in_grid]])
{
constexpr sampler s(coord::normalized, address::clamp_to_zero, filter::linear);
const uint vIdx = tid.y * uniforms.terrainSegments.x + tid.x;
// Get the height from the texture.
float2 texCoord = texCoords[vIdx];
float4 height = texture.sample(s, texCoord);
// Update the vertex data.
vertexes[vIdx].y = height.r;
}
kernel void updateGeometryNormals(texture2d<float> texture [[texture(GeneratorTextureIndexIn)]],
constant float3 *vertexes [[buffer(GeneratorBufferIndexVertexes)]],
constant float2 *texCoords [[buffer(GeneratorBufferIndexTexCoords)]],
constant uint *indexes [[buffer(GeneratorBufferIndexIndexes)]],
constant Uniforms &uniforms [[buffer(GeneratorBufferIndexUniforms)]],
uint2 tid [[thread_position_in_grid]])
{
}
#pragma mark - ZeroGenerator
kernel void zeroKernel(texture2d<float, access::write> outTexture [[texture(GeneratorTextureIndexOut)]], kernel void zeroKernel(texture2d<float, access::write> outTexture [[texture(GeneratorTextureIndexOut)]],
uint2 tid [[thread_position_in_grid]]) uint2 tid [[thread_position_in_grid]])
{ {
outTexture.write(0, tid); outTexture.write(0, tid);
} }
#pragma mark - RandomAlgorithm #pragma mark - RandomGenerator
kernel void randomKernel(texture2d<float, access::write> outTexture [[texture(GeneratorTextureIndexOut)]], kernel void randomKernel(texture2d<float, access::write> outTexture [[texture(GeneratorTextureIndexOut)]],
constant RandomAlgorithmUniforms &uniforms [[buffer(0)]], constant RandomAlgorithmUniforms &uniforms [[buffer(0)]],