Proof of concept for Algorithm objects
Next up compute kernel dispatch!
This commit is contained in:
parent
96730ecd41
commit
55a134882d
6 changed files with 160 additions and 34 deletions
|
@ -7,6 +7,8 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
C08C58A0218F46F000EAFC2D /* Algorithms.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C589F218F46F000EAFC2D /* Algorithms.swift */; };
|
||||||
|
C08C58A2218F474E00EAFC2D /* TerrainAlgorithms.metal in Sources */ = {isa = PBXBuildFile; fileRef = C08C58A1218F474E00EAFC2D /* TerrainAlgorithms.metal */; };
|
||||||
C0C15A8E218DDD85007494E2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C15A8D218DDD85007494E2 /* AppDelegate.swift */; };
|
C0C15A8E218DDD85007494E2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C15A8D218DDD85007494E2 /* AppDelegate.swift */; };
|
||||||
C0C15A90218DDD87007494E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0C15A8F218DDD87007494E2 /* Assets.xcassets */; };
|
C0C15A90218DDD87007494E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0C15A8F218DDD87007494E2 /* Assets.xcassets */; };
|
||||||
C0C15A93218DDD87007494E2 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = C0C15A91218DDD87007494E2 /* MainMenu.xib */; };
|
C0C15A93218DDD87007494E2 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = C0C15A91218DDD87007494E2 /* MainMenu.xib */; };
|
||||||
|
@ -27,6 +29,8 @@
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
C08C589F218F46F000EAFC2D /* Algorithms.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Algorithms.swift; sourceTree = "<group>"; };
|
||||||
|
C08C58A1218F474E00EAFC2D /* TerrainAlgorithms.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = TerrainAlgorithms.metal; sourceTree = "<group>"; };
|
||||||
C0C15A8A218DDD85007494E2 /* Terrain.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Terrain.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
C0C15A8A218DDD85007494E2 /* Terrain.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Terrain.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
C0C15A8D218DDD85007494E2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
C0C15A8D218DDD85007494E2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
C0C15A8F218DDD87007494E2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
C0C15A8F218DDD87007494E2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
@ -125,7 +129,9 @@
|
||||||
C0C15AB4218E2A90007494E2 /* GameViewController.swift */,
|
C0C15AB4218E2A90007494E2 /* GameViewController.swift */,
|
||||||
C0C15AB6218E2A90007494E2 /* Renderer.swift */,
|
C0C15AB6218E2A90007494E2 /* Renderer.swift */,
|
||||||
C0C15AC5218E32B2007494E2 /* Terrain.swift */,
|
C0C15AC5218E32B2007494E2 /* Terrain.swift */,
|
||||||
|
C08C589F218F46F000EAFC2D /* Algorithms.swift */,
|
||||||
C0C15AB8218E2A90007494E2 /* Shaders.metal */,
|
C0C15AB8218E2A90007494E2 /* Shaders.metal */,
|
||||||
|
C08C58A1218F474E00EAFC2D /* TerrainAlgorithms.metal */,
|
||||||
C0C15ABA218E2A90007494E2 /* ShaderTypes.h */,
|
C0C15ABA218E2A90007494E2 /* ShaderTypes.h */,
|
||||||
C0C15ABB218E2A90007494E2 /* Assets.xcassets */,
|
C0C15ABB218E2A90007494E2 /* Assets.xcassets */,
|
||||||
C0C15ABD218E2A90007494E2 /* Main.storyboard */,
|
C0C15ABD218E2A90007494E2 /* Main.storyboard */,
|
||||||
|
@ -248,11 +254,13 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
C08C58A2218F474E00EAFC2D /* TerrainAlgorithms.metal in Sources */,
|
||||||
C0C15AB9218E2A90007494E2 /* Shaders.metal in Sources */,
|
C0C15AB9218E2A90007494E2 /* Shaders.metal in Sources */,
|
||||||
C0C15AB5218E2A90007494E2 /* GameViewController.swift in Sources */,
|
C0C15AB5218E2A90007494E2 /* GameViewController.swift in Sources */,
|
||||||
C0C15AC6218E32B3007494E2 /* Terrain.swift in Sources */,
|
C0C15AC6218E32B3007494E2 /* Terrain.swift in Sources */,
|
||||||
C0C15AB7218E2A90007494E2 /* Renderer.swift in Sources */,
|
C0C15AB7218E2A90007494E2 /* Renderer.swift in Sources */,
|
||||||
C0C15AB3218E2A90007494E2 /* AppDelegate.swift in Sources */,
|
C0C15AB3218E2A90007494E2 /* AppDelegate.swift in Sources */,
|
||||||
|
C08C58A0218F46F000EAFC2D /* Algorithms.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
104
Terrain2/Algorithms.swift
Normal file
104
Terrain2/Algorithms.swift
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
//
|
||||||
|
// Algorithms.swift
|
||||||
|
// Terrain2
|
||||||
|
//
|
||||||
|
// Created by Eryn Wells on 11/4/18.
|
||||||
|
// Copyright © 2018 Eryn Wells. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Metal
|
||||||
|
|
||||||
|
enum KernelError: Error {
|
||||||
|
case badFunction
|
||||||
|
case textureCreationFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol Algorithm {
|
||||||
|
static var name: String { get }
|
||||||
|
var outTexture: MTLTexture { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Kernel {
|
||||||
|
static let textureSize = MTLSize(width: 512, height: 512, depth: 1)
|
||||||
|
|
||||||
|
class func buildTexture(device: MTLDevice, size: MTLSize) -> MTLTexture? {
|
||||||
|
let desc = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .r32Float, width: size.width, height: size.height, mipmapped: false)
|
||||||
|
desc.usage = [.shaderRead, .shaderWrite]
|
||||||
|
let tex = device.makeTexture(descriptor: desc)
|
||||||
|
return tex
|
||||||
|
}
|
||||||
|
|
||||||
|
let pipeline: MTLComputePipelineState
|
||||||
|
let textures: [MTLTexture]
|
||||||
|
|
||||||
|
var outTexture: MTLTexture {
|
||||||
|
return textures[indexes.out]
|
||||||
|
}
|
||||||
|
|
||||||
|
private(set) var indexes: (`in`: Int, out: Int) = (in: 0, out: 1)
|
||||||
|
|
||||||
|
init(device: MTLDevice, library: MTLLibrary, functionName: String) throws {
|
||||||
|
guard let computeFunction = library.makeFunction(name: functionName) else {
|
||||||
|
throw KernelError.badFunction
|
||||||
|
}
|
||||||
|
self.pipeline = try device.makeComputePipelineState(function: computeFunction)
|
||||||
|
|
||||||
|
// Create our input and output textures
|
||||||
|
var textures = [MTLTexture]()
|
||||||
|
for i in 0..<2 {
|
||||||
|
guard let tex = Kernel.buildTexture(device: device, size: Kernel.textureSize) else {
|
||||||
|
print("Couldn't create heights texture i=\(i)")
|
||||||
|
throw KernelError.textureCreationFailed
|
||||||
|
}
|
||||||
|
textures.append(tex)
|
||||||
|
}
|
||||||
|
self.textures = textures
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(in encoder: MTLComputeCommandEncoder, inTexture: MTLTexture?, outTexture: MTLTexture?) {
|
||||||
|
encoder.setComputePipelineState(pipeline)
|
||||||
|
encoder.setTexture(inTexture, index: GeneratorTextureIndex.in.rawValue)
|
||||||
|
encoder.setTexture(inTexture, index: GeneratorTextureIndex.out.rawValue)
|
||||||
|
encoder.dispatchThreads(Kernel.textureSize, threadsPerThreadgroup: MTLSize(width: 8, height: 8, depth: 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "Compute" zero for every value of the height map.
|
||||||
|
class ZeroAlgorithm: Kernel, Algorithm {
|
||||||
|
static let name = "Zero"
|
||||||
|
|
||||||
|
init?(device: MTLDevice, library: MTLLibrary) {
|
||||||
|
do {
|
||||||
|
try super.init(device: device, library: library, functionName: "zeroKernel")
|
||||||
|
} catch let e {
|
||||||
|
print("Couldn't create compute kernel. Error: \(e)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Randomly generate heights that are independent of all others.
|
||||||
|
class RandomAlgorithm: Kernel, Algorithm {
|
||||||
|
static let name = "Random"
|
||||||
|
|
||||||
|
init?(device: MTLDevice, library: MTLLibrary) {
|
||||||
|
do {
|
||||||
|
try super.init(device: device, library: library, functionName: "randomKernel")
|
||||||
|
} catch let e {
|
||||||
|
print("Couldn't create compute kernel. Error: \(e)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of the Diamond-Squares algorithm.
|
||||||
|
/// - https://en.wikipedia.org/wiki/Diamond-square_algorithm
|
||||||
|
//class DiamondSquareAlgorithm: Algorithm {
|
||||||
|
// static let name = "Diamond-Square"
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// Implementation of the Circles algorithm.
|
||||||
|
//class CirclesAlgorithm: Algorithm {
|
||||||
|
// static let name = "Circles"
|
||||||
|
//}
|
|
@ -24,6 +24,7 @@ enum RendererError: Error {
|
||||||
class Renderer: NSObject, MTKViewDelegate {
|
class Renderer: NSObject, MTKViewDelegate {
|
||||||
|
|
||||||
public let device: MTLDevice
|
public let device: MTLDevice
|
||||||
|
let library: MTLLibrary
|
||||||
let commandQueue: MTLCommandQueue
|
let commandQueue: MTLCommandQueue
|
||||||
var dynamicUniformBuffer: MTLBuffer
|
var dynamicUniformBuffer: MTLBuffer
|
||||||
var pipelineState: MTLRenderPipelineState
|
var pipelineState: MTLRenderPipelineState
|
||||||
|
@ -61,10 +62,17 @@ class Renderer: NSObject, MTKViewDelegate {
|
||||||
metalKitView.colorPixelFormat = MTLPixelFormat.bgra8Unorm_srgb
|
metalKitView.colorPixelFormat = MTLPixelFormat.bgra8Unorm_srgb
|
||||||
metalKitView.sampleCount = 1
|
metalKitView.sampleCount = 1
|
||||||
|
|
||||||
terrain = Terrain(dimensions: float2(10, 10), segments: uint2(100, 100), device: device)!
|
guard let library = device.makeDefaultLibrary() else {
|
||||||
|
print("Unable to create default library")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
self.library = library
|
||||||
|
|
||||||
|
terrain = Terrain(dimensions: float2(10, 10), segments: uint2(100, 100), device: device, library: library)!
|
||||||
|
|
||||||
do {
|
do {
|
||||||
pipelineState = try Renderer.buildRenderPipelineWithDevice(device: device,
|
pipelineState = try Renderer.buildRenderPipelineWithDevice(device: device,
|
||||||
|
library: library,
|
||||||
metalKitView: metalKitView,
|
metalKitView: metalKitView,
|
||||||
mtlVertexDescriptor: terrain.vertexDescriptor)
|
mtlVertexDescriptor: terrain.vertexDescriptor)
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -89,14 +97,13 @@ class Renderer: NSObject, MTKViewDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
class func buildRenderPipelineWithDevice(device: MTLDevice,
|
class func buildRenderPipelineWithDevice(device: MTLDevice,
|
||||||
|
library: MTLLibrary,
|
||||||
metalKitView: MTKView,
|
metalKitView: MTKView,
|
||||||
mtlVertexDescriptor: MTLVertexDescriptor) throws -> MTLRenderPipelineState {
|
mtlVertexDescriptor: MTLVertexDescriptor) throws -> MTLRenderPipelineState {
|
||||||
/// Build a render state pipeline object
|
/// Build a render state pipeline object
|
||||||
|
|
||||||
let library = device.makeDefaultLibrary()
|
let vertexFunction = library.makeFunction(name: "vertexShader")
|
||||||
|
let fragmentFunction = library.makeFunction(name: "fragmentShader")
|
||||||
let vertexFunction = library?.makeFunction(name: "vertexShader")
|
|
||||||
let fragmentFunction = library?.makeFunction(name: "fragmentShader")
|
|
||||||
|
|
||||||
let pipelineDescriptor = MTLRenderPipelineDescriptor()
|
let pipelineDescriptor = MTLRenderPipelineDescriptor()
|
||||||
pipelineDescriptor.label = "RenderPipeline"
|
pipelineDescriptor.label = "RenderPipeline"
|
||||||
|
@ -203,7 +210,7 @@ class Renderer: NSObject, MTKViewDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEncoder.setVertexTexture(terrain.heightMap, index: 0)
|
renderEncoder.setVertexTexture(terrain.algorithm.outTexture, index: 0)
|
||||||
renderEncoder.setFragmentTexture(colorMap, index: TextureIndex.color.rawValue)
|
renderEncoder.setFragmentTexture(colorMap, index: TextureIndex.color.rawValue)
|
||||||
|
|
||||||
for submesh in terrain.mesh.submeshes {
|
for submesh in terrain.mesh.submeshes {
|
||||||
|
|
|
@ -41,6 +41,12 @@ typedef NS_ENUM(NSInteger, TextureIndex)
|
||||||
TextureIndexColor = 0,
|
TextureIndexColor = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, GeneratorTextureIndex)
|
||||||
|
{
|
||||||
|
GeneratorTextureIndexIn = 0,
|
||||||
|
GeneratorTextureIndexOut = 1,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
matrix_float4x4 projectionMatrix;
|
matrix_float4x4 projectionMatrix;
|
||||||
|
|
|
@ -72,36 +72,14 @@ class Terrain: NSObject {
|
||||||
|
|
||||||
private static let heightMapSize = MTLSize(width: 512, height: 512, depth: 1)
|
private static let heightMapSize = MTLSize(width: 512, height: 512, depth: 1)
|
||||||
|
|
||||||
class func buildHeightsTexture(device: MTLDevice) -> MTLTexture? {
|
|
||||||
let heightsDesc = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .r32Float, width: heightMapSize.width, height: heightMapSize.height, mipmapped: false)
|
|
||||||
heightsDesc.usage = [.shaderRead, .shaderWrite]
|
|
||||||
|
|
||||||
let tex = device.makeTexture(descriptor: heightsDesc)
|
|
||||||
|
|
||||||
if let tex = tex {
|
|
||||||
var initialHeights = [Float]()
|
|
||||||
let numberOfHeights = tex.height * tex.width
|
|
||||||
initialHeights.reserveCapacity(numberOfHeights)
|
|
||||||
for _ in 0..<numberOfHeights {
|
|
||||||
initialHeights.append(Float.random(in: 0...0.5))
|
|
||||||
}
|
|
||||||
let origin = MTLOrigin(x: 0, y: 0, z: 0)
|
|
||||||
let size = MTLSize(width: tex.width, height: tex.height, depth: 1)
|
|
||||||
let region = MTLRegion(origin: origin, size: size)
|
|
||||||
let bytesPerRow = MemoryLayout<Float>.stride * tex.width
|
|
||||||
tex.replace(region: region, mipmapLevel: 0, withBytes: initialHeights, bytesPerRow: bytesPerRow)
|
|
||||||
}
|
|
||||||
|
|
||||||
return tex
|
|
||||||
}
|
|
||||||
|
|
||||||
let dimensions: float2
|
let dimensions: float2
|
||||||
let segments: uint2
|
let segments: uint2
|
||||||
let vertexDescriptor: MTLVertexDescriptor
|
let vertexDescriptor: MTLVertexDescriptor
|
||||||
let mesh: MTKMesh
|
let mesh: MTKMesh
|
||||||
let heightMap: MTLTexture
|
|
||||||
|
|
||||||
init?(dimensions dim: float2, segments seg: uint2, device: MTLDevice) {
|
var algorithm: Algorithm
|
||||||
|
|
||||||
|
init?(dimensions dim: float2, segments seg: uint2, device: MTLDevice, library: MTLLibrary) {
|
||||||
dimensions = dim
|
dimensions = dim
|
||||||
segments = seg
|
segments = seg
|
||||||
vertexDescriptor = Terrain.buildVertexDescriptor()
|
vertexDescriptor = Terrain.buildVertexDescriptor()
|
||||||
|
@ -113,11 +91,11 @@ class Terrain: NSObject {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let tex = Terrain.buildHeightsTexture(device: device) else {
|
guard let alg = ZeroAlgorithm(device: device, library: library) else {
|
||||||
print("Couldn't create heights texture")
|
print("Couldn't create algorithm")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
heightMap = tex
|
algorithm = alg
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
23
Terrain2/TerrainAlgorithms.metal
Normal file
23
Terrain2/TerrainAlgorithms.metal
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// TerrainAlgorithms.metal
|
||||||
|
// Terrain2
|
||||||
|
//
|
||||||
|
// Created by Eryn Wells on 11/4/18.
|
||||||
|
// Copyright © 2018 Eryn Wells. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <metal_stdlib>
|
||||||
|
#include "ShaderTypes.h"
|
||||||
|
using namespace metal;
|
||||||
|
|
||||||
|
kernel void zeroKernel(texture2d<float, access::write> outTexture [[texture(GeneratorTextureIndexOut)]],
|
||||||
|
uint2 tid [[thread_position_in_grid]])
|
||||||
|
{
|
||||||
|
outTexture.write(0, tid);
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel void randomKernel(texture2d<float, access::write> outTexture [[texture(GeneratorTextureIndexOut)]],
|
||||||
|
uint2 tid [[thread_position_in_grid]])
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue