From a149de885cb17b87503ae1c106416590317534f2 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sun, 1 Sep 2024 19:41:22 -0400 Subject: [PATCH 1/3] Parallelize chunk generation using fan-out pattern. --- Sources/Voxelotl/World.swift | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Sources/Voxelotl/World.swift b/Sources/Voxelotl/World.swift index 33984d9..11d81b4 100644 --- a/Sources/Voxelotl/World.swift +++ b/Sources/Voxelotl/World.swift @@ -62,15 +62,31 @@ public class World { func generate(width: Int, height: Int, depth: Int, seed: UInt64) { self._generator.reset(seed: seed) let orig = SIMD3(width, height, depth) / 2 + + var localChunks: [SIMD3: Chunk] = [:] + let localChunksLock = NSLock() + let queue = OperationQueue() + queue.qualityOfService = .userInitiated for z in 0.. Date: Sun, 1 Sep 2024 22:30:47 -0400 Subject: [PATCH 2/3] implement a safe concurrent dictionary --- Sources/Voxelotl/CMakeLists.txt | 4 + .../Common/ConcurrentDictionary.swift | 73 +++++++++++++++++++ Sources/Voxelotl/World.swift | 7 +- 3 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 Sources/Voxelotl/Common/ConcurrentDictionary.swift diff --git a/Sources/Voxelotl/CMakeLists.txt b/Sources/Voxelotl/CMakeLists.txt index ec4b1c8..a4cee82 100644 --- a/Sources/Voxelotl/CMakeLists.txt +++ b/Sources/Voxelotl/CMakeLists.txt @@ -7,6 +7,9 @@ add_executable(Voxelotl MACOSX_BUNDLE shadertypes.h shader.metal + # Common library + Common/ConcurrentDictionary.swift + # Maths library Math/FloatExtensions.swift Math/IntegerExtensions.swift @@ -98,6 +101,7 @@ set_source_files_properties(test.png PROPERTIES MACOSX_PACKAGE_LOCATION Resource #TODO: should use TREE mode as documented in https://cmake.org/cmake/help/latest/command/source_group.html source_group("Resources" FILES Assets.xcassets test.png) +source_group("Source Files\\Common" REGULAR_EXPRESSION "Common/") source_group("Source Files" REGULAR_EXPRESSION "\\.(swift|metal)$") source_group("Source Files\\Random" REGULAR_EXPRESSION "Random/") source_group("Source Files\\Math" REGULAR_EXPRESSION "Math/") diff --git a/Sources/Voxelotl/Common/ConcurrentDictionary.swift b/Sources/Voxelotl/Common/ConcurrentDictionary.swift new file mode 100644 index 0000000..3a1907e --- /dev/null +++ b/Sources/Voxelotl/Common/ConcurrentDictionary.swift @@ -0,0 +1,73 @@ +import Foundation + +public class ConcurrentDictionary: Collection { + private var inner: [V : T] + private var lock: NSLock = .init() + + public var keys: Dictionary.Keys { + self.locked { + inner.keys + } + } + + public var values: Dictionary.Values { + self.locked { + self.inner.values + } + } + + public var startIndex: Dictionary.Index { + self.locked { + self.inner.startIndex + } + } + + public var endIndex: Dictionary.Index { + self.locked { + self.inner.endIndex + } + } + + public init(inner: [V:T]) { + self.inner = inner + } + + public convenience init() { + self.init(inner: [:]) + } + + public func index(after i: Dictionary.Index) -> Dictionary.Index { + self.locked { + self.inner.index(after: i) + } + } + + public subscript(key: V) -> T? { + set(newValue) { + self.locked { + self.inner[key] = newValue + } + } + + get { + self.locked { + self.inner[key] + } + } + } + + public subscript(index: Dictionary.Index) -> Dictionary.Element { + self.locked { + self.inner[index] + } + } + + fileprivate func locked(_ perform: () -> X) -> X { + self.lock.lock() + defer { + self.lock.unlock() + } + let value = perform() + return value + } +} diff --git a/Sources/Voxelotl/World.swift b/Sources/Voxelotl/World.swift index 11d81b4..4f95a43 100644 --- a/Sources/Voxelotl/World.swift +++ b/Sources/Voxelotl/World.swift @@ -63,8 +63,7 @@ public class World { self._generator.reset(seed: seed) let orig = SIMD3(width, height, depth) / 2 - var localChunks: [SIMD3: Chunk] = [:] - let localChunksLock = NSLock() + let localChunks = ConcurrentDictionary, Chunk>() let queue = OperationQueue() queue.qualityOfService = .userInitiated for z in 0.. Date: Mon, 2 Sep 2024 18:28:36 +1000 Subject: [PATCH 3/3] use chunkid typealias for clarity in concurrent worldgen --- Sources/Voxelotl/World.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Voxelotl/World.swift b/Sources/Voxelotl/World.swift index 4f95a43..b1517ff 100644 --- a/Sources/Voxelotl/World.swift +++ b/Sources/Voxelotl/World.swift @@ -63,7 +63,7 @@ public class World { self._generator.reset(seed: seed) let orig = SIMD3(width, height, depth) / 2 - let localChunks = ConcurrentDictionary, Chunk>() + let localChunks = ConcurrentDictionary() let queue = OperationQueue() queue.qualityOfService = .userInitiated for z in 0..