Update heights of plane geometry in a shader kernel
This commit is contained in:
parent
e5678f291b
commit
3462fa2458
4 changed files with 80 additions and 10 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)]],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue