-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit eee874f
Showing
15 changed files
with
1,195 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
name: Run Tests and Create Tag | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
paths-ignore: [ '.gitignore', '.swift-format', 'README.md', 'LICENSE' ] | ||
|
||
jobs: | ||
run_tests_and_create_tag: | ||
runs-on: macos-14 | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.CUSTOM_ACCESS_TOKEN }} | ||
permissions: | ||
contents: write | ||
steps: | ||
- name: Set XCode version | ||
uses: maxim-lobanov/setup-xcode@v1 | ||
with: | ||
xcode-version: "15" | ||
- name: Checkout project including tags | ||
uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
- name: Run tests | ||
run: swift test | ||
- name: Get next version | ||
id: get-next-version | ||
uses: paulhatch/semantic-version@v5.0.0-alpha2 | ||
with: | ||
tag_prefix: "v" | ||
search_commit_body: false | ||
- uses: ncipollo/release-action@v1 | ||
with: | ||
commit: main | ||
tag: ${{ steps.get-next-version.outputs.version_tag }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
name: Run Tests PR | ||
|
||
on: | ||
pull_request: | ||
branches: [ main ] | ||
paths-ignore: [ '.gitignore', '.swift-format', 'README.md', 'LICENSE' ] | ||
|
||
jobs: | ||
run_tests_pr: | ||
runs-on: macos-14 | ||
steps: | ||
- name: Set XCode version | ||
uses: maxim-lobanov/setup-xcode@v1 | ||
with: | ||
xcode-version: "15" | ||
- name: Checkout project including tags | ||
uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 | ||
- name: Run tests | ||
run: swift test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
.DS_Store | ||
/.build | ||
/Packages | ||
xcuserdata/ | ||
DerivedData/ | ||
.swiftpm/configuration/registries.json | ||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata | ||
.netrc | ||
.build | ||
.swiftpm | ||
*.gputrace | ||
*.pgm | ||
*.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"originHash" : "3291dbb33922ce36c22b90713335195658a0672ec9059869b5353bc1d0355a66", | ||
"pins" : [ | ||
{ | ||
"identity" : "spmopensimplex2", | ||
"kind" : "remoteSourceControl", | ||
"location" : "https://github.com/andystanton/SPMOpenSimplex2.git", | ||
"state" : { | ||
"revision" : "54fef25b02f1b8afd827dd53495a1c12a77659a4", | ||
"version" : "0.0.10" | ||
} | ||
} | ||
], | ||
"version" : 3 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// swift-tools-version: 5.10 | ||
|
||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "SPMNoiseGeneration", | ||
products: [ | ||
.library( | ||
name: "SPMNoiseGeneration", | ||
targets: ["SPMNoiseGeneration"]), | ||
], | ||
dependencies: [ | ||
.package(url: "https://github.com/andystanton/SPMOpenSimplex2.git", "0.0.0"..<"0.1.0") | ||
], | ||
targets: [ | ||
.target( | ||
name: "SPMNoiseGeneration", | ||
dependencies: [ | ||
.product(name: "SPMOpenSimplex2", package: "spmopensimplex2")]), | ||
.testTarget( | ||
name: "SPMNoiseGenerationTests", | ||
dependencies: ["SPMNoiseGeneration"]), | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# SPM Fractal Noise | ||
|
||
A Swift Package containing a fractional Brownian Motion implementation over a noise function. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import SPMOpenSimplex2 | ||
|
||
public struct FractalOpenSimplex2NoiseParameters { | ||
let openSimplex2Variant: OpenSimplex2Noise2Variant | ||
let openSimplex3Variant: OpenSimplex2Noise3Variant | ||
let openSimplex4Variant: OpenSimplex2Noise4Variant | ||
|
||
init( | ||
openSimplex2Variant: OpenSimplex2Noise2Variant = .standard, | ||
openSimplex3Variant: OpenSimplex2Noise3Variant = .xy, | ||
openSimplex4Variant: OpenSimplex2Noise4Variant = .xyz | ||
) { | ||
self.openSimplex2Variant = openSimplex2Variant | ||
self.openSimplex3Variant = openSimplex3Variant | ||
self.openSimplex4Variant = openSimplex4Variant | ||
} | ||
} | ||
|
||
public enum FractalNoiseTypeParameters { | ||
case OpenSimplex2(FractalOpenSimplex2NoiseParameters) | ||
} | ||
|
||
public struct FractalNoiseParameters { | ||
let noiseTypeParameters: FractalNoiseTypeParameters | ||
|
||
let octaves: Int32 | ||
let lacunarity: Float | ||
let hurstExponent: Float | ||
|
||
let startingAmplitude: Float | ||
let startingFrequency: Float | ||
} | ||
|
||
public protocol FractalNoise { | ||
func noise3(seed: Int32, coord: SIMD3<Float>, fractalNoiseParameters: FractalNoiseParameters) -> Float | ||
func noise3(seed: Int32, coords: [SIMD3<Float>], fractalNoiseParameters: FractalNoiseParameters) -> [Float] | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import SPMOpenSimplex2 | ||
import simd | ||
|
||
public class FractalNoiseCPU { | ||
private let openSimplex2: OpenSimplex2CPU | ||
|
||
public init() { | ||
self.openSimplex2 = OpenSimplex2CPU() | ||
} | ||
|
||
private func getNoise3Value( | ||
seed: Int32, | ||
coord: SIMD3<Float>, | ||
noiseType: FractalNoiseTypeParameters | ||
) -> Float { | ||
switch noiseType { | ||
case .OpenSimplex2(let parameters): | ||
return openSimplex2.noise3( | ||
seed: seed, | ||
coord: coord, | ||
variant: parameters.openSimplex3Variant) | ||
} | ||
} | ||
} | ||
|
||
extension FractalNoiseCPU: FractalNoise { | ||
public func noise3( | ||
seed: Int32, | ||
coords: [SIMD3<Float>], | ||
fractalNoiseParameters: FractalNoiseParameters | ||
) -> [Float] { | ||
return coords.map { | ||
noise3( | ||
seed: seed, | ||
coord: $0, | ||
fractalNoiseParameters: fractalNoiseParameters) | ||
} | ||
} | ||
|
||
public func noise3( | ||
seed: Int32, | ||
coord: SIMD3<Float>, | ||
fractalNoiseParameters: FractalNoiseParameters | ||
) -> Float { | ||
var fractalNoise = Float.zero | ||
var amplitude = fractalNoiseParameters.startingAmplitude | ||
var frequency = fractalNoiseParameters.startingFrequency | ||
let gain = exp2(-fractalNoiseParameters.hurstExponent) | ||
|
||
for _ in 0..<fractalNoiseParameters.octaves { | ||
fractalNoise += amplitude * getNoise3Value( | ||
seed: seed, | ||
coord: coord * frequency, | ||
noiseType: fractalNoiseParameters.noiseTypeParameters) | ||
|
||
frequency *= fractalNoiseParameters.lacunarity | ||
amplitude *= gain | ||
} | ||
return fractalNoise | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import SPMOpenSimplex2 | ||
import simd | ||
import Metal | ||
|
||
public class FractalNoiseMetal { | ||
private let commandQueue: MTLCommandQueue! | ||
private let device: MTLDevice! | ||
|
||
private let noise2Pipeline: MTLComputePipelineState? | ||
private let noise3Pipeline: MTLComputePipelineState? | ||
private let noise4Pipeline: MTLComputePipelineState? | ||
|
||
public init( | ||
device: MTLDevice? = nil, | ||
commandQueue: MTLCommandQueue? = nil | ||
) { | ||
self.device = device ?? MTLCreateSystemDefaultDevice()! | ||
self.commandQueue = commandQueue ?? self.device.makeCommandQueue() | ||
|
||
let kernel = """ | ||
#include <metal_stdlib> | ||
using namespace metal; | ||
\(OpenSimplex2MetalShaderLoader(dimensionality: [.two, .three, .four]).shader) | ||
\(FractalNoiseMetalShaderLoader(dimensionality: [.two, .three, .four]).shader) | ||
""" | ||
|
||
let library = try! self.device.makeLibrary(source: kernel, options: nil) | ||
|
||
if let noise2Function = library.makeFunction(name: FractalNoiseMetalShaderLoader.noise2FunctionName) { | ||
noise2Pipeline = try! self.device.makeComputePipelineState(function: noise2Function) | ||
} else { | ||
noise2Pipeline = nil | ||
} | ||
if let noise3Function = library.makeFunction(name: FractalNoiseMetalShaderLoader.noise3FunctionName) { | ||
noise3Pipeline = try! self.device.makeComputePipelineState(function: noise3Function) | ||
} else { | ||
noise3Pipeline = nil | ||
} | ||
if let noise4Function = library.makeFunction(name: FractalNoiseMetalShaderLoader.noise4FunctionName) { | ||
noise4Pipeline = try! self.device.makeComputePipelineState(function: noise4Function) | ||
} else { | ||
noise4Pipeline = nil | ||
} | ||
} | ||
|
||
private func executeNoiseFunction( | ||
pipeline: MTLComputePipelineState, | ||
seed: Int32, | ||
fractalNoiseParameters: FractalNoiseParameters, | ||
inBuffer: MTLBuffer, | ||
inputCount: Int | ||
) -> [Float] { | ||
let outByteLength = MemoryLayout<Float>.stride * inputCount | ||
let outBuffer = device.makeBuffer(length: outByteLength, options: [.storageModeShared])! | ||
|
||
guard let commandBuffer = commandQueue.makeCommandBuffer(), | ||
let commandEncoder = commandBuffer.makeComputeCommandEncoder() else { | ||
return [] | ||
} | ||
|
||
let (noiseType, noiseTypeParameters) = switch fractalNoiseParameters.noiseTypeParameters { | ||
case .OpenSimplex2(let params): | ||
(FractalNoiseMetalType.OpenSimplex2, | ||
FractalNoiseMetalTypeParameters.OpenSimplex2( | ||
OpenSimplex2MetalParameters( | ||
seed: seed, | ||
noise2Variant: params.openSimplex2Variant.toMetalVariant(), | ||
noise3Variant: params.openSimplex3Variant.toMetalVariant(), | ||
noise4Variant: params.openSimplex4Variant.toMetalVariant()))) | ||
} | ||
|
||
var uniforms = FractalNoiseMetalParameters( | ||
lacunarity: fractalNoiseParameters.lacunarity, | ||
gain: exp2(-fractalNoiseParameters.hurstExponent), | ||
startingAmplitude: fractalNoiseParameters.startingAmplitude, | ||
startingFrequency: fractalNoiseParameters.startingFrequency, | ||
octaves: fractalNoiseParameters.octaves, | ||
noiseType: noiseType, | ||
noiseTypeParameters: noiseTypeParameters) | ||
|
||
commandEncoder.setComputePipelineState(pipeline) | ||
commandEncoder.setBytes(&uniforms, length: MemoryLayout<FractalNoiseMetalParameters>.stride, index: 0) | ||
commandEncoder.setBuffer(inBuffer, offset: 0, index: 1) | ||
commandEncoder.setBuffer(outBuffer, offset: 0, index: 2) | ||
|
||
let groupWidth = inputCount > pipeline.maxTotalThreadsPerThreadgroup | ||
? pipeline.maxTotalThreadsPerThreadgroup | ||
: inputCount | ||
let groupSize = MTLSize(width: groupWidth, height: 1, depth: 1) | ||
|
||
let (groupCount, groupCountRemainder) = inputCount.quotientAndRemainder(dividingBy: groupWidth) | ||
let finalGroupCount = groupCount + (groupCountRemainder > 0 ? 1 : 0) | ||
|
||
let gridSize = MTLSize(width: groupWidth * finalGroupCount, height: 1, depth: 1) | ||
|
||
commandEncoder.dispatchThreads(gridSize, threadsPerThreadgroup: groupSize) | ||
|
||
commandEncoder.endEncoding() | ||
|
||
commandBuffer.commit() | ||
commandBuffer.waitUntilCompleted() | ||
|
||
return downloadFromBuffer(outBuffer, count: inputCount) | ||
} | ||
|
||
private func uploadToBuffer<T>(_ buffer: MTLBuffer, data: [T], offset: Int = 0) { | ||
let memoryPointer = buffer.contents().advanced(by: offset) | ||
memcpy(memoryPointer, data, MemoryLayout<T>.stride * data.count) | ||
} | ||
|
||
private func downloadFromBuffer<T>(_ buffer: MTLBuffer, count: Int, offset: Int = 0) -> [T] { | ||
let memoryPointer = buffer.contents().advanced(by: offset) | ||
let typedPointer = memoryPointer.bindMemory(to: T.self, capacity: MemoryLayout<T>.stride * count) | ||
let bufferedPointer = UnsafeBufferPointer(start: typedPointer, count: count) | ||
return Array(bufferedPointer) | ||
} | ||
} | ||
|
||
extension FractalNoiseMetal: FractalNoise { | ||
public func noise3( | ||
seed: Int32, | ||
coord: SIMD3<Float>, | ||
fractalNoiseParameters: FractalNoiseParameters | ||
) -> Float { | ||
return noise3( | ||
seed: seed, | ||
coords: [coord], | ||
fractalNoiseParameters: fractalNoiseParameters)[0] | ||
} | ||
|
||
public func noise3( | ||
seed: Int32, | ||
coords: [SIMD3<Float>], | ||
fractalNoiseParameters: FractalNoiseParameters | ||
) -> [Float] { | ||
guard let pipeline = noise3Pipeline else { | ||
return [] | ||
} | ||
let inByteLength = MemoryLayout<SIMD3<Float>>.stride * coords.count | ||
let inBuffer = device.makeBuffer(length: inByteLength, options: [.storageModeShared])! | ||
uploadToBuffer(inBuffer, data: coords, offset: 0) | ||
|
||
return executeNoiseFunction( | ||
pipeline: pipeline, | ||
seed: seed, | ||
fractalNoiseParameters: fractalNoiseParameters, | ||
inBuffer: inBuffer, | ||
inputCount: coords.count) | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
Sources/SPMNoiseGeneration/metal/FractalNoiseMetalNoise2Shader.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
public class FractalNoiseMetalNoise2: FractalNoiseMetalNoiseShader { | ||
static var functionName: String = "fractalNoise2" | ||
|
||
static var metalFunction: String = | ||
""" | ||
kernel void \(functionName)( | ||
constant FractalNoiseMetalParameters &uniforms [[ buffer(0) ]], | ||
constant const float2 * in [[ buffer(1) ]], | ||
device float * out [[ buffer(2) ]], | ||
uint2 thread_position_in_grid [[ thread_position_in_grid ]] | ||
) { | ||
int index = thread_position_in_grid.x; | ||
} | ||
""" | ||
|
||
static var metalFunctionWithNormal: String? = nil | ||
|
||
static var functionWithNormalName: String? = nil | ||
|
||
static var baseFunction: String = | ||
""" | ||
""" | ||
} | ||
|
Oops, something went wrong.