├── .github
└── workflows
│ └── swift.yml
├── .gitignore
├── .periphery.yml
├── .pre-commit-config.yaml
├── .swift-version
├── .swiftformat
├── .swiftlint.yml
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── xcshareddata
│ └── WorkspaceSettings.xcsettings
├── .vscode
└── settings.json
├── Demos
├── CLI
│ ├── CLI.swift
│ ├── Media.xcassets
│ │ ├── Contents.json
│ │ └── HD-Testcard-original.imageset
│ │ │ ├── Contents.json
│ │ │ └── HD-Testcard-original.jpg
│ └── OffscreenDemo.swift
├── Demos
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── appstore1024.png
│ │ │ ├── ipad152.png
│ │ │ ├── ipad76.png
│ │ │ ├── ipadNotification20.png
│ │ │ ├── ipadNotification40.png
│ │ │ ├── ipadPro167.png
│ │ │ ├── ipadSettings29.png
│ │ │ ├── ipadSettings58.png
│ │ │ ├── ipadSpotlight40.png
│ │ │ ├── ipadSpotlight80.png
│ │ │ ├── iphone120.png
│ │ │ ├── iphone180.png
│ │ │ ├── mac1024.png
│ │ │ ├── mac128.png
│ │ │ ├── mac16.png
│ │ │ ├── mac256.png
│ │ │ ├── mac32.png
│ │ │ ├── mac512.png
│ │ │ ├── mac64.png
│ │ │ ├── notification40.png
│ │ │ ├── notification60.png
│ │ │ ├── settings58.png
│ │ │ ├── settings87.png
│ │ │ ├── spotlight120.png
│ │ │ └── spotlight80.png
│ │ ├── BlueSkySkybox.textureset
│ │ │ ├── Contents.json
│ │ │ └── Universal.mipmapset
│ │ │ │ ├── BlueSkySkybox.png
│ │ │ │ └── Contents.json
│ │ ├── Checkerboard.textureset
│ │ │ ├── Contents.json
│ │ │ └── Universal.mipmapset
│ │ │ │ ├── Checkerboard.png
│ │ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── custom.camera.badge.arrow.down.rectangle.symbolset
│ │ │ ├── Contents.json
│ │ │ └── custom.camera.badge.arrow.down.rectangle.svg
│ │ └── seamless-foods-mixed-0020.textureset
│ │ │ ├── Contents.json
│ │ │ └── Universal.mipmapset
│ │ │ ├── Contents.json
│ │ │ └── seamless-foods-mixed-0020.jpeg
│ ├── CSGDemoView.swift
│ ├── ContentView-VisionOS.swift
│ ├── ContentView.swift
│ ├── Demos.entitlements
│ ├── DemosApp-VisionOS.swift
│ ├── DemosApp.swift
│ ├── Info-visionOS.plist
│ ├── Info.plist
│ ├── Interactive
│ │ ├── BallRotationModifier.swift
│ │ ├── FirstPersonInteractiveViewModifier.swift
│ │ ├── GameControllerWidget.swift
│ │ └── MovementController.swift
│ ├── MetalCanvas
│ │ └── MetalCanvas.swift
│ ├── Output
│ │ ├── perseverance_01.ktx
│ │ ├── perseverance_02.ktx
│ │ ├── perseverance_03.ktx
│ │ ├── perseverance_04.ktx
│ │ ├── perseverance_05.ktx
│ │ ├── perseverance_06.ktx
│ │ ├── perseverance_07.ktx
│ │ ├── perseverance_08.ktx
│ │ ├── perseverance_09.ktx
│ │ ├── perseverance_10.ktx
│ │ ├── perseverance_11.ktx
│ │ ├── perseverance_12.ktx
│ │ ├── perseverance_13.ktx
│ │ └── perseverance_14.ktx
│ ├── Particles
│ │ ├── Particles.swift
│ │ ├── ParticlesRenderJob.swift
│ │ └── SimulationView.swift
│ ├── Particles2
│ │ └── Particles2.swift
│ ├── PerseveranceTiles
│ │ ├── perseverance_01.ktx
│ │ ├── perseverance_02.ktx
│ │ ├── perseverance_03.ktx
│ │ ├── perseverance_04.ktx
│ │ ├── perseverance_05.ktx
│ │ ├── perseverance_06.ktx
│ │ ├── perseverance_07.ktx
│ │ ├── perseverance_08.ktx
│ │ ├── perseverance_09.ktx
│ │ ├── perseverance_10.ktx
│ │ ├── perseverance_11.ktx
│ │ ├── perseverance_12.ktx
│ │ ├── perseverance_13.ktx
│ │ └── perseverance_14.ktx
│ ├── PixelFormatsView.swift
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── Scratch.swift
│ ├── ScreenSpaceDemoView.swift
│ ├── ShaderToy
│ │ ├── ShaderToy.metal
│ │ └── ShaderToyView.swift
│ ├── SimpleScene
│ │ ├── BarramundiFish.glb
│ │ ├── Cube.glb
│ │ ├── FlatMaterial.swift
│ │ ├── GLTF+YAMesh.swift
│ │ ├── PanoramaRenderJob.swift
│ │ ├── SimpleJobsBasedRenderPass.swift
│ │ ├── SimpleMesh.swift
│ │ ├── SimpleScene+Demo.swift
│ │ ├── SimpleScene.swift
│ │ ├── SimpleSceneInspector.swift
│ │ ├── SimpleSceneMapView.swift
│ │ ├── SimpleSceneRenderPass.swift
│ │ ├── SimpleSceneView.swift
│ │ ├── TextureManager.swift
│ │ ├── UnlitMaterial.swift
│ │ └── YAMesh+Conversion.swift
│ ├── Support
│ │ ├── Bindings.swift
│ │ ├── Cache.swift
│ │ ├── Counters.swift
│ │ ├── DisplayLink.swift
│ │ ├── Gradient.swift
│ │ ├── Logging.swift
│ │ ├── Names.swift
│ │ ├── Projections.swift
│ │ ├── Resources.swift
│ │ ├── Support.swift
│ │ ├── SwiftUI+Support.swift
│ │ ├── TarArchive.swift
│ │ ├── adjectives.txt
│ │ └── nouns.txt
│ ├── Teapot.ply
│ ├── TestcardTiles
│ │ ├── Testcard_01.ktx
│ │ ├── Testcard_02.ktx
│ │ ├── Testcard_03.ktx
│ │ ├── Testcard_04.ktx
│ │ ├── Testcard_05.ktx
│ │ ├── Testcard_06.ktx
│ │ ├── Testcard_07.ktx
│ │ ├── Testcard_08.ktx
│ │ ├── Testcard_09.ktx
│ │ ├── Testcard_10.ktx
│ │ ├── Testcard_11.ktx
│ │ └── Testcard_12.ktx
│ ├── TextureView.swift
│ └── Volumetric
│ │ ├── StanfordVolumeData.tar
│ │ ├── VolumeData.swift
│ │ ├── VolumetricRenderPass.swift
│ │ └── VolumetricView.swift
├── Packages
│ ├── Compute
│ │ ├── .gitignore
│ │ ├── .swiftpm
│ │ │ └── xcode
│ │ │ │ └── package.xcworkspace
│ │ │ │ └── contents.xcworkspacedata
│ │ ├── Package.swift
│ │ └── Sources
│ │ │ ├── Compute
│ │ │ ├── Compute.swift
│ │ │ └── ShaderFunction.swift
│ │ │ └── ComputeTool
│ │ │ ├── BitonicSort.metal
│ │ │ ├── BitonicSort.swift
│ │ │ ├── ComputeTool.swift
│ │ │ ├── GameOfLife.metal
│ │ │ ├── GameOfLife.swift
│ │ │ ├── RandomFill.metal
│ │ │ ├── RandomFill.swift
│ │ │ └── Support.swift
│ └── DemosSupport
│ │ ├── .gitignore
│ │ ├── Package.swift
│ │ ├── Sources
│ │ └── DemosSupport
│ │ │ └── SpatialLookupTable.swift
│ │ └── Tests
│ │ └── DemosSupportTests
│ │ └── DemosSupportTests.swift
├── RenderKitDemos.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Demos.xcscheme
└── xcconfig
│ ├── CLI-Debug.xcconfig
│ ├── CLI-Release.xcconfig
│ ├── CLI-Shared.xcconfig
│ ├── Demos-Debug.xcconfig
│ ├── Demos-Release.xcconfig
│ ├── Demos-Shared.xcconfig
│ ├── Project-Debug.xcconfig
│ ├── Project-Release.xcconfig
│ └── Project-Shared.xcconfig
├── Documentation
├── Exported PNG image.png
├── Mesh.plantuml
├── Meshes.svg
├── Screenshot 2023-07-01 at 08.06.29.png
└── Screenshot 2023-09-19 at 19.51.00.png
├── LICENSE
├── MyPlayground.playground
├── Contents.swift
└── contents.xcplayground
├── Package.swift
├── README.md
├── Sources
├── RenderKit
│ ├── MetalView.swift
│ ├── OffscreenRenderPass.swift
│ ├── PackedFloat.swift
│ ├── RenderKitShaders.swift
│ ├── RenderPass.swift
│ ├── RendererView.swift
│ ├── SimpleVertex.swift
│ ├── Support
│ │ ├── Functions.swift
│ │ ├── LOLID2.swift
│ │ ├── Logging.swift
│ │ ├── Metal+Support.swift
│ │ ├── Scratch.swift
│ │ ├── Shape3D.swift
│ │ └── Support.swift
│ ├── VertexDescriptor.swift
│ ├── VisionOS
│ │ ├── Assets.xcassets
│ │ │ ├── ColorMap.textureset
│ │ │ │ ├── Contents.json
│ │ │ │ └── Universal.mipmapset
│ │ │ │ │ ├── ColorMap.png
│ │ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ └── Renderer.swift
│ └── YAMesh
│ │ └── YAMesh.swift
├── RenderKitScratch
│ ├── CSG.swift
│ ├── CircularPairs.swift
│ ├── HighContrastColors.swift
│ ├── PLYEncoder.swift
│ ├── Scratch.swift
│ ├── Shapes+CSG.swift
│ ├── Shapes+Intersections.swift
│ ├── Shapes+ShapeScript.swift
│ ├── Shapes.swift
│ └── WingedEdge.swift
└── RenderKitShaders
│ ├── Classic
│ ├── BlinnPhongShaders.metal
│ ├── CheckerBoardCompute.metal
│ ├── GPULifeKernel.metal
│ ├── ParticleShaders.metal
│ ├── ShaderToy.metal
│ ├── Support.metal
│ ├── Voxels.metal
│ └── include
│ │ ├── Bindings.h
│ │ ├── BlinnPhongShaders.h
│ │ ├── Common.h
│ │ ├── GLSLCompat.h
│ │ ├── ParticleShaders.h
│ │ ├── Random.h
│ │ ├── Shaders.h
│ │ ├── Support.h
│ │ └── Voxels.h
│ ├── Empty.c
│ ├── FlatShader.metal
│ ├── GridShader.metal
│ ├── MarchingCubes.metal
│ ├── Noise.metal
│ ├── PanoramicShaders.metal
│ ├── ParticlesShader.metal
│ ├── ReallyFlatShader.metal
│ ├── Scratch
│ ├── DemoBlit.metal
│ └── MeshShaders.metal
│ ├── UnlitShader.metal
│ ├── VolumeShaders.metal
│ ├── immersiveShaders.metal
│ ├── include
│ ├── CommonTypes.h
│ ├── FlatShader.h
│ ├── GLSLCompat.h
│ ├── GraphToy.h
│ ├── ImmersiveShaders.h
│ ├── MetalSupport.h
│ ├── PanoramicShaders.h
│ ├── Random.h
│ ├── RenderKitShaders.h
│ ├── UnlitShader.h
│ └── VolumeShaders.h
│ └── voronoiNoise.metal
├── TODO.md
└── Tests
└── RenderKitTests
├── CSGTests.swift
├── RenderKit3Tests.swift
├── ScratchTests.swift
├── VertexDescriptorTests.swift
└── WingedEdgeTests.swift
/.github/workflows/swift.yml:
--------------------------------------------------------------------------------
1 | name: Swift
2 | env:
3 | XCODE_SCHEME: "RenderKitDemos"
4 | XCODE_PROJECT_PATH: "./Demos"
5 | XCODE_VERSION: "latest-stable"
6 | on:
7 | push:
8 | pull_request:
9 | jobs:
10 | swift-build:
11 | runs-on: macos-15 # macos-latest
12 | steps:
13 | - uses: maxim-lobanov/setup-xcode@v1
14 | with:
15 | xcode-version: ${{ env.XCODE_VERSION }}
16 | - uses: actions/checkout@v3
17 | - run: swift build -v
18 | - run: swift test -v
19 | xcode-build:
20 | strategy:
21 | matrix:
22 | destination: ["platform=iOS Simulator,name=iPhone 16 Plus", "generic/platform=iOS", "generic/platform=macOS"]
23 | runs-on: macos-15 # macos-latest
24 | steps:
25 | - uses: maxim-lobanov/setup-xcode@v1
26 | with:
27 | xcode-version: ${{ env.XCODE_VERSION }}
28 | - uses: actions/checkout@v3
29 | - run: xcodebuild -scheme "${{ env.XCODE_SCHEME }}" -destination "${{ matrix.destination }}" build CODE_SIGNING_ALLOWED=NO
30 | working-directory: "${{ env.XCODE_PROJECT_PATH }}"
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/configuration/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 | Package.resolved
10 |
--------------------------------------------------------------------------------
/.periphery.yml:
--------------------------------------------------------------------------------
1 | retain_public: true
2 | targets:
3 | - RenderKit
4 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: local
3 | hooks:
4 | - id: swiftlint
5 | name: swiftlint lint
6 | entry: /opt/homebrew/bin/swiftlint lint --quiet
7 | language: system
8 | types: [swift]
9 | - id: swiftlint-fix
10 | name: swiftlint lint --fix
11 | entry: /opt/homebrew/bin/swiftlint lint --quiet --fix
12 | language: system
13 | types: [swift]
14 | - repo: https://github.com/pre-commit/pre-commit-hooks
15 | rev: v2.3.0
16 | hooks:
17 | - id: check-case-conflict
18 | - id: check-executables-have-shebangs
19 | - id: check-json
20 | - id: check-merge-conflict
21 | - id: check-symlinks
22 | - id: check-toml
23 | - id: check-xml
24 | - id: check-yaml
25 | - id: end-of-file-fixer
26 | - id: trailing-whitespace
27 | # - id: check-shebang-scripts-are-executable
28 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.7
2 |
--------------------------------------------------------------------------------
/.swiftformat:
--------------------------------------------------------------------------------
1 | --disable andOperator
2 | --disable emptyBraces
3 | --disable fileHeader
4 | --disable redundantParens
5 | --disable trailingClosures
6 | --enable isEmpty
7 |
8 | --elseposition next-line
9 | --ifdef indent
10 | --patternlet inline
11 | --stripunusedargs closure-only
12 | --closingparen balanced
13 | --wraparguments preserve
14 | --wrapcollections before-first
15 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/Demos/CLI/CLI.swift:
--------------------------------------------------------------------------------
1 | import RenderKit
2 | import Foundation
3 | import Metal
4 | import MetalKit
5 | import ModelIO
6 |
7 | @main
8 | struct Main {
9 | static func main() async throws {
10 | // try await OffscreenDemo.main()
11 |
12 | let device = MTLCreateSystemDefaultDevice()!
13 |
14 | let mesh = try Sphere().toYAMesh(allocator: MTKMeshBufferAllocator(device: device), device: device)
15 | print(mesh)
16 | }
17 | }
18 |
19 | extension YAMesh {
20 | func toMDLMesh() throws -> MDLMesh {
21 | let vertexBuffers = vertexBufferViews.map { bufferView in
22 | return MDLMeshBufferData(type: .vertex, data: Data(bufferView))
23 | }
24 | let vertexDescriptor = MDLVertexDescriptor(vertexDescriptor)
25 | let submeshes = submeshes.map { submesh in
26 | let indexBuffer = MDLMeshBufferData(type: .index, data: Data(submesh.indexBufferView))
27 | return MDLSubmesh(indexBuffer: indexBuffer, indexCount: 0, indexType: .invalid, geometryType: .lines, material: nil)
28 | }
29 | return MDLMesh(vertexBuffers: vertexBuffers, vertexCount: 0, descriptor: vertexDescriptor, submeshes: submeshes)
30 | }
31 | }
32 |
33 | extension YAMesh {
34 | func write(to url: URL) throws {
35 | let mdlMesh = try toMDLMesh()
36 | let asset = MDLAsset()
37 | asset.add(mdlMesh)
38 | try asset.export(to: url)
39 | }
40 | }
41 |
42 | extension Data {
43 | init(_ buffer: BufferView) {
44 | fatalError()
45 | }
46 | }
47 |
48 | extension MDLVertexDescriptor {
49 | convenience init(_ vertexDescriptor: VertexDescriptor) {
50 | fatalError()
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Demos/CLI/Media.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Demos/CLI/Media.xcassets/HD-Testcard-original.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "HD-Testcard-original.jpg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Demos/CLI/Media.xcassets/HD-Testcard-original.imageset/HD-Testcard-original.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/CLI/Media.xcassets/HD-Testcard-original.imageset/HD-Testcard-original.jpg
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/appstore1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/appstore1024.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipad152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipad152.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipad76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipad76.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadNotification20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadNotification20.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadNotification40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadNotification40.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadPro167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadPro167.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadSettings29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadSettings29.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadSettings58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadSettings58.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadSpotlight40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadSpotlight40.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadSpotlight80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/ipadSpotlight80.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/iphone120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/iphone120.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/iphone180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/iphone180.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac1024.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac128.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac16.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac256.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac32.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac512.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/mac64.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/notification40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/notification40.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/notification60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/notification60.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/settings58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/settings58.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/settings87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/settings87.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/spotlight120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/spotlight120.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/AppIcon.appiconset/spotlight80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/AppIcon.appiconset/spotlight80.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/BlueSkySkybox.textureset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "textures" : [
7 | {
8 | "filename" : "Universal.mipmapset",
9 | "idiom" : "universal"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/BlueSkySkybox.textureset/Universal.mipmapset/BlueSkySkybox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/BlueSkySkybox.textureset/Universal.mipmapset/BlueSkySkybox.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/BlueSkySkybox.textureset/Universal.mipmapset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "levels" : [
7 | {
8 | "filename" : "BlueSkySkybox.png",
9 | "mipmap-level" : "base"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/Checkerboard.textureset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "textures" : [
7 | {
8 | "filename" : "Universal.mipmapset",
9 | "idiom" : "universal"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/Checkerboard.textureset/Universal.mipmapset/Checkerboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/Checkerboard.textureset/Universal.mipmapset/Checkerboard.png
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/Checkerboard.textureset/Universal.mipmapset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "levels" : [
7 | {
8 | "filename" : "Checkerboard.png",
9 | "mipmap-level" : "base"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/custom.camera.badge.arrow.down.rectangle.symbolset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "symbols" : [
7 | {
8 | "filename" : "custom.camera.badge.arrow.down.rectangle.svg",
9 | "idiom" : "universal"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/seamless-foods-mixed-0020.textureset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "textures" : [
7 | {
8 | "filename" : "Universal.mipmapset",
9 | "idiom" : "universal"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/seamless-foods-mixed-0020.textureset/Universal.mipmapset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "levels" : [
7 | {
8 | "filename" : "seamless-foods-mixed-0020.jpeg",
9 | "mipmap-level" : "base"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/Demos/Demos/Assets.xcassets/seamless-foods-mixed-0020.textureset/Universal.mipmapset/seamless-foods-mixed-0020.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Assets.xcassets/seamless-foods-mixed-0020.textureset/Universal.mipmapset/seamless-foods-mixed-0020.jpeg
--------------------------------------------------------------------------------
/Demos/Demos/ContentView-VisionOS.swift:
--------------------------------------------------------------------------------
1 | #if os(visionOS)
2 | import SwiftUI
3 | import RenderKit
4 |
5 | struct ContentView: View {
6 | @State private var showImmersiveSpace = false
7 | @State private var immersiveSpaceIsShown = false
8 |
9 | @Environment(\.openImmersiveSpace) var openImmersiveSpace
10 | @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
11 |
12 | var body: some View {
13 | VStack {
14 | VStack {
15 | Toggle("Show/Hide Immersive Space", isOn: $showImmersiveSpace)
16 | .toggleStyle(.button)
17 | }.padding().glassBackgroundEffect()
18 | }
19 | .onChange(of: showImmersiveSpace) { _, newValue in
20 | Task {
21 | if newValue {
22 | switch await openImmersiveSpace(id: "ImmersiveSpace") {
23 | case .opened:
24 | immersiveSpaceIsShown = true
25 | case .error, .userCancelled:
26 | fallthrough
27 | @unknown default:
28 | immersiveSpaceIsShown = false
29 | showImmersiveSpace = false
30 | }
31 | } else if immersiveSpaceIsShown {
32 | await dismissImmersiveSpace()
33 | immersiveSpaceIsShown = false
34 | }
35 | }
36 | }
37 | }
38 | }
39 | #endif
40 |
--------------------------------------------------------------------------------
/Demos/Demos/ContentView.swift:
--------------------------------------------------------------------------------
1 | #if !os(visionOS)
2 | import SwiftUI
3 | import RenderKit
4 | import Everything
5 |
6 | struct ContentView: View {
7 | enum Demo: String, CaseIterable, Hashable {
8 | case simpleScene = "Simple Scene"
9 | case simpleSceneExtended = "Simple Scene (Extended)"
10 | case volumetric = "Volumetric"
11 | // case gltf = "GLTF"
12 | case simulationSUI
13 | // case screenSpaceDemoView = "ScreenSpaceDemoView"
14 | case particles2
15 | case pixelFormats
16 | case csgDemoView
17 | }
18 |
19 | @State
20 | var demo: Demo = .simpleScene
21 |
22 | var body: some View {
23 | NavigationStack {
24 | Group {
25 | List {
26 | ForEach(Demo.allCases, id: \.self) { demo in
27 | NavigationLink(value: demo) {
28 | Text(verbatim: demo.rawValue)
29 | }
30 | }
31 | }
32 | #if os(macOS)
33 | .frame(maxWidth: 320)
34 | .cornerRadius(8)
35 | .padding()
36 | .frame(maxWidth: .infinity)
37 | .background(.ultraThickMaterial)
38 | #endif
39 | }
40 | .navigationTitle("RenderKit")
41 | .navigationDestination(for: Demo.self) { demo in
42 | switch demo {
43 | case .simpleScene:
44 | let device = MTLCreateSystemDefaultDevice()!
45 | let scene = try! SimpleScene.demo(device: device)
46 | CoreSimpleSceneView(scene: .constant(scene))
47 | .navigationTitle(demo.rawValue)
48 | case .simpleSceneExtended:
49 | SimpleSceneView()
50 | .navigationTitle(demo.rawValue)
51 | case .volumetric:
52 | VolumetricView()
53 | .navigationTitle(demo.rawValue)
54 | // case .gltf:
55 | // GLTFView()
56 | // .navigationTitle(demo.rawValue)
57 | case .simulationSUI:
58 | SimulationView()
59 | // case .screenSpaceDemoView:
60 | // ScreenSpaceDemoView()
61 | case .particles2:
62 | Particles2View()
63 | case .pixelFormats:
64 | PixelFormatsView()
65 | case .csgDemoView:
66 | CSGDemoView()
67 | }
68 | }
69 | }
70 | .metalDevice(MTLCreateSystemDefaultDevice()!)
71 | }
72 | }
73 |
74 | #Preview {
75 | ContentView()
76 | }
77 | #endif
78 |
--------------------------------------------------------------------------------
/Demos/Demos/Demos.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.device.bluetooth
8 |
9 | com.apple.security.device.usb
10 |
11 | com.apple.security.files.user-selected.read-write
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Demos/Demos/DemosApp-VisionOS.swift:
--------------------------------------------------------------------------------
1 | #if os(visionOS)
2 | import SwiftUI
3 | import CompositorServices
4 | import RenderKit
5 |
6 | /*
7 |
8 |
9 |
10 |
11 | UISceneConfigurations
12 |
13 | UIApplicationSupportsMultipleScenes
14 |
15 | UIApplicationPreferredDefaultSceneSessionRole
16 | UIWindowSceneSessionRoleVolumetricApplication
17 |
18 |
19 | */
20 |
21 | @main
22 | struct DemosApp: App {
23 | var body: some Scene {
24 | WindowGroup {
25 | ContentView()
26 | }.windowStyle(.volumetric)
27 | ImmersiveSpace(id: "ImmersiveSpace") {
28 | CompositorLayer(configuration: ContentStageConfiguration()) { layerRenderer in
29 | try! Renderer(layerRenderer).startRenderLoop()
30 | }
31 | }
32 | .immersionStyle(selection: .constant(.full), in: .full)
33 | }
34 | }
35 | #endif
36 |
--------------------------------------------------------------------------------
/Demos/Demos/Info-visionOS.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UISceneConfigurations
8 |
9 | UIApplicationSupportsMultipleScenes
10 |
11 | UIApplicationPreferredDefaultSceneSessionRole
12 | UIWindowSceneSessionRoleVolumetricApplication
13 |
14 | GCSupportedGameControllers
15 |
16 |
17 | ProfileName
18 | ExtendedGamepad
19 |
20 |
21 | ProfileName
22 | MicroGamepad
23 |
24 |
25 | ProfileName
26 | DirectionalGamepad
27 |
28 |
29 | GCSupportsControllerUserInteraction
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Demos/Demos/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GCSupportedGameControllers
6 |
7 |
8 | ProfileName
9 | ExtendedGamepad
10 |
11 |
12 | ProfileName
13 | MicroGamepad
14 |
15 |
16 | ProfileName
17 | DirectionalGamepad
18 |
19 |
20 | GCSupportsControllerUserInteraction
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Demos/Demos/Interactive/FirstPersonInteractiveViewModifier.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SIMDSupport
3 | import GameController
4 | import simd
5 |
6 | public struct FirstPersonInteractiveViewModifier: ViewModifier, @unchecked Sendable {
7 | @Environment(\.displayLink)
8 | var displayLink
9 |
10 | @State
11 | var movementController: MovementController?
12 |
13 | @FocusState
14 | var renderViewFocused: Bool
15 |
16 | @State
17 | var movementConsumerTask: Task<(), Never>?
18 |
19 | @Binding
20 | var camera: Camera
21 |
22 | public func body(content: Content) -> some View {
23 | content
24 | .task() {
25 | guard let displayLink else {
26 | return
27 | }
28 | let movementController = MovementController(displayLink: displayLink)
29 | movementController.focused = renderViewFocused
30 | self.movementController = movementController
31 | #if os(macOS)
32 | movementController.disableUIKeys()
33 | #endif
34 | movementConsumerTask = Task.detached { [movementController] in
35 | for await event in movementController.events() {
36 | Counters.shared.increment(counter: "Consumption")
37 | switch event.payload {
38 | case .movement(let movement):
39 | let target = camera.target
40 | let angle = atan2(target.z, target.x) - .pi / 2
41 | let rotation = simd_quaternion(angle, [0, -1, 0])
42 | Task {
43 | await MainActor.run {
44 | camera.transform.translation += simd_act(rotation, movement * 0.1)
45 | }
46 | }
47 | case .rotation(let rotation):
48 | Task {
49 | await MainActor.run {
50 | camera.heading.degrees += Double(rotation * 2)
51 | }
52 | }
53 | }
54 | }
55 | }
56 | }
57 | .onChange(of: renderViewFocused) {
58 | movementController?.focused = renderViewFocused
59 | }
60 | .onDisappear {
61 | movementConsumerTask?.cancel()
62 | movementConsumerTask = nil
63 | }
64 | .focusable(interactions: .automatic)
65 | .focused($renderViewFocused)
66 | .focusEffectDisabled()
67 | .defaultFocus($renderViewFocused, true)
68 | .onKeyPress(.escape, action: {
69 | renderViewFocused = false
70 | return .handled
71 | })
72 | .overlay(alignment: .topTrailing) {
73 | Group {
74 | Group {
75 | if renderViewFocused {
76 | //Image(systemName: "dot.square.fill").foregroundStyle(.selection)
77 | Text("Focused")
78 | }
79 | else {
80 | Text("Unfocused")
81 | //Image(systemName: "dot.square").foregroundStyle(.selection)
82 | }
83 | }
84 | .padding()
85 | .background(.regularMaterial)
86 | }
87 | .padding()
88 | }
89 | .overlay(alignment: .bottomLeading) {
90 | GameControllerWidget()
91 | .padding()
92 | }
93 | .overlay(alignment: .bottomLeading) {
94 | GameControllerWidget()
95 | .padding()
96 | }
97 | }
98 | }
99 |
100 | public extension View {
101 | func firstPersonInteractive(camera: Binding) -> some View {
102 | modifier(FirstPersonInteractiveViewModifier(camera: camera))
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/Demos/Demos/MetalCanvas/MetalCanvas.swift:
--------------------------------------------------------------------------------
1 | //import CoreGraphics
2 | //import RenderKit
3 | //import Metal
4 | //import SwiftUI
5 | //
6 | //struct MetalGraphicsContext {
7 | // enum Command {
8 | // case stroke(Path)
9 | // }
10 | //
11 | // var commands: [Command] = []
12 | //
13 | // mutating func stroke(path: Path) {
14 | // commands.append(.stroke(path))
15 | // }
16 | //}
17 | //
18 | //struct MetalGraphicsCanvas {
19 | // typealias Configuration = OffscreenRenderPassConfiguration
20 | //
21 | // var size: CGSize
22 | //
23 | // var renderPass: any RenderPass
24 | // var configuration: Configuration
25 | //
26 | // init(size: CGSize) throws {
27 | // self.size = size
28 | // let device = MTLCreateSystemDefaultDevice()!
29 | // configuration = OffscreenRenderPassConfiguration(device: device, size: size)
30 | // configuration.colorPixelFormat = .bgra10_xr_srgb
31 | // configuration.update()
32 | // renderPass = AnyRenderPass { _, _ in
33 | // fatalError()
34 | // }
35 | // drawableSizeWillChange: { _, _, _ in
36 | // fatalError()
37 | // }
38 | // draw: { _, _, _, _, _ in
39 | // fatalError()
40 | // }
41 | // }
42 | //
43 | // func draw(_ block: (inout MetalGraphicsContext) throws -> R) rethrows -> R {
44 | // var context = MetalGraphicsContext()
45 | // let result = try block(&context)
46 | // try! render(context.commands) // TODO: try!
47 | // return result
48 | // }
49 | //
50 | // func render(_ context: [MetalGraphicsContext.Command]) throws {
51 | // fatalError()
52 | // }
53 | //}
54 |
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_01.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_01.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_02.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_02.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_03.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_03.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_04.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_04.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_05.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_05.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_06.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_06.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_07.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_07.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_08.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_08.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_09.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_09.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_10.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_10.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_11.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_11.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_12.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_12.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_13.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_13.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Output/perseverance_14.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Output/perseverance_14.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_01.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_01.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_02.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_02.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_03.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_03.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_04.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_04.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_05.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_05.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_06.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_06.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_07.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_07.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_08.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_08.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_09.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_09.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_10.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_10.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_11.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_11.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_12.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_12.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_13.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_13.ktx
--------------------------------------------------------------------------------
/Demos/Demos/PerseveranceTiles/perseverance_14.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/PerseveranceTiles/perseverance_14.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Demos/Demos/Scratch.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Scratch.swift
3 | // Demos
4 | //
5 | // Created by Jonathan Wight on 9/17/23.
6 | //
7 |
8 | import Foundation
9 | import Metal
10 |
11 | struct VolumeData {
12 | var directoryURL: URL
13 | var size: MTLSize
14 |
15 | func load() throws -> (MTLDevice) throws -> MTLTexture {
16 | let slices = try FileManager().contentsOfDirectory(atPath: directoryURL.path)
17 | .map { directoryURL.appendingPathComponent($0) }
18 | .sorted { lhs, rhs in
19 | let lhs = Int(lhs.pathExtension)!
20 | let rhs = Int(rhs.pathExtension)!
21 | return lhs < rhs
22 | }
23 | .map {
24 | let data = try Data(contentsOf: $0)
25 | return data.withUnsafeBytes { buffer in
26 | buffer.bindMemory(to: UInt16.self).map {
27 | UInt16(bigEndian: $0)
28 | }
29 | }
30 | }
31 |
32 | return { device in
33 | let textureDescriptor = MTLTextureDescriptor()
34 | textureDescriptor.textureType = .type3D
35 | textureDescriptor.pixelFormat = .r16Uint
36 | textureDescriptor.width = size.width
37 | textureDescriptor.height = size.height
38 | textureDescriptor.depth = size.depth
39 |
40 | guard let texture = device.makeTexture(descriptor: textureDescriptor) else {
41 | fatalError()
42 | }
43 | texture.label = directoryURL.lastPathComponent
44 |
45 | for slice in slices {
46 | let region = MTLRegionMake3D(0, 0, 0, size.width, size.height, 1)
47 | texture.replace(region: region, mipmapLevel: 0, slice: 0, withBytes: slice, bytesPerRow: size.width * 2, bytesPerImage: size.width * size.height * 2)
48 | }
49 |
50 | return texture
51 | }
52 | }
53 | }
54 |
55 | //Description: CT study of a cadaver head
56 | //Dimensions: 113 slices of 256 x 256 pixels,
57 | // voxel grid is rectangular, and
58 | // X:Y:Z shape of each voxel is 1:1:2
59 | //Files: 113 binary files, one file per slice
60 | //File format: 16-bit integers (Mac byte ordering), file contains no header
61 | //Data source: acquired on a General Electric CT Scanner and provided
62 | // courtesy of North Carolina Memorial Hospital
63 |
--------------------------------------------------------------------------------
/Demos/Demos/ShaderToy/ShaderToy.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | using namespace metal;
5 |
6 | struct ShaderToyVertex {
7 | vector_float3 position [[attribute(0)]];
8 | vector_float3 normal [[attribute(1)]];
9 | vector_float2 textureCoords [[attribute(2)]];
10 | };
11 |
12 | struct ShaderToyVertexUniforms {
13 | simd_float3x4 transform;
14 | };
15 |
16 | struct ShaderToyFragmentUniforms {
17 | float time;
18 | vector_float2 scale;
19 | };
20 |
21 | struct Fragment {
22 | vector_float4 position [[position]];
23 | };
24 |
25 | // MARK: -
26 |
27 | enum ShaderToyID {
28 | kShaderToyID_Vertices = 0,
29 | kShaderToyID_VertexShaderUniforms = 1,
30 | kShaderToyID_FragmentShaderUniforms = 2,
31 | kShaderToyID_PixelateFunctionConstant = 0,
32 | };
33 |
34 | // MARK: -
35 |
36 | constant bool pixelate [[function_constant(kShaderToyID_PixelateFunctionConstant)]];
37 |
38 | [[vertex]]
39 | Fragment shaderToyVertexShader(ShaderToyVertex vertexIn [[stage_in]], constant ShaderToyVertexUniforms &uniforms [[buffer(kShaderToyID_VertexShaderUniforms)]])
40 | {
41 | return {
42 | .position = vector_float4(vector_float4(vertexIn.position, 1.0) * uniforms.transform, 1.0)
43 | };
44 | }
45 |
46 | [[fragment]]
47 | vector_float4 shaderToyFragmentShader(Fragment fragmentIn [[stage_in]], constant ShaderToyFragmentUniforms &uniforms [[buffer(kShaderToyID_FragmentShaderUniforms)]])
48 | {
49 | vector_float2 vert = fragmentIn.position.xy * uniforms.scale;
50 | if (pixelate) {
51 | vert = floor(vert);
52 | }
53 | const auto step = vector_float2(sin(uniforms.time), cos(uniforms.time)) * 20.0;
54 | const auto scale = ((sin(uniforms.time / 60.0) + 1.0) / 5.0) + 0.2;
55 | const auto r = sin((vert.x + step.x) * scale) + cos((vert.y + step.x) * scale);
56 | const auto g = sin((vert.x + step.x) * scale) + cos((vert.y + step.y) * scale);
57 | const auto b = sin((vert.x + step.y) * scale) + cos((vert.y + step.y) * scale);
58 | return vector_float4(r, g, b, 1.0);
59 | }
60 |
--------------------------------------------------------------------------------
/Demos/Demos/SimpleScene/BarramundiFish.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/SimpleScene/BarramundiFish.glb
--------------------------------------------------------------------------------
/Demos/Demos/SimpleScene/Cube.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/SimpleScene/Cube.glb
--------------------------------------------------------------------------------
/Demos/Demos/SimpleScene/PanoramaRenderJob.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import ModelIO
3 | import Metal
4 | import MetalKit
5 | import SIMDSupport
6 | import RenderKitShaders
7 | import RenderKit
8 | import Observation
9 | import Everything
10 |
11 | class PanoramaRenderJob: SceneRenderJob {
12 | var renderPipelineState: MTLRenderPipelineState?
13 | var depthStencilState: MTLDepthStencilState?
14 | var mesh: YAMesh?
15 |
16 | var scene: SimpleScene
17 | var panorama: Panorama
18 | var textureManager: TextureManager
19 | var textures: [MTLTexture] = []
20 |
21 | init(scene: SimpleScene, textureManager: TextureManager, panorama: Panorama) {
22 | self.scene = scene
23 | self.textureManager = textureManager
24 | self.panorama = panorama
25 | }
26 |
27 | func setup(device: MTLDevice, configuration: inout Configuration) throws {
28 | let library = try! device.makeDefaultLibrary(bundle: .shadersBundle)
29 | let vertexFunction = library.makeFunction(name: "panoramicVertexShader")!
30 | let constantValues = MTLFunctionConstantValues()
31 | let fragmentFunction = try library.makeFunction(name: "panoramicFragmentShader", constantValues: constantValues)
32 | depthStencilState = device.makeDepthStencilState(descriptor: MTLDepthStencilDescriptor.always())
33 | let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
34 | renderPipelineDescriptor.vertexFunction = vertexFunction
35 | renderPipelineDescriptor.fragmentFunction = fragmentFunction
36 | renderPipelineDescriptor.colorAttachments[0].pixelFormat = configuration.colorPixelFormat
37 | renderPipelineDescriptor.depthAttachmentPixelFormat = configuration.depthStencilPixelFormat
38 | let descriptor = VertexDescriptor.packed(semantics: [.position, .normal, .textureCoordinate])
39 | renderPipelineDescriptor.vertexDescriptor = MTLVertexDescriptor(descriptor)
40 | renderPipelineState = try device.makeRenderPipelineState(descriptor: renderPipelineDescriptor)
41 | mesh = try! panorama.mesh(device)
42 | let loader = MTKTextureLoader(device: device)
43 | textures = try panorama.tileTextures.map { try $0(loader) }
44 | }
45 |
46 | func encode(on encoder: MTLRenderCommandEncoder, size: CGSize) throws {
47 | guard let renderPipelineState, let depthStencilState else {
48 | return
49 | }
50 | encoder.withDebugGroup("Panorama") {
51 | guard let mesh else {
52 | fatalError()
53 | }
54 | encoder.setRenderPipelineState(renderPipelineState)
55 | encoder.setDepthStencilState(depthStencilState)
56 | encoder.setVertexBuffers(mesh)
57 | let cameraUniforms = CameraUniforms(projectionMatrix: scene.camera.projection.matrix(viewSize: SIMD2(size)))
58 | let inverseCameraMatrix = scene.camera.transform.matrix.inverse
59 | encoder.setVertexBytes(of: cameraUniforms, index: 1)
60 | let modelViewMatrix = inverseCameraMatrix * float4x4.translation(scene.camera.transform.translation)
61 | encoder.setVertexBytes(of: modelViewMatrix, index: 2)
62 | let uniforms = PanoramaFragmentUniforms(gridSize: panorama.tilesSize, colorFactor: [1, 1, 1, 1])
63 | encoder.setFragmentBytes(of: uniforms, index: 0)
64 | encoder.setFragmentTextures(textures, range: 0..(device: MTLDevice, configuration: inout Configuration) throws {
12 | try jobs.forEach { job in
13 | try job.setup(device: device, configuration: &configuration)
14 | }
15 | }
16 |
17 | public func drawableSizeWillChange(device: MTLDevice, size: CGSize) throws {
18 | try jobs.forEach { job in
19 | try job.drawableSizeWillChange(device: device, size: size)
20 | }
21 | }
22 |
23 | public func draw(device: MTLDevice, size: CGSize, renderPassDescriptor: MTLRenderPassDescriptor, commandBuffer: MTLCommandBuffer) throws {
24 | try commandBuffer.withRenderCommandEncoder(descriptor: renderPassDescriptor) { encoder in
25 | encoder.label = "SimpleJobsBasedRenderPass-RenderCommandEncoder"
26 | try jobs.forEach { job in
27 | try job.encode(on: encoder, size: size)
28 | }
29 | }
30 | }
31 | }
32 |
33 | extension SimpleJobsBasedRenderPass: Observable {
34 | }
35 |
--------------------------------------------------------------------------------
/Demos/Demos/SimpleScene/SimpleMesh.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import ModelIO
3 | import Metal
4 | import MetalKit
5 | import SIMDSupport
6 | import RenderKitShaders
7 | import Everything
8 | import MetalSupport
9 | import os
10 | import RenderKit
11 | import Shapes2D
12 |
13 | protocol MTLBufferProviding {
14 | var buffer: MTLBuffer { get }
15 | }
16 |
17 | extension YAMesh {
18 | static func triangle(label: String? = nil, triangle: Triangle, transform: simd_float3x2 = simd_float3x2([1, 0], [0, 1], [0, 0]), device: MTLDevice, textureCoordinate: (CGPoint) -> SIMD2) throws -> YAMesh {
19 | let vertices = [
20 | triangle.vertices.0,
21 | triangle.vertices.1,
22 | triangle.vertices.2,
23 | ]
24 | .map {
25 | // TODO; Normal not impacted by transform. It should be.
26 | SimpleVertex(position: SIMD2($0) * transform, normal: [0, 0, 1], textureCoordinate: textureCoordinate($0))
27 | }
28 | return try YAMesh.simpleMesh(label: label, indices: [0, 1, 2], vertices: vertices, device: device)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Demos/Demos/SimpleScene/SimpleScene+Demo.swift:
--------------------------------------------------------------------------------
1 | import Metal
2 | import RenderKit
3 | import MetalKit
4 | import Algorithms
5 |
6 | public extension SimpleScene {
7 | static func demo(device: MTLDevice) throws -> SimpleScene {
8 | let allocator = MTKMeshBufferAllocator(device: device)
9 | let cone = try Cone(extent: [0.5, 1, 0.5], segments: [20, 10]).toYAMesh(allocator: allocator, device: device)
10 | let sphere = try Sphere(extent: [0.5, 0.5, 0.5], segments: [20, 10]).toYAMesh(allocator: allocator, device: device)
11 | let capsule = try Capsule(extent: [0.25, 1, 0.25], cylinderSegments: [30, 10], hemisphereSegments: 5).toYAMesh(allocator: allocator, device: device)
12 |
13 | let meshes = [cone, sphere, capsule]
14 |
15 | let xRange = [Float](stride(from: -2, through: 2, by: 1))
16 | let zRange = [Float](stride(from: 0, through: -10, by: -1))
17 |
18 | let tilesSize: SIMD2
19 | let tileTextures: [(MTKTextureLoader) throws -> MTLTexture]
20 | if false {
21 | tilesSize = [6, 2]
22 | tileTextures = (1 ... 12).map { index in
23 | BundleResourceReference(bundle: .main, name: "perseverance_\(index.formatted(.number.precision(.integerLength(2))))", extension: "ktx")
24 | //ResourceReference.bundle(.main, name: "Testcard_\(index.formatted(.number.precision(.integerLength(2))))", extension: "ktx")
25 | }
26 | .map { resource -> ((MTKTextureLoader) throws -> MTLTexture) in
27 | return { loader in
28 | try loader.newTexture(resource: resource, options: [.textureStorageMode: MTLStorageMode.private.rawValue])
29 | }
30 | }
31 | }
32 | else {
33 | tilesSize = [1, 1]
34 | tileTextures = [ { loader in
35 | try loader.newTexture(name: "BlueSkySkybox", scaleFactor: 1, bundle: .main, options: [
36 | .textureStorageMode: MTLStorageMode.private.rawValue,
37 | .SRGB: true,
38 | ])
39 | }
40 | ]
41 | }
42 |
43 | var models: [Model] = []
44 | models += product(xRange, zRange).map { x, z in
45 | let hsv: SIMD3 = [Float.random(in: 0...1), 1, 1]
46 | let rgba = SIMD4(hsv.hsv2rgb(), 1.0)
47 | let material = FlatMaterial(baseColorFactor: rgba, baseColorTexture: .init(resource: BundleResourceReference(bundle: .main, name: "Checkerboard")))
48 | return Model(transform: .translation([x, 0, z]), material: material, mesh: meshes.randomElement()!)
49 | }
50 |
51 | let fishModel = Model(
52 | transform: .translation([0, 1, 0]).rotated(angle: .degrees(90), axis: [0, 1, 0]),
53 | material: UnlitMaterial(baseColorFactor: [1, 0, 1, 1], baseColorTexture: .init(resource: BundleResourceReference(bundle: .main, name: "seamless-foods-mixed-0020"))),
54 | mesh: try YAMesh(gltf: "BarramundiFish", device: device)
55 | )
56 | models.append(fishModel)
57 |
58 | let panorama = Panorama(tilesSize: tilesSize, tileTextures: tileTextures) { device in
59 | try Sphere(extent: [95, 95, 95], inwardNormals: true).toYAMesh(allocator: MTKMeshBufferAllocator(device: device), device: device)
60 | }
61 |
62 | let scene = SimpleScene(
63 | camera: Camera(transform: .translation([0, 0, 2]), target: [0, 0, -1], projection: .perspective(.init(fovy: .degrees(90), zClip: 0.1 ... 100))),
64 | light: .init(position: .translation([-2, 2, -1]), color: [1, 1, 1], power: 1),
65 | ambientLightColor: [0, 0, 0],
66 | models: models,
67 | panorama: panorama
68 | )
69 |
70 | return scene
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Demos/Demos/SimpleScene/SimpleSceneMapView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import simd
3 | import CoreGraphicsSupport
4 | import SIMDSupport
5 |
6 | struct SimpleSceneMapView: View {
7 | @Binding
8 | var scene: SimpleScene
9 |
10 | let scale: CGFloat = 10
11 |
12 | var body: some View {
13 | Canvas(opaque: true) { context, size in
14 | context.concatenate(CGAffineTransform.translation(CGPoint(size.width / 2, size.height / 2)))
15 | for model in scene.models {
16 | let position = CGPoint(model.transform.translation.xz)
17 | let colorVector = (model.material as? FlatMaterial)?.baseColorFactor ?? [1, 0, 0, 1]
18 | let color = Color(red: Double(colorVector.r), green: Double(colorVector.g), blue: Double(colorVector.b))
19 | context.fill(Path(ellipseIn: CGRect(center: position * scale, radius: 0.5 * scale)), with: .color(color.opacity(0.5)))
20 | }
21 | let cameraPosition = CGPoint(scene.camera.transform.translation.xz)
22 |
23 | if case let .perspective(perspective) = scene.camera.projection {
24 | // TODO: This is showing fovY but it should be fovX
25 | let viewCone = Path.arc(center: cameraPosition * scale, radius: 4 * scale, midAngle: .radians(Double(scene.camera.heading.radians)), width: .radians(Double(perspective.fovy.radians)))
26 | // context.fill(viewCone, with: .radialGradient(Gradient(colors: [.white.opacity(0.5), .white.opacity(0.0)]), center: cameraPosition * scale, startRadius: 0, endRadius: 4 * scale))
27 | context.stroke(viewCone, with: .color(.white))
28 | }
29 |
30 | var cameraImage = context.resolve(Image(systemName: "camera.circle.fill"))
31 | cameraImage.shading = .color(.mint)
32 | context.draw(cameraImage, at: cameraPosition * scale, anchor: .center)
33 |
34 | let lightPosition = CGPoint(scene.light.position.translation.xz)
35 | var lightImage = context.resolve(Image(systemName: "lightbulb.fill"))
36 | lightImage.shading = .color(.yellow)
37 | context.draw(lightImage, at: lightPosition * scale, anchor: .center)
38 |
39 | let targetPosition = cameraPosition + CGPoint(scene.camera.target.xz)
40 | var targetImage = context.resolve(Image(systemName: "scope"))
41 | targetImage.shading = .color(.white)
42 | context.draw(targetImage, at: targetPosition * scale, anchor: .center)
43 | }
44 | .background(.black)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Demos/Demos/SimpleScene/SimpleSceneRenderPass.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import ModelIO
3 | import Metal
4 | import MetalKit
5 | import SIMDSupport
6 | import RenderKitShaders
7 | import RenderKit
8 | import Observation
9 | import Everything
10 |
11 | protocol SceneRenderJob: RenderJob {
12 | var scene: SimpleScene { get set }
13 | var textureManager: TextureManager { get set }
14 | }
15 |
16 | class SimpleSceneRenderPass: RenderPass {
17 | var scene: SimpleScene {
18 | didSet {
19 | renderJobs.forEach { job in
20 | job.scene = scene
21 | }
22 | }
23 | }
24 | var renderJobs: [any RenderJob & SceneRenderJob] = []
25 |
26 | var textureManager: TextureManager?
27 |
28 | init(scene: SimpleScene) {
29 | self.scene = scene
30 | }
31 |
32 | func setup(device: MTLDevice, configuration: inout Configuration) throws {
33 | let textureManager = TextureManager(device: device)
34 | self.textureManager = textureManager
35 |
36 | if let panorama = scene.panorama {
37 | let job = PanoramaRenderJob(scene: scene, textureManager: textureManager, panorama: panorama)
38 | job.scene = scene
39 | self.renderJobs.append(job)
40 | }
41 |
42 | let flatModels = scene.models.filter { ($0.material as? FlatMaterial) != nil }
43 | if !flatModels.isEmpty {
44 | let job = FlatMaterialRenderJob(scene: scene, textureManager: textureManager, models: flatModels)
45 | self.renderJobs.append(job)
46 | }
47 |
48 | let unlitModels = scene.models.filter { ($0.material as? UnlitMaterial) != nil }
49 | if !unlitModels.isEmpty {
50 | let job = UnlitMaterialRenderJob(scene: scene, textureManager: textureManager, models: unlitModels)
51 | self.renderJobs.append(job)
52 | }
53 |
54 | try self.renderJobs.forEach { job in
55 | try job.setup(device: device, configuration: &configuration)
56 | }
57 | }
58 |
59 | func draw(device: MTLDevice, size: CGSize, renderPassDescriptor: MTLRenderPassDescriptor, commandBuffer: MTLCommandBuffer) throws {
60 | try commandBuffer.withRenderCommandEncoder(descriptor: renderPassDescriptor) { encoder in
61 | encoder.label = "SimpleJobsBasedRenderPass-RenderCommandEncoder"
62 | try renderJobs.forEach { job in
63 | try job.encode(on: encoder, size: size)
64 | }
65 | }
66 | }
67 | }
68 |
69 | // MARK: -
70 |
--------------------------------------------------------------------------------
/Demos/Demos/SimpleScene/YAMesh+Conversion.swift:
--------------------------------------------------------------------------------
1 | import RenderKit
2 |
3 | extension YAMesh {
4 | func convert(to other: VertexDescriptor) throws -> YAMesh {
5 | if vertexDescriptor == other {
6 | return self
7 | }
8 |
9 | print(vertexDescriptor)
10 | print(other)
11 |
12 | fatalError()
13 | }
14 |
15 | func pack(to other: VertexDescriptor) throws -> YAMesh {
16 | if vertexDescriptor == other {
17 | return self
18 | }
19 |
20 | print(vertexDescriptor)
21 | print(other)
22 |
23 | fatalError()
24 | }
25 | }
26 |
27 | extension VertexDescriptor {
28 | var isPacked: Bool {
29 | return layouts.count == 1
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Demos/Demos/Support/Bindings.swift:
--------------------------------------------------------------------------------
1 | import Metal
2 |
3 | // TODO: These parameter names are terrible. But this is a very useful function.
4 | // TODO: Want to validate types too if possible
5 | func resolveBindings (reflection: MTLRenderPipelineReflection, bindable: inout Bindable, _ a: [(WritableKeyPath, MTLFunctionType, String)]) {
6 | for (keyPath, shaderType, name) in a {
7 | switch shaderType {
8 | case .vertex:
9 | let binding = reflection.vertexBindings.first(where: { $0.name == name })!
10 | bindable[keyPath: keyPath] = binding.index
11 | case .fragment:
12 | let binding = reflection.fragmentBindings.first(where: { $0.name == name })!
13 | bindable[keyPath: keyPath] = binding.index
14 | default:
15 | fatalError()
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Demos/Demos/Support/Gradient.swift:
--------------------------------------------------------------------------------
1 | import simd
2 | import SwiftUI
3 | import Everything
4 |
5 | struct MyGradient {
6 | public struct Stop {
7 | public var color: SIMD4
8 | public var location: Float
9 | internal var controls: (Float, Float, Float) = (0, 0, 0)
10 | }
11 |
12 | var stops: [Stop]
13 |
14 | init(stops: [Stop]) {
15 | if stops.count == 1 {
16 | self.stops = stops
17 | return
18 | }
19 | assert(stops.first?.location == 0)
20 | assert(stops.last?.location == 1)
21 | self.stops = stops.enumerated().map { index, stop in
22 | var handle: (Float, Float, Float) = (stop.location, stop.location, stop.location)
23 | if index != 0 {
24 | handle.0 = stops[index - 1].location
25 | }
26 | if index != stops.count - 1 {
27 | handle.2 = stops[index + 1].location
28 | }
29 | var stop = stop
30 | stop.controls = handle
31 | return(stop)
32 | }
33 | // print(color(at: 0.5))
34 | }
35 | }
36 |
37 | extension MyGradient {
38 | init(colors: [SIMD4]) {
39 | let delta = 1 / Float(colors.count - 1)
40 | self = .init(stops: colors.enumerated().map { index, color in
41 | Stop(color: color, location: Float(index) * delta)
42 | })
43 | }
44 | }
45 |
46 | extension MyGradient {
47 | func color(at position: Float) -> SIMD4 {
48 | if stops.count == 1 {
49 | return stops.first!.color
50 | }
51 | let color = stops.reduce(SIMD4.zero) { result, stop in
52 | var factor: Float = 0
53 | if (stop.controls.0 ... stop.controls.1).contains(position) {
54 | factor = inverseLerp(value: position, startValue: stop.controls.0, endValue: stop.controls.1)
55 | }
56 | else if (stop.controls.1 ... stop.controls.2).contains(position) {
57 | factor = 1 - inverseLerp(value: position, startValue: stop.controls.1, endValue: stop.controls.2)
58 | }
59 | let color = stop.color * factor
60 | return result + color
61 | }
62 |
63 | return color
64 | }
65 | }
66 |
67 | //func clamp(_ value: T, _ lower: T, _ upper: T) -> T {
68 | // let max_ = max(value, lower)
69 | // let min_ = min(max_, upper)
70 | // return min_
71 | //}
72 |
73 | func inverseLerp(value: Float, startValue: Float, endValue: Float) -> Float {
74 | let result: Float
75 | // if value <= startValue {
76 | // result = 0
77 | // }
78 | // else if value >= endValue {
79 | // result = 0
80 | // }
81 | // else {
82 | result = (value - startValue) / (endValue - startValue)
83 | // }
84 | return result
85 | }
86 |
87 | extension Color {
88 | init(_ color: SIMD4) {
89 | self = Color(red: Double(color[0]), green: Double(color[1]), blue: Double(color[2]))
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Demos/Demos/Support/Logging.swift:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | let logger: Logger? = Logger()
4 |
--------------------------------------------------------------------------------
/Demos/Demos/Support/Names.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | // https://github.com/fnichol/names
4 |
5 | struct Names {
6 | static let shared = Names()
7 |
8 | let adjectives: [String.SubSequence]
9 | let nouns: [String.SubSequence]
10 |
11 | init() {
12 | do {
13 | adjectives = try String(contentsOf: Bundle.main.url(forResource: "adjectives", withExtension: "txt")!).split(whereSeparator: \.isNewline)
14 | nouns = try String(contentsOf: Bundle.main.url(forResource: "nouns", withExtension: "txt")!).split(whereSeparator: \.isNewline)
15 | }
16 | catch {
17 | fatalError("\(error)")
18 | }
19 | }
20 |
21 | func random(pad: Int? = nil) -> String {
22 | let adjective = adjectives.randomElement()!
23 | let noun = nouns.randomElement()!
24 | if let pad {
25 | let max = Int(pow(10, Double(pad)))
26 | let pad = Int.random(in: 1..(hashable value: Value, pad: Int? = nil) -> String where Value: Hashable {
35 | var value = abs(value.hashValue)
36 | let adjective = adjectives[value % adjectives.count]
37 | value /= adjectives.count
38 | let noun = nouns[value % nouns.count]
39 | value /= nouns.count
40 | if let pad {
41 | let padCount = Int(pow(10, Double(pad))) - 1
42 | let pad = value % padCount
43 | value /= padCount
44 | return "\(adjective)-\(noun)-\(pad)"
45 | }
46 | else {
47 | return "\(adjective)-\(noun)"
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Demos/Demos/Support/Projections.swift:
--------------------------------------------------------------------------------
1 | import simd
2 | import SIMDSupport
3 | import SwiftUI
4 |
5 | public protocol ProjectionProtocol: Equatable, Sendable {
6 | func matrix(viewSize: SIMD2) -> simd_float4x4
7 | }
8 |
9 | public struct PerspectiveProjection: ProjectionProtocol {
10 | public var fovy: Angle
11 | public var zClip: ClosedRange
12 |
13 | public init(fovy: Angle, zClip: ClosedRange) {
14 | self.fovy = fovy
15 | self.zClip = zClip
16 | }
17 |
18 | public func matrix(viewSize: SIMD2) -> simd_float4x4 {
19 | let aspect = viewSize.x / viewSize.y
20 | return .perspective(aspect: aspect, fovy: Float(fovy.radians), near: zClip.lowerBound, far: zClip.upperBound)
21 | }
22 | }
23 |
24 | public struct OrthographicProjection: ProjectionProtocol {
25 | public var left: Float
26 | public var right: Float
27 | public var bottom: Float
28 | public var top: Float
29 | public var near: Float
30 | public var far: Float
31 |
32 | public init(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float) {
33 | self.left = left
34 | self.right = right
35 | self.bottom = bottom
36 | self.top = top
37 | self.near = near
38 | self.far = far
39 | }
40 |
41 | public func matrix(viewSize: SIMD2) -> simd_float4x4 {
42 | .orthographic(left: left, right: right, bottom: bottom, top: top, near: near, far: far)
43 | }
44 | }
45 |
46 | public enum Projection: ProjectionProtocol {
47 | case matrix(simd_float4x4)
48 | case perspective(PerspectiveProjection)
49 | case orthographic(OrthographicProjection)
50 |
51 | public func matrix(viewSize: SIMD2) -> simd_float4x4 {
52 | switch self {
53 | case .matrix(let projection):
54 | return projection
55 | case .perspective(let projection):
56 | return projection.matrix(viewSize: viewSize)
57 | case .orthographic(let projection):
58 | return projection.matrix(viewSize: viewSize)
59 | }
60 | }
61 |
62 | // TODO: Use that macro
63 | public enum Meta: CaseIterable {
64 | case matrix
65 | case perspective
66 | case orthographic
67 | }
68 |
69 | public var meta: Meta {
70 | switch self {
71 | case .matrix:
72 | return .matrix
73 | case .perspective:
74 | return .perspective
75 | case .orthographic:
76 | return .orthographic
77 | }
78 | }
79 | }
80 |
81 | extension Projection: Sendable {
82 | }
83 |
--------------------------------------------------------------------------------
/Demos/Demos/Support/Resources.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MetalKit
3 |
4 | // TODO: all this is very experimental.
5 |
6 | public protocol ResourceProtocol: Hashable, Sendable {
7 | }
8 |
9 | public extension ResourceProtocol {
10 | func `as`(_ type: any URLProviding.Type) -> (any URLProviding)? {
11 | self as? any URLProviding
12 | }
13 | }
14 |
15 | public protocol URLProviding: ResourceProtocol {
16 | var url: URL { get throws }
17 | }
18 |
19 | public protocol SynchronousLoadable: ResourceProtocol {
20 | associatedtype Content
21 | associatedtype Parameter
22 | func load(_ parameter: Parameter) throws -> Content
23 | }
24 |
25 | public extension SynchronousLoadable where Parameter == () {
26 | func load() throws -> Content {
27 | return try load(())
28 | }
29 | }
30 |
31 | public protocol AsynchronousLoadable: ResourceProtocol {
32 | associatedtype Content // TODO: Sendable?
33 | associatedtype Parameter
34 | func load(_ parameter: Parameter) async throws -> Content
35 | }
36 |
37 | public extension AsynchronousLoadable where Parameter == () {
38 | func load() async throws -> Content {
39 | return try await load(())
40 | }
41 | }
42 |
43 | // MARK: -
44 |
45 | public enum BundleReference: Hashable, Sendable {
46 | enum Error: Swift.Error {
47 | case missingBundle
48 | }
49 |
50 | case main
51 | case byURL(URL)
52 | case byIdentifier(String)
53 | }
54 |
55 | public extension BundleReference {
56 | var exists: Bool {
57 | switch self {
58 | case .main:
59 | return true
60 | case .byURL(let url):
61 | return Bundle(url: url) != nil
62 | case .byIdentifier(let identifier):
63 | return Bundle(identifier: identifier) != nil
64 | }
65 | }
66 |
67 | var bundle: Bundle {
68 | get throws {
69 | switch self {
70 | case .main:
71 | return Bundle.main
72 | case .byURL(let url):
73 | guard let bundle = Bundle(url: url) else {
74 | throw Error.missingBundle
75 | }
76 | return bundle
77 | case .byIdentifier(let identifier):
78 | guard let bundle = Bundle(identifier: identifier) else {
79 | throw Error.missingBundle
80 | }
81 | return bundle
82 | }
83 | }
84 | }
85 | }
86 |
87 | // MARK: -
88 |
89 | public struct BundleResourceReference {
90 | public var bundle: BundleReference
91 | public var name: String
92 | public var `extension`: String?
93 |
94 | public init(bundle: BundleReference, name: String, `extension`: String? = nil) {
95 | self.bundle = bundle
96 | self.name = name
97 | self.`extension` = `extension`
98 | }
99 |
100 | enum Error: Swift.Error {
101 | case missingResource
102 | }
103 |
104 | public var exists: Bool {
105 | return (try? bundle.bundle.url(forResource: name, withExtension: `extension`)) != nil
106 | }
107 |
108 | public var url: URL {
109 | get throws {
110 | guard let url = try bundle.bundle.url(forResource: name, withExtension: `extension`) else {
111 | throw Error.missingResource
112 | }
113 | return url
114 | }
115 | }
116 | }
117 |
118 | public extension BundleReference {
119 | func resource(named name: String, withExtension `extension`: String?) -> BundleResourceReference {
120 | return BundleResourceReference(bundle: self, name: name, extension: `extension`)
121 | }
122 | }
123 |
124 | // MARK: -
125 |
126 | extension BundleResourceReference: URLProviding {
127 | }
128 |
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_01.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_01.ktx
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_02.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_02.ktx
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_03.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_03.ktx
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_04.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_04.ktx
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_05.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_05.ktx
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_06.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_06.ktx
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_07.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_07.ktx
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_08.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_08.ktx
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_09.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_09.ktx
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_10.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_10.ktx
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_11.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_11.ktx
--------------------------------------------------------------------------------
/Demos/Demos/TestcardTiles/Testcard_12.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/TestcardTiles/Testcard_12.ktx
--------------------------------------------------------------------------------
/Demos/Demos/Volumetric/StanfordVolumeData.tar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Demos/Demos/Volumetric/StanfordVolumeData.tar
--------------------------------------------------------------------------------
/Demos/Packages/Compute/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
--------------------------------------------------------------------------------
/Demos/Packages/Compute/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demos/Packages/Compute/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.9
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "Compute",
7 | platforms: [
8 | .iOS(.v17),
9 | .macOS(.v14),
10 | .macCatalyst(.v17),
11 | .tvOS(.v17),
12 | .visionOS(.v1),
13 | ],
14 | products: [
15 | .library(name: "Compute",
16 | targets: ["Compute"]),
17 | ],
18 | dependencies: [
19 | .package(url: "https://github.com/schwa/SwiftGraphics", branch: "jwight/develop"),
20 | .package(path: "../../.."),
21 | ],
22 | targets: [
23 | .target(name: "Compute", dependencies: [
24 | .product(name: "MetalSupport", package: "SwiftGraphics"),
25 | .product(name: "CoreGraphicsSupport", package: "SwiftGraphics"),
26 | .product(name: "RenderKit", package: "RenderKit"),
27 | ]),
28 | .executableTarget(name: "ComputeTool", dependencies: ["Compute"], resources: [
29 | .process("BitonicSort.metal"),
30 | .process("GameOfLife.metal"),
31 | .process("RandomFill.metal"),
32 | ]),
33 | ]
34 | )
35 |
--------------------------------------------------------------------------------
/Demos/Packages/Compute/Sources/Compute/ShaderFunction.swift:
--------------------------------------------------------------------------------
1 | import Metal
2 |
3 | @dynamicMemberLookup
4 | public struct ShaderLibrary {
5 | public static var `default` = ShaderLibrary.bundle(.main)
6 |
7 | public static func bundle(_ bundle: Bundle) -> Self {
8 | return Self(bundle: bundle)
9 | }
10 |
11 | var bundle: Bundle
12 |
13 | private init(bundle: Bundle) {
14 | self.bundle = bundle
15 | }
16 |
17 | public subscript(dynamicMember name: String) -> ShaderFunction {
18 | return ShaderFunction(library: self, name: name)
19 | }
20 | }
21 |
22 | public extension MTLDevice {
23 | func makeLibrary(_ library: ShaderLibrary) throws -> MTLLibrary {
24 | return try makeDefaultLibrary(bundle: library.bundle)
25 | }
26 | }
27 |
28 | // MARK: -
29 |
30 | public struct ShaderFunction: Identifiable {
31 | public let id = UUID()
32 | public let library: ShaderLibrary
33 | public let name: String
34 | public let constants: [ShaderConstant]
35 |
36 | public init(library: ShaderLibrary, name: String, constants: [ShaderConstant] = []) {
37 | self.library = library
38 | self.name = name
39 | self.constants = constants
40 |
41 | // MTLFunctionConstantValues
42 | // MTLDataType
43 | }
44 | }
45 |
46 | public struct ShaderConstant {
47 | var dataType: MTLDataType
48 | var accessor: ((UnsafeRawPointer) -> Void) -> Void
49 |
50 | public init(dataType: MTLDataType, value: [T]) {
51 | self.dataType = dataType
52 | accessor = { (callback: (UnsafeRawPointer) -> Void) in
53 | value.withUnsafeBytes { pointer in
54 | callback(pointer.baseAddress!)
55 | }
56 | }
57 | }
58 |
59 | public init(dataType: MTLDataType, value: T) {
60 | self.dataType = dataType
61 | accessor = { (callback: (UnsafeRawPointer) -> Void) in
62 | withUnsafeBytes(of: value) { pointer in
63 | callback(pointer.baseAddress!)
64 | }
65 | }
66 | }
67 |
68 | public func add(to values: MTLFunctionConstantValues, name: String) {
69 | accessor { pointer in
70 | values.setConstantValue(pointer, type: dataType, withName: name)
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Demos/Packages/Compute/Sources/ComputeTool/BitonicSort.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | using namespace metal;
5 |
6 | [[kernel]]
7 | void bitonicSort(
8 | uint3 thread_position_in_grid [[thread_position_in_grid]],
9 | constant uint &numEntries [[buffer(0)]],
10 | constant uint &groupWidth [[buffer(1)]],
11 | constant uint &groupHeight [[buffer(2)]],
12 | constant uint &stepIndex [[buffer(3)]],
13 | device uint *entries [[buffer(4)]]
14 | ) {
15 | const auto index = thread_position_in_grid.x;
16 | const auto hIndex = index & (groupWidth - 1);
17 | const auto indexLeft = hIndex + (groupHeight + 1) * (index / groupWidth);
18 | const auto stepSize = stepIndex == 0 ? groupHeight - 2 * hIndex : (groupHeight + 1) / 2;
19 | const auto indexRight = indexLeft + stepSize;
20 | // Exit if out of bounds (for non-power of 2 input sizes)
21 | if (indexRight >= numEntries) {
22 | return;
23 | }
24 | const auto valueLeft = entries[indexLeft];
25 | const auto valueRight = entries[indexRight];
26 | // Swap entries if value is descending
27 | if (valueLeft > valueRight) {
28 | entries[indexLeft] = valueRight;
29 | entries[indexRight] = valueLeft;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Demos/Packages/Compute/Sources/ComputeTool/BitonicSort.swift:
--------------------------------------------------------------------------------
1 | import Metal
2 | import Compute
3 |
4 | public struct BitonicSortDemo {
5 | public init() {
6 | }
7 |
8 | public func main() throws {
9 | let stopWatch = StopWatch()
10 |
11 | print("Creating random buffer", stopWatch)
12 | var entries: [UInt32] = (0..<100_000).shuffled()
13 |
14 | print("Copying buffer to GPU.", stopWatch)
15 | let device = MTLCreateSystemDefaultDevice()!
16 | let numEntries = entries.count
17 | let buffer = entries.withUnsafeMutableBufferPointer { buffer in
18 | let buffer = UnsafeMutableRawBufferPointer(buffer)
19 | return device.makeBuffer(bytes: buffer.baseAddress!, length: buffer.count)!
20 | }
21 | print("Preparing compute.", stopWatch)
22 |
23 | print(Bundle.module.bundlePath)
24 |
25 | let function = ShaderLibrary.bundle(.module).bitonicSort
26 | let numStages = Int(log2(nextPowerOfTwo(Double(numEntries))))
27 |
28 | let compute = try Compute(device: device)
29 |
30 | var pass = try compute.makePass(function: function, arguments: [
31 | "numEntries": .int(numEntries),
32 | "entries": .buffer(buffer),
33 | ])
34 |
35 | let start = CFAbsoluteTimeGetCurrent()
36 | print("Running \(numStages) compute stages", stopWatch)
37 |
38 | var threadgroupsPerGrid = (entries.count + pass.maxTotalThreadsPerThreadgroup - 1) / pass.maxTotalThreadsPerThreadgroup
39 | threadgroupsPerGrid = (threadgroupsPerGrid + pass.threadExecutionWidth - 1) / pass.threadExecutionWidth * pass.threadExecutionWidth
40 |
41 | try compute.task { task in
42 | try task { dispatch in
43 | for stageIndex in 0..
2 | #include
3 |
4 | using namespace metal;
5 |
6 | constant bool wrap [[function_constant(0)]];
7 |
8 | static constant int2 positions[] = {
9 | int2(-1, -1),
10 | int2( 0, -1),
11 | int2(+1, -1),
12 | int2(-1, 0),
13 | int2(+1, 0),
14 | int2(-1, +1),
15 | int2( 0, +1),
16 | int2(+1, +1),
17 | };
18 |
19 | bool rules(uint count, bool alive) {
20 | if (alive == true && (count == 2 || count == 3)) {
21 | return true;
22 | }
23 | else if (alive == false && count == 3) {
24 | return true;
25 | }
26 | else if (alive == true) {
27 | return false;
28 | }
29 | else {
30 | return false;
31 | }
32 | }
33 |
34 | template void gameOfLifeGENERIC(
35 | uint2 gid,
36 | texture2d inputTexture,
37 | texture2d outputTexture,
38 | V clear,
39 | V set
40 | )
41 | {
42 | const int2 sgid = int2(gid);
43 | const int2 inputTextureSize = int2(inputTexture.get_width(), inputTexture.get_height());
44 | if (sgid.x >= inputTextureSize.x || sgid.y >= inputTextureSize.y) {
45 | return;
46 | }
47 | uint count = 0;
48 | for (int N = 0; N != 8; ++N) {
49 | int2 position = sgid + positions[N];
50 | if (!wrap) {
51 | if (position.x < 0 || position.x >= inputTextureSize.x) {
52 | continue;
53 | }
54 | else if (position.y < 0 || position.y >= inputTextureSize.y) {
55 | continue;
56 | }
57 | }
58 | else {
59 | position.x = (position.x + inputTextureSize.x) % inputTextureSize.x;
60 | position.y = (position.y + inputTextureSize.y) % inputTextureSize.y;
61 | }
62 | count += inputTexture.read(uint2(position)).r ? 1 : 0;
63 | }
64 | const bool alive = inputTexture.read(gid).r != 0;
65 | outputTexture.write(rules(count, alive), gid);
66 | }
67 |
68 |
69 | [[kernel]]
70 | void gameOfLife_uint(
71 | uint2 gid [[thread_position_in_grid]],
72 | texture2d inputTexture [[texture(0)]],
73 | texture2d outputTexture [[texture(1)]])
74 | {
75 | gameOfLifeGENERIC(gid, inputTexture, outputTexture, 0, 1);
76 | }
77 |
78 | [[kernel]]
79 | void gameOfLife_float4(
80 | uint2 gid [[thread_position_in_grid]],
81 | texture2d inputTexture [[texture(0)]],
82 | texture2d outputTexture [[texture(1)]])
83 | {
84 | gameOfLifeGENERIC(gid, inputTexture, outputTexture, float4(0, 0, 0, 0), float4(1, 1, 1, 1));
85 | }
86 |
--------------------------------------------------------------------------------
/Demos/Packages/Compute/Sources/ComputeTool/GameOfLife.swift:
--------------------------------------------------------------------------------
1 | import Metal
2 | import Foundation
3 | import Compute
4 | import AVFoundation
5 |
6 | struct GameOfLife {
7 | let width = 16
8 | let height = 16
9 | let device = MTLCreateSystemDefaultDevice()!
10 |
11 | func main() throws {
12 | let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .r8Uint, width: width, height: height, mipmapped: false)
13 | textureDescriptor.usage = [.shaderRead, .shaderWrite]
14 | let textureA = device.makeTexture(descriptor: textureDescriptor)!
15 | textureA.label = "texture-a"
16 | let textureB = device.makeTexture(descriptor: textureDescriptor)!
17 | textureB.label = "texture-b"
18 | let compute = try Compute(device: device)
19 | let library = ShaderLibrary.bundle(.module)
20 | var randomFillPass = try compute.makePass(function: library.randomFill_uint)
21 | randomFillPass.arguments.outputTexture = .texture(textureA)
22 |
23 | var gameOfLifePassA = try compute.makePass(function: library.gameOfLife_uint, constants: ["wrap": .bool(false)])
24 | gameOfLifePassA.arguments.inputTexture = .texture(textureA)
25 | gameOfLifePassA.arguments.outputTexture = .texture(textureB)
26 | // print(gameOfLifePassA)
27 |
28 | var gameOfLifePassB = gameOfLifePassA
29 | gameOfLifePassB.arguments.inputTexture = .texture(textureB)
30 | gameOfLifePassB.arguments.outputTexture = .texture(textureA)
31 |
32 | let assetWriter = try AVAssetWriter(outputURL: URL(filePath: "1234.mp4"), fileType: .mp4)
33 |
34 | let assetWriterVideoInput = AVAssetWriterInput(mediaType: .video, outputSettings: [
35 | AVVideoCodecKey: AVVideoCodecType.h264,
36 | AVVideoWidthKey: width,
37 | AVVideoHeightKey: height,
38 | ])
39 | assetWriterVideoInput.expectsMediaDataInRealTime = false
40 | assetWriter.add(assetWriterVideoInput)
41 |
42 | let assetWriterPixelBufferInput = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: assetWriterVideoInput, sourcePixelBufferAttributes: [
43 | kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA,
44 | kCVPixelBufferWidthKey as String: width,
45 | kCVPixelBufferHeightKey as String: height,
46 | kCVPixelBufferMetalCompatibilityKey as String: true
47 | ])
48 |
49 | assetWriter.startWriting()
50 |
51 | print(assetWriterPixelBufferInput)
52 | // guard let pixelBufferPool = assetWriterPixelBufferInput.pixelBufferPool else {
53 | // fatalError()
54 | // }
55 | // print(pixelBufferPool)
56 |
57 | try compute.task { task in
58 | try task { dispatch in
59 | try dispatch(pass: randomFillPass, threadgroupsPerGrid: MTLSize(width: width, height: height, depth: 1), threadsPerThreadgroup: MTLSize(width: 1, height: 1, depth: 1))
60 | }
61 | }
62 |
63 | for _ in 0..<10 {
64 | try compute.task { task in
65 | try task { dispatch in
66 | try dispatch(pass: gameOfLifePassA, threadgroupsPerGrid: MTLSize(width: width, height: height, depth: 1), threadsPerThreadgroup: MTLSize(width: 1, height: 1, depth: 1))
67 | }
68 | }
69 |
70 | try compute.task { task in
71 | try task { dispatch in
72 | try dispatch(pass: gameOfLifePassA, threadgroupsPerGrid: MTLSize(width: width, height: height, depth: 1), threadsPerThreadgroup: MTLSize(width: 1, height: 1, depth: 1))
73 | }
74 | }
75 | }
76 |
77 | print(textureA.toString())
78 | print(textureB.toString())
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Demos/Packages/Compute/Sources/ComputeTool/RandomFill.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | using namespace metal;
5 |
6 | float random(float2 p)
7 | {
8 | // We need irrationals for pseudo randomness.
9 | // Most (all?) known transcendental numbers will (generally) work.
10 | const float2 r = float2(
11 | 23.1406926327792690, // e^pi (Gelfond's constant)
12 | 2.6651441426902251); // 2^sqrt(2) (Gelfond–Schneider constant)
13 | return fract(cos(fmod(123456789.0, 1e-7 + 256.0 * dot(p,r))));
14 | }
15 |
16 | [[kernel]]
17 | void randomFill_uint(
18 | uint2 thread_position_in_grid [[thread_position_in_grid]],
19 | texture2d outputTexture [[texture(1)]])
20 | {
21 | const float2 id = float2(thread_position_in_grid);
22 | outputTexture.write(random(id) > 0.5 ? 255 : 0, thread_position_in_grid);
23 | }
24 |
25 | [[kernel]]
26 | void randomFill_float(
27 | uint2 thread_position_in_grid [[thread_position_in_grid]],
28 | texture2d outputTexture [[texture(1)]])
29 | {
30 | const float2 id = float2(thread_position_in_grid);
31 | outputTexture.write(random(id) > 0.5 ? 1 : 0, thread_position_in_grid);
32 | }
33 |
--------------------------------------------------------------------------------
/Demos/Packages/Compute/Sources/ComputeTool/Support.swift:
--------------------------------------------------------------------------------
1 | import Metal
2 | import Foundation
3 | import Compute
4 | import MetalSupport
5 | import CoreGraphicsSupport
6 | import CoreGraphics
7 | import AppKit
8 | import RenderKit
9 |
10 | class StopWatch: CustomStringConvertible {
11 | var last: CFAbsoluteTime?
12 |
13 | var time: CFAbsoluteTime {
14 | let now = CFAbsoluteTimeGetCurrent()
15 | if last == nil {
16 | last = now
17 | }
18 | return now - last!
19 | }
20 |
21 | var description: String {
22 | return "\(time)"
23 | }
24 | }
25 |
26 | func time(_ block: () -> Void) -> CFAbsoluteTime {
27 | let start = CFAbsoluteTimeGetCurrent()
28 | block()
29 | let end = CFAbsoluteTimeGetCurrent()
30 | return end - start
31 | }
32 |
33 | public func nextPowerOfTwo(_ value: Double) -> Double {
34 | let logValue = log2(Double(value))
35 | let nextPower = pow(2.0, ceil(logValue))
36 | return nextPower
37 | }
38 |
39 | public func nextPowerOfTwo(_ value: Int) -> Int {
40 | return Int(nextPowerOfTwo(Double(value)))
41 | }
42 |
43 | extension Collection where Element: Comparable {
44 | var isSorted: Bool {
45 | return zip(self, sorted()).allSatisfy { lhs, rhs in
46 | lhs == rhs
47 | }
48 | }
49 | }
50 |
51 | public extension MTLSize {
52 | init(width: Int) {
53 | self = MTLSize(width: width, height: 1, depth: 1)
54 | }
55 | }
56 |
57 | extension MTLTexture {
58 | func toString() -> String {
59 | assert(pixelFormat == .r8Uint)
60 | assert(depth == 1)
61 |
62 | let size = width * height * depth
63 |
64 | // TODO: Assumes width is aligned correctly
65 | var buffer = Array(repeating: UInt8.zero, count: size)
66 |
67 | buffer.withUnsafeMutableBytes { buffer in
68 | getBytes(buffer.baseAddress!, bytesPerRow: width, from: MTLRegion(origin: MTLOrigin(x: 0, y: 0, z: 0), size: MTLSize(width: width, height: height, depth: depth)), mipmapLevel: 0)
69 | }
70 |
71 | var s = ""
72 | for row in 0.. CGImage? {
84 | guard let context = CGContext.bitmapContext(definition: .init(width: width, height: height, pixelFormat: .rgba8)) else {
85 | return nil
86 | }
87 | let rect = CGRect(width: CGFloat(width), height: CGFloat(height))
88 | let size2 = rect.size / 2
89 | context.setFillColor(CGColor(red: 1, green: 0, blue: 0, alpha: 1))
90 | context.fill([CGRect(origin: rect.minXMinY, size: size2)])
91 | context.setFillColor(CGColor(red: 0, green: 1, blue: 0, alpha: 1))
92 | context.fill([CGRect(origin: rect.midXMinY, size: size2)])
93 | context.setFillColor(CGColor(red: 0, green: 0, blue: 1, alpha: 1))
94 | context.fill([CGRect(origin: rect.minXMidY, size: size2)])
95 |
96 | var locations: [CGFloat] = [0, 1]
97 | let colors = [CGColor(red: 1, green: 1, blue: 1, alpha: 0), CGColor(red: 1, green: 1, blue: 1, alpha: 1)]
98 | let gradient = CGGradient(colorsSpace: context.colorSpace!, colors: colors as CFArray, locations: &locations)!
99 |
100 | context.clip(to: [CGRect(origin: rect.midXMidY, size: size2)])
101 | context.drawLinearGradient(gradient, start: rect.midXMidY, end: rect.maxXMaxY, options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
102 | return context.makeImage()
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/Demos/Packages/DemosSupport/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/configuration/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 |
--------------------------------------------------------------------------------
/Demos/Packages/DemosSupport/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.9
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "DemosSupport",
8 | platforms: [
9 | .iOS(.v17),
10 | .macOS(.v14),
11 | .macCatalyst(.v17),
12 | .tvOS(.v17),
13 | .visionOS(.v1),
14 | ],
15 | products: [
16 | // Products define the executables and libraries a package produces, making them visible to other packages.
17 | .library(
18 | name: "DemosSupport",
19 | targets: ["DemosSupport"]),
20 | ],
21 | targets: [
22 | // Targets are the basic building blocks of a package, defining a module or a test suite.
23 | // Targets can depend on other targets in this package and products from dependencies.
24 | .target(
25 | name: "DemosSupport"),
26 | .testTarget(
27 | name: "DemosSupportTests",
28 | dependencies: ["DemosSupport"]),
29 | ]
30 | )
31 |
--------------------------------------------------------------------------------
/Demos/Packages/DemosSupport/Tests/DemosSupportTests/DemosSupportTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import DemosSupport
3 |
4 | final class SpatialLookupTests: XCTestCase {
5 | func test1() throws {
6 | var table = SpatialLookupTable<[SIMD2]>(size: [100, 100], positions: [])
7 | table.update(points: [
8 | [0, 0], [100, 100]
9 | ], radius: 50)
10 |
11 | XCTAssertEqual(table.indicesNear(point: [0, 0]).sorted(), [0])
12 | XCTAssertEqual(table.indicesNear(point: [50, 50]).sorted(), [])
13 | XCTAssertEqual(table.indicesNear(point: [100, 100]).sorted(), [1])
14 |
15 | table.update(points: [
16 | [0, 0], [10, 10]
17 | ], radius: 50)
18 | XCTAssertEqual(table.indicesNear(point: [0, 0]).sorted(), [0, 1])
19 | XCTAssertEqual(table.indicesNear(point: [50, 50]).sorted(), [])
20 | XCTAssertEqual(table.indicesNear(point: [100, 100]).sorted(), [])
21 |
22 | table.update(points: [
23 | [0, 0], [0, 0]
24 | ], radius: 50)
25 | XCTAssertEqual(table.indicesNear(point: [0, 0]).sorted(), [0, 1])
26 | XCTAssertEqual(table.indicesNear(point: [50, 50]).sorted(), [])
27 | XCTAssertEqual(table.indicesNear(point: [100, 100]).sorted(), [])
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Demos/RenderKitDemos.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demos/RenderKitDemos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demos/RenderKitDemos.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demos/RenderKitDemos.xcodeproj/xcshareddata/xcschemes/Demos.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
42 |
44 |
50 |
51 |
52 |
53 |
59 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/Demos/xcconfig/CLI-Debug.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // CLI-Debug.xcconfig
3 | //
4 | // Generated by BuildSettingExtractor on 6/27/23
5 | // https://buildsettingextractor.com
6 | //
7 |
8 | #include "CLI-Shared.xcconfig"
9 |
10 | //********************************************//
11 | //* Currently no build settings in this file *//
12 | //********************************************//
13 |
--------------------------------------------------------------------------------
/Demos/xcconfig/CLI-Release.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // CLI-Release.xcconfig
3 | //
4 | // Generated by BuildSettingExtractor on 6/27/23
5 | // https://buildsettingextractor.com
6 | //
7 |
8 | #include "CLI-Shared.xcconfig"
9 |
10 | //********************************************//
11 | //* Currently no build settings in this file *//
12 | //********************************************//
13 |
--------------------------------------------------------------------------------
/Demos/xcconfig/CLI-Shared.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // CLI-Shared.xcconfig
3 | //
4 | // Generated by BuildSettingExtractor on 6/27/23
5 | // https://buildsettingextractor.com
6 | //
7 |
8 | CODE_SIGN_STYLE = Automatic
9 | DEVELOPMENT_TEAM = 6E23EP94PG
10 | ENABLE_HARDENED_RUNTIME = YES
11 | MACOSX_DEPLOYMENT_TARGET = 14.0
12 | PRODUCT_NAME = $(TARGET_NAME)
13 | SDKROOT = macosx
14 | SWIFT_VERSION = 5.0
15 |
--------------------------------------------------------------------------------
/Demos/xcconfig/Demos-Debug.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // Demos-Debug.xcconfig
3 | //
4 | // Generated by BuildSettingExtractor on 6/27/23
5 | // https://buildsettingextractor.com
6 | //
7 |
8 | #include "Demos-Shared.xcconfig"
9 |
10 | //********************************************//
11 | //* Currently no build settings in this file *//
12 | //********************************************//
13 |
--------------------------------------------------------------------------------
/Demos/xcconfig/Demos-Release.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // Demos-Release.xcconfig
3 | //
4 | // Generated by BuildSettingExtractor on 6/27/23
5 | // https://buildsettingextractor.com
6 | //
7 |
8 | #include "Demos-Shared.xcconfig"
9 |
10 | //********************************************//
11 | //* Currently no build settings in this file *//
12 | //********************************************//
13 |
--------------------------------------------------------------------------------
/Demos/xcconfig/Demos-Shared.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // Demos-Shared.xcconfig
3 | //
4 | // Generated by BuildSettingExtractor on 6/27/23
5 | // https://buildsettingextractor.com
6 | //
7 |
8 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
9 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor
10 | CODE_SIGN_ENTITLEMENTS = Demos/Demos.entitlements
11 | CODE_SIGN_STYLE = Automatic
12 | CURRENT_PROJECT_VERSION = 1
13 | DEVELOPMENT_ASSET_PATHS = "Demos/Preview Content"
14 | DEVELOPMENT_TEAM = 6E23EP94PG
15 | ENABLE_HARDENED_RUNTIME = YES
16 | ENABLE_PREVIEWS = YES
17 | GENERATE_INFOPLIST_FILE = YES
18 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*] = YES
19 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*] = YES
20 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*] = YES
21 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*] = YES
22 | INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*] = YES
23 | INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*] = YES
24 | INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*] = UIStatusBarStyleDefault
25 | INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*] = UIStatusBarStyleDefault
26 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
27 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
28 | IPHONEOS_DEPLOYMENT_TARGET = 17.0
29 | LD_RUNPATH_SEARCH_PATHS = @executable_path/Frameworks
30 | LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = @executable_path/../Frameworks
31 | MACOSX_DEPLOYMENT_TARGET = 14.0
32 | MARKETING_VERSION = 1.0
33 | PRODUCT_BUNDLE_IDENTIFIER = io.schwa.Demos
34 | PRODUCT_NAME = $(TARGET_NAME)
35 | SDKROOT = auto
36 | SUPPORTED_PLATFORMS = iphoneos iphonesimulator macosx
37 | SWIFT_EMIT_LOC_STRINGS = YES
38 | SWIFT_VERSION = 5.0
39 |
40 | SUPPORTED_PLATFORMS = iphoneos iphonesimulator macosx xros xrsimulator
41 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
42 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
43 | INFOPLIST_FILE = Demos/Info.plist
44 | SUPPORTS_MACCATALYST = NO
45 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO
46 | SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES
47 | TARGETED_DEVICE_FAMILY = 1,2,7
48 |
--------------------------------------------------------------------------------
/Demos/xcconfig/Project-Debug.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // Project-Debug.xcconfig
3 | //
4 | // Generated by BuildSettingExtractor on 6/27/23
5 | // https://buildsettingextractor.com
6 | //
7 |
8 | #include "Project-Shared.xcconfig"
9 |
10 | DEBUG_INFORMATION_FORMAT = dwarf
11 | ENABLE_TESTABILITY = YES
12 | GCC_DYNAMIC_NO_PIC = NO
13 | GCC_OPTIMIZATION_LEVEL = 0
14 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited)
15 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE
16 | ONLY_ACTIVE_ARCH = YES
17 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG $(inherited)
18 | SWIFT_OPTIMIZATION_LEVEL = -Onone
19 |
--------------------------------------------------------------------------------
/Demos/xcconfig/Project-Release.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // Project-Release.xcconfig
3 | //
4 | // Generated by BuildSettingExtractor on 6/27/23
5 | // https://buildsettingextractor.com
6 | //
7 |
8 | #include "Project-Shared.xcconfig"
9 |
10 | DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
11 | ENABLE_NS_ASSERTIONS = NO
12 | MTL_ENABLE_DEBUG_INFO = NO
13 | SWIFT_COMPILATION_MODE = wholemodule
14 |
--------------------------------------------------------------------------------
/Demos/xcconfig/Project-Shared.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // Project-Shared.xcconfig
3 | //
4 | // Generated by BuildSettingExtractor on 6/27/23
5 | // https://buildsettingextractor.com
6 | //
7 |
8 | ALWAYS_SEARCH_USER_PATHS = NO
9 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES
10 | CLANG_ANALYZER_NONNULL = YES
11 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE
12 | CLANG_CXX_LANGUAGE_STANDARD = gnu++20
13 | CLANG_ENABLE_MODULES = YES
14 | CLANG_ENABLE_OBJC_ARC = YES
15 | CLANG_ENABLE_OBJC_WEAK = YES
16 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
17 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES
18 | CLANG_WARN_BOOL_CONVERSION = YES
19 | CLANG_WARN_COMMA = YES
20 | CLANG_WARN_CONSTANT_CONVERSION = YES
21 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
22 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR
23 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES
24 | CLANG_WARN_EMPTY_BODY = YES
25 | CLANG_WARN_ENUM_CONVERSION = YES
26 | CLANG_WARN_INFINITE_RECURSION = YES
27 | CLANG_WARN_INT_CONVERSION = YES
28 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES
29 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
30 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES
31 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR
32 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES
33 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES
34 | CLANG_WARN_STRICT_PROTOTYPES = YES
35 | CLANG_WARN_SUSPICIOUS_MOVE = YES
36 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
37 | CLANG_WARN_UNREACHABLE_CODE = YES
38 | COPY_PHASE_STRIP = NO
39 | ENABLE_STRICT_OBJC_MSGSEND = YES
40 | ENABLE_USER_SCRIPT_SANDBOXING = YES
41 | GCC_C_LANGUAGE_STANDARD = gnu17
42 | GCC_NO_COMMON_BLOCKS = YES
43 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES
44 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR
45 | GCC_WARN_UNDECLARED_SELECTOR = YES
46 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE
47 | GCC_WARN_UNUSED_FUNCTION = YES
48 | GCC_WARN_UNUSED_VARIABLE = YES
49 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES
50 | MTL_FAST_MATH = YES
51 |
52 | DEAD_CODE_STRIPPING = YES
53 | SWIFT_STRICT_CONCURRENCY = complete
54 |
--------------------------------------------------------------------------------
/Documentation/Exported PNG image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Documentation/Exported PNG image.png
--------------------------------------------------------------------------------
/Documentation/Mesh.plantuml:
--------------------------------------------------------------------------------
1 | @startuml Meshes
2 |
3 | package MetalKit {
4 | class MTKMesh {
5 | + submeshes: [MTKSubmesh]
6 | + vertexBuffers: [MTKMeshBuffer]
7 | + vertexDescriptor: MTLVertexDescriptor
8 | + vertexCount: Int
9 | + name: String
10 | + init(mesh:,device:)
11 | + class func newMeshes(asset:,device:)
12 |
13 | }
14 |
15 | class MTKSubmesh {
16 | + indexBuffer: MTKMeshBuffer
17 | + indexCount: Int
18 | + indexType: MTLIndexType
19 | + mesh: MTKMesh
20 | + name: String
21 | + primitiveType: MTLPrimitiveType
22 | }
23 |
24 | class MTKMeshBuffer {
25 | + allocator: MTKMeshBufferAllocator
26 | + buffer: MTLBuffer
27 | + offset: Int
28 | + length: Int
29 | + type: MTKMeshBufferType
30 | }
31 |
32 | MTKMesh --> MTKSubmesh : submeshes
33 | MTKMesh --> MTKMeshBuffer : vertexBuffers
34 | MTKSubmesh --> MTKMeshBuffer : indexBuffer
35 | }
36 |
37 | package ModelIO {
38 | class MDLMesh {
39 | + submeshes: [MDLSubmesh]
40 | + vertexBuffers: [MDLMeshBuffer]
41 | + vertexDescriptor: MDLVertexDescriptor
42 | + vertexCount: Int
43 | + allocator: MDLMeshBufferAllocator
44 | + boundingBox: MDLAxisAlignedBoundingBox
45 | }
46 |
47 | class MDLSubmesh {
48 | + indexBuffer: MDLMeshBuffer
49 | + indexCount: Int
50 | + indexType: MDLIndexBitDepth
51 | + geometryType: MDLGeometryType
52 | + topology: MDLSubmeshTopology?
53 | + material: MDLMaterial?
54 | + name: String
55 | }
56 |
57 | class MDLMeshBuffer {
58 | + allocator: MDLMeshBufferAllocator
59 | + length: Int
60 | + type: MDLMeshBufferType
61 | + zone: MDLMeshBufferZone
62 | }
63 |
64 | MDLMesh --> MDLSubmesh : submeshes
65 | MDLMesh --> MDLMeshBuffer : vertexBuffers
66 | MDLSubmesh --> MDLMeshBuffer : indexBuffer
67 |
68 | }
69 | @enduml
70 |
--------------------------------------------------------------------------------
/Documentation/Screenshot 2023-07-01 at 08.06.29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Documentation/Screenshot 2023-07-01 at 08.06.29.png
--------------------------------------------------------------------------------
/Documentation/Screenshot 2023-09-19 at 19.51.00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Documentation/Screenshot 2023-09-19 at 19.51.00.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2023, Jonathan Wight
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | 1. Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 |
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | 3. Neither the name of the copyright holder nor the names of its
16 | contributors may be used to endorse or promote products derived from
17 | this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/MyPlayground.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import RenderKitScratch
3 | import simd
4 |
5 | let sphere = Sphere(center: .zero, radius: 8)
6 | print(try sphere.encodeToShapeScript())
7 |
8 | let line = Line3D(point: [0, 0, 0], direction: [1, 0, 0])
9 |
10 | print(try line.encodeToShapeScript())
11 |
--------------------------------------------------------------------------------
/MyPlayground.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.9
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "RenderKit",
7 | platforms: [
8 | .iOS(.v17),
9 | .macOS(.v14),
10 | .macCatalyst(.v17),
11 | .tvOS(.v17),
12 | .visionOS(.v1),
13 | ],
14 | products: [
15 | .library(name: "RenderKit", targets: ["RenderKit"]),
16 | .library(name: "RenderKitScratch", targets: ["RenderKitScratch"]),
17 | .library(name: "RenderKitShaders", targets: ["RenderKitShaders"]),
18 | ],
19 | dependencies: [
20 | .package(url: "https://github.com/schwa/Everything", branch: "jwight/downsizing"),
21 | .package(url: "https://github.com/schwa/SwiftGraphics", branch: "jwight/develop"),
22 | .package(url: "https://github.com/apple/swift-algorithms", from: "1.1.0"),
23 | .package(url: "https://github.com/apple/swift-async-algorithms", from: "0.1.0"),
24 | .package(url: "https://github.com/schwa/swiftformats", from: "0.3.3"),
25 | .package(url: "https://github.com/schwa/swiftfields", from: "0.1.3"),
26 | .package(url: "https://github.com/schwa/MetalCompilerPlugin", from: "0.0.2"),
27 | //.package(url: "https://github.com/schwa/StreamBuilder", branch: "main"),
28 | ],
29 | targets: [
30 | .target(
31 | name: "RenderKit",
32 | dependencies: [
33 | .product(name: "MetalSupport", package: "SwiftGraphics"),
34 | .product(name: "MetalSupportUnsafeConformances", package: "SwiftGraphics"),
35 | .product(name: "SIMDSupport", package: "SwiftGraphics"),
36 | .product(name: "Shapes2D", package: "SwiftGraphics"),
37 | .product(name: "Everything", package: "Everything"),
38 | .product(name: "CoreGraphicsSupport", package: "SwiftGraphics"),
39 | .product(name: "Algorithms", package: "swift-algorithms"),
40 | .product(name: "SwiftFields", package: "swiftfields"),
41 | .product(name: "SwiftFormats", package: "swiftformats"),
42 | .product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
43 | "RenderKitShaders",
44 | // .product(name: "StreamBuilder", package: "StreamBuilder"),
45 | ],
46 | resources: [
47 | // .process("Media.xcassets"),
48 | .process("VisionOS/Assets.xcassets"),
49 | ],
50 | swiftSettings: [
51 | .enableExperimentalFeature("VariadicGenerics"),
52 | .enableUpcomingFeature("StrictConcurrency"),
53 | ]
54 | ),
55 | .target(
56 | name: "RenderKitShaders",
57 | plugins: [
58 | // .plugin(name: "MetalCompilerPlugin", package: "MetalCompilerPlugin")
59 | ]
60 | ),
61 | .target(
62 | name: "RenderKitScratch",
63 | dependencies: [
64 | "Everything",
65 | "RenderKit",
66 | ]
67 | ),
68 | .testTarget(
69 | name: "RenderKitTests",
70 | dependencies: ["RenderKit", "RenderKitScratch"]),
71 | ]
72 | )
73 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RenderKit
2 |
3 | Yet another Metal Rendering engine experiment…
4 |
5 | ## Assumptions
6 |
7 | Post-multiplication, Column-Major, Right-handed, Y up
8 |
9 | ## Screenshots
10 |
11 | ![Latest Screenshot]()
12 | ![Older Screenshot]()
13 |
14 | ## Goals
15 |
16 | ### Longer Term
17 |
18 | * Tie into WASD/Game Controller system (from RenderKit - move code to Everything)
19 | * Render the position of lights
20 | * Learn from [RenderKit2](https://github.com/schwa/RenderKit/tree/RenderKit2) & [RenderKitClassic](https://github.com/schwa/RenderKit/tree/RenderKitClassic)
21 | * More type-safety. Especially pipeline attributes
22 | * Async. (Good luck)
23 | * Outputs
24 | * A unified "renderable" system. Can render to any of multiple locations - MTKView, "raw" CAMetalLayer, offscreen textures, VistionKit immersive spaces etc.
25 | * ~~Built-in offscreen renderer~~ (Working but API can be fixed)
26 | * Built-in support for ~~MTKView~~ and CAMetalLayer
27 | * VisionKit 'CPSceneSessionRoleImmersiveSpaceApplication' output
28 | * (The more outputs the less fragile the APO will be)
29 | * Simpler API for just getting shit on-screen
30 | * Create a simple render API that takes a single pipeline
31 | * RenderEnvironment variables modelled after SwiftUI.Environment for safety
32 | * Use Function-builder for pipelines/stages etc
33 | * Cleaner render model that more closely matches Metal.
34 | * Built-in support for RenderGraph editor (use NodeEditor)
35 | * Work with multi-sample, ray tracing
36 | * Figure out how to #include metal headers across packages (likely not possible).
37 | * Get more helper code of RenderKit and into MetalSupport etc
38 | * Use Spatial framework
39 | * Use the shape code from Spatial to bring in a `shape3d` type that can export MTKMeshes* Simple SwiftUI Canvas style line drawing mode.
40 | * Some of the rotation code here may be useful - what else is new in Spatial?
41 | * Bring over my Projection package (3d vector graphics)
42 | * Take advantage of Swift macros (macro to encode struct into a buffer compatible with SwiftUI)
43 | * Sort out the various projection APIs
44 |
45 | ## Links
46 |
47 | *
48 |
--------------------------------------------------------------------------------
/Sources/RenderKit/PackedFloat.swift:
--------------------------------------------------------------------------------
1 | import simd
2 | import RenderKitShaders
3 |
4 | public extension PackedFloat3 {
5 | init(_ value: SIMD3) {
6 | self = .init(x: value.x, y: value.y, z: value.z)
7 | }
8 | }
9 |
10 | extension PackedFloat3: Equatable {
11 | public static func == (lhs: PackedFloat3, rhs: PackedFloat3) -> Bool {
12 | SIMD3(lhs) == SIMD3(rhs)
13 | }
14 | }
15 |
16 | extension PackedFloat3: Hashable {
17 | public func hash(into hasher: inout Hasher) {
18 | SIMD3(self).hash(into: &hasher)
19 | }
20 | }
21 |
22 | extension PackedFloat3: @unchecked Sendable {
23 | }
24 |
25 | extension PackedFloat3: ExpressibleByArrayLiteral {
26 | public init(arrayLiteral elements: Float...) {
27 | self = .init(x: elements[0], y: elements[1], z: elements[2])
28 | }
29 | }
30 |
31 | public extension SIMD3 where Scalar == Float {
32 | init(_ packed: PackedFloat3) {
33 | self = .init(x: packed.x, y: packed.y, z: packed.z)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Sources/RenderKit/RenderKitShaders.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Jonathan Wight on 6/30/23.
6 | //
7 |
8 | import Foundation
9 |
10 | public extension Bundle {
11 | static let shadersBundle: Bundle = {
12 | // Step 1. Find the bundle as a child of main bundle.
13 | if let shadersBundleURL = Bundle.main.url(forResource: "RenderKit_RenderKitShaders", withExtension: "bundle"), let bundle = Bundle(url: shadersBundleURL) {
14 | return bundle
15 | }
16 | // Step 2. Find the bundle as peer to the current `Bundle.module`
17 | if let bundle = Bundle(url: Bundle.module.bundleURL.deletingLastPathComponent().appendingPathComponent("RenderKit_RenderKitShaders.bundle")) {
18 | return bundle
19 | }
20 | // Fail.
21 | fatalError("Could not find shaders bundle")
22 | }()
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/RenderKit/RenderPass.swift:
--------------------------------------------------------------------------------
1 | import CoreGraphics
2 | import Metal
3 |
4 | public protocol MetalConfiguration {
5 | var colorPixelFormat: MTLPixelFormat { get set }
6 | var clearColor: MTLClearColor { get set }
7 | var depthStencilPixelFormat: MTLPixelFormat { get set }
8 | var depthStencilStorageMode: MTLStorageMode { get set }
9 | var clearDepth: Double { get set }
10 | }
11 |
12 | // MARK: -
13 |
14 | public protocol RenderPass: AnyObject {
15 | func setup (device: MTLDevice, configuration: inout Configuration) throws
16 | func drawableSizeWillChange(device: MTLDevice, size: CGSize) throws
17 | func draw (device: MTLDevice, size: CGSize, renderPassDescriptor: MTLRenderPassDescriptor, commandBuffer: MTLCommandBuffer) throws
18 | }
19 |
20 | public extension RenderPass {
21 | func drawableSizeWillChange(device: MTLDevice, size: CGSize) throws {
22 | }
23 | }
24 |
25 | // MARK: -
26 |
27 | // TODO: Combine jobs and passes
28 |
29 | public protocol RenderJob: AnyObject {
30 | func setup(device: MTLDevice, configuration: inout Configuration) throws
31 | func drawableSizeWillChange(device: MTLDevice, size: CGSize) throws
32 | func encode(on encoder: MTLRenderCommandEncoder, size: CGSize) throws // TODO: Add configuration just to be consistent? Or remove from renderpass.
33 | }
34 |
35 | public extension RenderJob {
36 | func drawableSizeWillChange(device: MTLDevice, size: CGSize) throws {
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Sources/RenderKit/RendererView.swift:
--------------------------------------------------------------------------------
1 | #if !os(visionOS)
2 | import SwiftUI
3 | import MetalSupport
4 |
5 | public struct RendererView : View where T: RenderPass {
6 | // TODO: Does this _really_ need to be a Binding?
7 | @Binding
8 | var renderPass: T
9 |
10 | @State
11 | var commandQueue: MTLCommandQueue?
12 |
13 | @Environment(\.metalDevice)
14 | var device
15 |
16 | public init(renderPass: Binding) {
17 | self._renderPass = renderPass
18 | }
19 |
20 | public var body: some View {
21 | MetalView { device, configuration in
22 | configuration.preferredFramesPerSecond = 120
23 | configuration.colorPixelFormat = .bgra8Unorm_srgb
24 | configuration.depthStencilPixelFormat = .depth16Unorm
25 | configuration.depthStencilStorageMode = .memoryless
26 | try renderPass.setup(device: device, configuration: &configuration)
27 | } drawableSizeWillChange: { device, _, size in
28 | try renderPass.drawableSizeWillChange(device: device, size: size)
29 | } draw: { device, _, size, currentDrawable, renderPassDescriptor in
30 | guard let commandQueue else {
31 | fatalError("Draw called before command queue set up. This should be impossible.")
32 | }
33 | try commandQueue.withCommandBuffer(drawable: currentDrawable, block: { commandBuffer in
34 | commandBuffer.label = "RendererView-CommandBuffer"
35 | try renderPass.draw(device: device, size: size, renderPassDescriptor: renderPassDescriptor, commandBuffer: commandBuffer)
36 | })
37 | }
38 | .onAppear {
39 | if commandQueue == nil {
40 | commandQueue = device.makeCommandQueue()
41 | }
42 | }
43 | }
44 | }
45 | #endif
46 |
--------------------------------------------------------------------------------
/Sources/RenderKit/SimpleVertex.swift:
--------------------------------------------------------------------------------
1 | import RenderKitShaders
2 | import simd
3 |
4 | public extension SimpleVertex {
5 | var position: SIMD3 {
6 | get {
7 | return SIMD3(packedPosition)
8 | }
9 | set {
10 | packedPosition = PackedFloat3(newValue)
11 | }
12 | }
13 |
14 | var normal: SIMD3 {
15 | get {
16 | return SIMD3(packedNormal)
17 | }
18 | set {
19 | packedNormal = PackedFloat3(newValue)
20 | }
21 | }
22 |
23 | init(position: SIMD3, normal: SIMD3, textureCoordinate: SIMD2) {
24 | self = .init(packedPosition: PackedFloat3(position), packedNormal: PackedFloat3(normal), textureCoordinate: textureCoordinate)
25 | }
26 | }
27 |
28 | extension SimpleVertex: @unchecked Sendable {
29 | }
30 |
31 | extension SimpleVertex: Equatable {
32 | public static func == (lhs: SimpleVertex, rhs: SimpleVertex) -> Bool {
33 | lhs.packedPosition == rhs.packedPosition && lhs.packedNormal == rhs.packedNormal && lhs.textureCoordinate == rhs.textureCoordinate
34 | }
35 | }
36 |
37 | extension SimpleVertex: Hashable {
38 | public func hash(into hasher: inout Hasher) {
39 | packedPosition.hash(into: &hasher)
40 | packedNormal.hash(into: &hasher)
41 | textureCoordinate.hash(into: &hasher)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Sources/RenderKit/Support/Functions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | // TODO: move
4 |
5 | // https://docs.gl/sl4/step
6 | // https://www.youtube.com/watch?v=YJB1QnEmlTs
7 |
8 | func step (_ x: V, _ a: V) -> V {
9 | x < a ? 0.0 : 1.0
10 | }
11 |
12 | func clamp(_ x: V, _ a: V, _ b: V) -> V {
13 | if x < a {
14 | return a
15 | }
16 | if x > b {
17 | return b
18 | }
19 | return x
20 | }
21 |
22 | func saturate(_ x: V) -> V {
23 | return clamp(x, 0.0, 1.0)
24 | }
25 |
26 | func smoothstep(_ a: V, _ b: V, _ x: V) -> V {
27 | let y = saturate((x - a) / (b - a))
28 | return y * y * (3.0 - 2.0 * y)
29 | }
30 |
31 | // from https://graphtoy.com
32 | //function clamp(x,a,b) { if( xb ) return b; return x; }
33 | //function saturate(x) { return clamp(x,0.0,1.0); }
34 | //function remap(a,b,x,c,d) { if( xb ) return d; let y=(x-a)/(b-a); return c + (d-c)*y; }
35 | //function smoothstep(a,b,x) { let y = saturate((x-a)/(b-a)); return y*y*(3.0-2.0*y); }
36 | //function ssign(x) { return (x>=0.0)?1.0:-1.0; }
37 | //function radians(degrees) { return degrees*Math.PI/180.0; }
38 | //function degrees(radians) { return radians*180.0/Math.PI; }
39 | //function inversesqrt(x) { return 1.0/Math.sqrt(x); }
40 | //function rsqrt(x) { return inversesqrt(x); }
41 | //function rcbrt(x) { return 1.0/Math.cbrt(x); }
42 | //function rcp(x) { return 1.0/x; }
43 | //function fma(x,y,z) { return x*y+z; }
44 | //function step(a,x) { return (x0.0) ? x : x+1.0; if(xa)?1.0:-1.0; }
50 | //function frac(x) { return x - Math.floor(x); }
51 | //function fract(x) { return frac(x); }
52 | //function exp2(x) { return pow(2.0,x); }
53 | //function exp10(x) { return pow(10.0,x); }
54 | //function mod(x,y) { return x-y*Math.floor(x/y); }
55 | //function cellnoise(x)
56 | //{
57 | // let n = Math.floor(x) | 0;
58 | // n = (n << 13) ^ n; n &= 0xffffffff;
59 | // let m = n;
60 | // n = n * 15731; n &= 0xffffffff;
61 | // n = n * m; n &= 0xffffffff;
62 | // n = n + 789221; n &= 0xffffffff;
63 | // n = n * m; n &= 0xffffffff;
64 | // n = n + 1376312589; n &= 0xffffffff;
65 | // n = (n>>14) & 65535;
66 | // return n/65535.0;
67 | //}
68 | //function voronoi(x)
69 | //{
70 | // const i = Math.floor(x);
71 | // const f = x - i;
72 | // const x0 = cellnoise(i-1); const d0 = Math.abs(f-(-1+x0));
73 | // const x1 = cellnoise(i ); const d1 = Math.abs(f-( x1));
74 | // const x2 = cellnoise(i+1); const d2 = Math.abs(f-( 1+x2));
75 | // let r = d0;
76 | // r = (d1 Self {
8 | nextIndexByPrefix.withLock { nextIndexByPrefix in
9 | let index = nextIndexByPrefix[prefix, default: 0]
10 | let id = LOLID2(rawValue: "\(prefix)-\(index)")
11 | nextIndexByPrefix[prefix] = index + 1
12 | return id
13 | }
14 | }
15 |
16 | internal let rawValue: String
17 |
18 | internal init(rawValue: String) {
19 | self.rawValue = rawValue
20 | }
21 |
22 | public init(prefix: String) {
23 | self = LOLID2.generate(prefix: prefix)
24 | }
25 | }
26 |
27 | extension LOLID2: CustomStringConvertible {
28 | public var description: String {
29 | return rawValue
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Sources/RenderKit/Support/Logging.swift:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | let logger: Logger? = Logger()
4 |
--------------------------------------------------------------------------------
/Sources/RenderKit/VisionOS/Assets.xcassets/ColorMap.textureset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "properties" : {
7 | "origin" : "bottom-left",
8 | "interpretation" : "non-premultiplied-colors"
9 | },
10 | "textures" : [
11 | {
12 | "idiom" : "universal",
13 | "filename" : "Universal.mipmapset"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/RenderKit/VisionOS/Assets.xcassets/ColorMap.textureset/Universal.mipmapset/ColorMap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schwa/RenderKit/9d8ea58902e2a6e270faf7b9edfef64b62beaec5/Sources/RenderKit/VisionOS/Assets.xcassets/ColorMap.textureset/Universal.mipmapset/ColorMap.png
--------------------------------------------------------------------------------
/Sources/RenderKit/VisionOS/Assets.xcassets/ColorMap.textureset/Universal.mipmapset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "levels" : [
7 | {
8 | "filename" : "ColorMap.png",
9 | "mipmap-level" : "base"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/RenderKit/VisionOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Sources/RenderKitScratch/CircularPairs.swift:
--------------------------------------------------------------------------------
1 | public extension Sequence {
2 | func circularPairs() -> CircularPairsSequence {
3 | CircularPairsSequence(base: self)
4 | }
5 | }
6 |
7 | public struct CircularPairsSequence {
8 | var base: Base
9 |
10 | public init(base: Base) {
11 | self.base = base
12 | }
13 | }
14 |
15 | extension CircularPairsSequence: Sequence {
16 | public func makeIterator() -> Iterator {
17 | Iterator(base: base.makeIterator())
18 | }
19 |
20 | public struct Iterator: IteratorProtocol {
21 | var base: Base.Iterator
22 | var first: Base.Element?
23 | var previous: Base.Element?
24 | var atEnd = false
25 |
26 | public mutating func next() -> (Base.Element, Base.Element)? {
27 | switch (base.next(), first, previous, atEnd) {
28 | case (.none, .none, .none, false):
29 | return nil
30 | case (.some(let current), .none, .none, false):
31 | guard let next = base.next() else {
32 | return (current, current)
33 | }
34 | first = current
35 | previous = next
36 | return (current, next)
37 | case (.some(let current), .some, .some(let previous), false):
38 | self.previous = current
39 | return (previous, current)
40 | case (.none, .some(let first), .some(let previous), false):
41 | atEnd = true
42 | return (previous, first)
43 | case (_, _, _, true):
44 | return nil
45 | default:
46 | fatalError()
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/RenderKitScratch/HighContrastColors.swift:
--------------------------------------------------------------------------------
1 | // https://stackoverflow.com/questions/470690/how-to-automatically-generate-n-distinct-colors/4382138#4382138
2 | // P. Green-Armytage (2010): A Colour Alphabet and the Limits of Colour Coding. // Colour: Design & Creativity (5) (2010): 10, 1-23
3 | // https://aic-color.org/resources/Documents/jaic_v5_06.pdf
4 | // https://ui.adsabs.harvard.edu/abs/1989SPIE.1077..322B/abstract
5 | // https://stackoverflow.com/questions/470690/how-to-automatically-generate-n-distinct-colors/4382138#4382138
6 |
7 | import SwiftUI
8 |
9 | public let kellyColors: [(Float, Float, Float)] = [
10 | 0xFFB300, // Vivid Yellow
11 | 0x803E75, // Strong Purple
12 | 0xFF6800, // Vivid Orange
13 | 0xA6BDD7, // Very Light Blue
14 | 0xC10020, // Vivid Red
15 | 0xCEA262, // Grayish Yellow
16 | 0x817066, // Medium Gray
17 | 0x007D34, // Vivid Green
18 | 0xF6768E, // Strong Purplish Pink
19 | 0x00538A, // Strong Blue
20 | 0xFF7A5C, // Strong Yellowish Pink
21 | 0x53377A, // Strong Violet
22 | 0xFF8E00, // Vivid Orange Yellow
23 | 0xB32851, // Strong Purplish Red
24 | 0xF4C800, // Vivid Greenish Yellow
25 | 0x7F180D, // Strong Reddish Brown
26 | 0x93AA00, // Vivid Yellowish Green
27 | 0x593315, // Deep Yellowish Brown
28 | 0xF13A13, // Vivid Reddish Orange
29 | 0x232C16, // Dark Olive Green
30 | ].map { hex in
31 | let red = hex >> 16 & 0xFF
32 | let green = hex >> 8 & 0xFF
33 | let blue = hex & 0xFF
34 | return (Float(red) / 255, Float(green) / 255, Float(blue) / 255)
35 | }
36 |
37 | public extension Color {
38 | init(rgb: (Float, Float, Float)) {
39 | self = .init(red: Double(rgb.0), green: Double(rgb.1), blue: Double(rgb.2))
40 | }
41 | }
42 |
43 | // Green-Armytage
44 | //240,163,255
45 | //0,117,220
46 | //153,63,0
47 | //76,0,92
48 | //25,25,25
49 | //0,92,49
50 | //43,206,72
51 | //255,204,153
52 | //128,128,128
53 | //148,255,181
54 | //143,124,0
55 | //157,204,0
56 | //194,0,136
57 | //0,51,128
58 | //255,164,5
59 | //255,168,187
60 | //66,102,0
61 | //255,0,16
62 | //94,241,242
63 | //0,153,143
64 | //224,255,102
65 | //116,10,255
66 | //153,0,0
67 | //255,255,128
68 | //255,255,0
69 | //255,80,5
70 |
--------------------------------------------------------------------------------
/Sources/RenderKitScratch/PLYEncoder.swift:
--------------------------------------------------------------------------------
1 | public struct PlyEncoder {
2 | public typealias Output = String
3 |
4 | public init() {
5 | }
6 |
7 | public func encodeHeader(to output: inout Output) {
8 | print("ply", to: &output)
9 | }
10 |
11 | public func encodeVersion(to output: inout Output) {
12 | print("format ascii 1.0", to: &output)
13 | }
14 |
15 | public func encodeComment(_ comment: String, to output: inout Output) {
16 | print("comment \(comment)", to: &output)
17 | }
18 |
19 | public enum NumericalType: String {
20 | case char
21 | case uchar
22 | case short
23 | case ushort
24 | case int
25 | case uint
26 | case float
27 | case double
28 | }
29 |
30 | public enum Value {
31 | case char(Int8)
32 | case uchar(UInt8)
33 | case short(Int16)
34 | case ushort(UInt16)
35 | case int(Int32)
36 | case uint(UInt32)
37 | case float(Float)
38 | case double(Double)
39 | }
40 |
41 | public enum Kind {
42 | case numerical(NumericalType)
43 | case list(count: NumericalType, element: NumericalType)
44 | }
45 |
46 | // element vertex 12
47 | // property float x
48 | // property float y
49 | // property float z
50 |
51 | public func encodeElementDefinition(name: String, count: Int, properties: [(Kind, String)], to output: inout Output) {
52 | print("element \(name) \(count)", to: &output)
53 | for (kind, name) in properties {
54 | switch kind {
55 | case .numerical(let numericType):
56 | print("property \(numericType.rawValue) \(name)", to: &output)
57 | case .list(let count, let element):
58 | print("property list \(count.rawValue) \(element.rawValue) \(name)", to: &output)
59 | }
60 | }
61 | }
62 |
63 | public func encodeEndHeader(to output: inout Output) {
64 | print("end_header", to: &output)
65 | }
66 |
67 | public func encodeElement(_ values: [Value], to output: inout Output) {
68 | print(values.map { $0.description }.joined(separator: " "), to: &output)
69 | }
70 |
71 | public func encodeListElement(_ values: [Value], to output: inout Output) {
72 | print("\(values.count) \(values.map { $0.description }.joined(separator: " "))", to: &output)
73 | }
74 | }
75 |
76 | public extension PlyEncoder.Kind {
77 | static let char = Self.numerical(.char)
78 | static let uchar = Self.numerical(.uchar)
79 | static let short = Self.numerical(.short)
80 | static let ushort = Self.numerical(.ushort)
81 | static let int = Self.numerical(.int)
82 | static let uint = Self.numerical(.uint)
83 | static let float = Self.numerical(.float)
84 | static let double = Self.numerical(.double)
85 | }
86 |
87 | extension PlyEncoder.Value: CustomStringConvertible {
88 | public var description: String {
89 | switch self {
90 | case .char(let value):
91 | return "\(value)"
92 | case .uchar(let value):
93 | return "\(value)"
94 | case .short(let value):
95 | return "\(value)"
96 | case .ushort(let value):
97 | return "\(value)"
98 | case .int(let value):
99 | return "\(value)"
100 | case .uint(let value):
101 | return "\(value)"
102 | case .float(let value):
103 | return "\(value)"
104 | case .double(let value):
105 | return "\(value)"
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/Sources/RenderKitScratch/Scratch.swift:
--------------------------------------------------------------------------------
1 | import RenderKit
2 | import RenderKitShaders
3 | import simd
4 | import SIMDSupport
5 |
6 | extension SIMD3 where Scalar == Float {
7 | func dot(_ other: Self) -> Float {
8 | simd_dot(self, other)
9 | }
10 | }
11 |
12 | extension SimpleVertex {
13 | init(position: SIMD3, normal: SIMD3) {
14 | self.init(position: position, normal: normal, textureCoordinate: .zero)
15 | }
16 | }
17 |
18 | extension Plane: CustomStringConvertible {
19 | public var description: String {
20 | return "Plane(normal: \(normal.x), \(normal.y), \(normal.z), w: \(w))"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/RenderKitScratch/Shapes+Intersections.swift:
--------------------------------------------------------------------------------
1 | import simd
2 |
3 | public extension LineSegment3D {
4 | func intersection(with other: LineSegment3D) -> LineSegment3D? {
5 | // https://stackoverflow.com/a/565282/273118
6 | let p = start
7 | let q = other.start
8 | let r = direction
9 | let s = other.direction
10 |
11 | let rCrossS = simd.cross(r, s)
12 | let qMinusP = q - p
13 | let qMinusPCrossR = simd.cross(qMinusP, r)
14 |
15 | let t = simd.dot(qMinusPCrossR, rCrossS) / simd.dot(rCrossS, rCrossS)
16 | let u = simd.dot(qMinusPCrossR, rCrossS) / simd.dot(rCrossS, rCrossS)
17 |
18 | if t >= 0, t <= 1, u >= 0, u <= 1 {
19 | return LineSegment3D(start: start + t * direction, end: start + t * direction)
20 | }
21 | else {
22 | return nil
23 | }
24 | }
25 | }
26 |
27 | public extension Line3D {
28 | enum LineSphereIntersection {
29 | case once(SIMD3)
30 | case twice(SIMD3, SIMD3)
31 | }
32 |
33 | func intersects(sphere: Sphere) -> LineSphereIntersection? {
34 | fatalError()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/RenderKitScratch/Shapes+ShapeScript.swift:
--------------------------------------------------------------------------------
1 | public protocol ShapeScriptEncodable {
2 | func encodeToShapeScript() throws -> String
3 | }
4 |
5 | // https://shapescript.info/mac/
6 | extension Sphere: ShapeScriptEncodable {
7 | public func encodeToShapeScript() throws -> String {
8 | """
9 | sphere {
10 | size \(radius * 2)
11 | position \(center.x) \(center.y) \(center.z)
12 | detail 36
13 | }
14 | """
15 | }
16 | }
17 |
18 | extension LineSegment3D: ShapeScriptEncodable {
19 | public func encodeToShapeScript() throws -> String {
20 | """
21 | path {
22 | point \(start.x) \(start.y) \(start.z)
23 | point \(end.x) \(end.y) \(end.z)
24 | }
25 | """
26 | }
27 | }
28 |
29 | extension Line3D: ShapeScriptEncodable {
30 | public func encodeToShapeScript() throws -> String {
31 | let segment = LineSegment3D(start: point + -direction * 1000, end: point + direction * 1000)
32 | return try segment.encodeToShapeScript()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/CheckerBoardCompute.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "include/Shaders.h"
4 |
5 | using namespace metal;
6 |
7 | kernel void checkerboard(
8 | uint2 gid [[thread_position_in_grid]],
9 | uint2 grid_size [[grid_size]],
10 | texture2d outputTexture [[texture(CheckerBoardComputeBindings_OutputTexture)]],
11 | constant float2 &size [[buffer(CheckerBoardComputeBindings_SizeBuffer)]])
12 | {
13 | float2 t = fmod(float2(gid), size) / size;
14 |
15 | float v1 = t.x >= 0.5 ? 1.0 : 0;
16 | float v2 = t.y >= 0.5 ? v1 : 1 - v1;
17 |
18 | outputTexture.write(float4(v2, 0, v2, 1), gid);
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/GPULifeKernel.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "include/Shaders.h"
5 |
6 | using namespace metal;
7 |
8 | bool rules(uint count, bool alive) {
9 | if (alive == true && (count == 2 || count == 3)) {
10 | return true;
11 | }
12 | else if (alive == false && count == 3) {
13 | return true;
14 | }
15 | else if (alive == true) {
16 | return false;
17 | }
18 | else {
19 | return false;
20 | }
21 | }
22 |
23 | kernel void gpuLifeKernelWrap(
24 | uint2 gid [[thread_position_in_grid]],
25 | texture2d inputTexture [[texture(GPULifeKernelBindings_InputTexture)]],
26 | texture2d outputTexture [[texture(GPULifeKernelBindings_OutputTexture)]]) {
27 |
28 | const int2 inputTextureSize = int2(inputTexture.get_width(), inputTexture.get_height());
29 | if (int(gid.x) >= inputTextureSize.x || int(gid.y) >= inputTextureSize.y) {
30 | return;
31 | }
32 |
33 | const int2 positions[] = {
34 | int2(-1, -1),
35 | int2( 0, -1),
36 | int2(+1, -1),
37 | int2(-1, 0),
38 | int2(+1, 0),
39 | int2(-1, +1),
40 | int2( 0, +1),
41 | int2(+1, +1),
42 | };
43 | uint count = 0;
44 |
45 | for (int N = 0; N != 8; ++N) {
46 | int2 position = int2(gid) + positions[N];
47 | position.x = (position.x + inputTextureSize.x) % inputTextureSize.x;
48 | position.y = (position.y + inputTextureSize.y) % inputTextureSize.y;
49 | count += inputTexture.read(uint2(position)).r ? 1 : 0;
50 | }
51 |
52 | const bool alive = inputTexture.read(gid).r != 0;
53 |
54 | outputTexture.write(rules(count, alive), gid);
55 | }
56 |
57 | kernel void gpuLifeKernalNoWrap(
58 | uint2 gid [[thread_position_in_grid]],
59 | texture2d inputTexture [[texture(GPULifeKernelBindings_InputTexture)]],
60 | texture2d outputTexture [[texture(GPULifeKernelBindings_OutputTexture)]]) {
61 |
62 | const int2 sgid = int2(gid);
63 | const int2 inputTextureSize = int2(inputTexture.get_width(), inputTexture.get_height());
64 | if (sgid.x >= inputTextureSize.x || sgid.y >= inputTextureSize.y) {
65 | return;
66 | }
67 |
68 | uint count = 0;
69 |
70 | const int2 positions[] = {
71 | int2(-1, -1),
72 | int2( 0, -1),
73 | int2(+1, -1),
74 | int2(-1, 0),
75 | int2(+1, 0),
76 | int2(-1, +1),
77 | int2( 0, +1),
78 | int2(+1, +1),
79 | };
80 |
81 |
82 | for (int N = 0; N != 8; ++N) {
83 | int2 position = sgid + positions[N];
84 |
85 | if (position.x < 0 || position.x >= inputTextureSize.x) {
86 | continue;
87 | }
88 | else if (position.y < 0 || position.y >= inputTextureSize.y) {
89 | continue;
90 | }
91 | count += inputTexture.read(uint2(position)).r ? 1 : 0;
92 | }
93 |
94 | const bool alive = inputTexture.read(gid).r != 0;
95 |
96 | outputTexture.write(rules(count, alive), gid);
97 | }
98 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/ParticleShaders.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include "include/Shaders.h"
3 | #include "include/ParticleShaders.h"
4 | #include "include/Random.h"
5 |
6 | using namespace metal;
7 |
8 | float3 apply_forces(float3 vel, float drag, float mass)
9 | {
10 | float3 grav_acc = float3{0.0, -9.81, 0 }; // 9.81m/s^2 down in the Z-axis
11 | float3 drag_force = 0.5 * drag * (vel * abs(vel)); // D = 0.5 * (rho * C * Area * vel^2)
12 | float3 drag_acc = drag_force / mass; // a = F/m
13 | return grav_acc - drag_acc;
14 | }
15 |
16 | // MARK: -
17 |
18 | [[kernel]]
19 | void particleUpdate(
20 | uint gid [[thread_position_in_grid]],
21 | device Particle *particles[[buffer(ParticleShadersBindings_ParticlesBuffer)]],
22 | constant ParticlesEnvironment &environment [[buffer(ParticleShadersBindings_EnvironmentBuffer)]]
23 | )
24 | {
25 | auto particle = particles[gid];
26 | if (particle.age >= particle.lifetime || particle.position.y < 0) {
27 | particle.age = 0;
28 | particle.position = float3(0, 0, 0);
29 | particle.oldPosition = float3(0, 0, 0);
30 | particle.acceleration = (rand1dTo3d(gid) + float3(-0.5, 0, -0.5)) * float3(200, 2000, 200);
31 | }
32 | const auto temp = particle.position;
33 | const float timestep = environment.timestep;
34 | particle.position += particle.position - particle.oldPosition + particle.acceleration * timestep * timestep;
35 | particle.oldPosition = temp;
36 | particle.age += timestep;
37 | particle.acceleration = apply_forces(particle.position - particle.oldPosition, 0.1, 1);
38 | particles[gid] = particle;
39 | }
40 |
41 | // MARK: -
42 |
43 | struct Fragment
44 | {
45 | float4 position [[position]];
46 | float2 textureCoordinate;
47 | uint particle_id;
48 | };
49 |
50 | [[vertex]]
51 | Fragment classicParticleVertexShader(
52 | Vertex in [[stage_in]],
53 | constant Transforms &transforms [[buffer(CommonBindings_TransformsBuffer)]],
54 | device const Particle *particles[[buffer(ParticleShadersBindings_ParticlesBuffer)]],
55 | uint iid [[instance_id]])
56 | {
57 | const auto particle = particles[iid];
58 | Fragment out;
59 | out.position = transforms.projection * transforms.modelView * float4(in.position + particle.position, 1.0);
60 | out.textureCoordinate = in.textureCoordinate;
61 | out.particle_id = iid;
62 | return out;
63 | }
64 |
65 | [[fragment]]
66 | float4 classicParticleFragmentShader(
67 | Fragment in [[stage_in]],
68 | device const Particle *particles[[buffer(ParticleShadersBindings_ParticlesBuffer)]]
69 | )
70 | {
71 | const auto particle = particles[in.particle_id];
72 | if (particle.age >= particle.lifetime) {
73 | discard_fragment();
74 | }
75 | return float4(1, 0, 1, 1) * 1 - (particle.age / particle.lifetime);
76 | }
77 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/ShaderToy.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "include/Shaders.h"
4 |
5 | using namespace metal;
6 |
7 | struct Fragment
8 | {
9 | float4 position [[position]];
10 | float2 textureCoordinate;
11 | };
12 |
13 | vertex Fragment ShaderToy_VertexShader(
14 | Vertex in [[stage_in]],
15 | constant Transforms &transforms [[buffer(CommonBindings_TransformsBuffer)]])
16 | {
17 | Fragment out;
18 | out.position = transforms.projection * transforms.modelView * float4(in.position, 1.0);
19 | out.position.z -= 0.001; // TODO: Parameterize
20 | out.textureCoordinate = in.textureCoordinate;
21 | return out;
22 | }
23 |
24 | float plot(float2 st) {
25 | return smoothstep(0.02, 0.0, abs(st.y - st.x));
26 | }
27 |
28 | fragment float4 ShaderToy_FragmentShader(
29 | Fragment in [[stage_in]],
30 | constant FrameState &frameState [[buffer(CommonBindings_FrameStateBuffer)]])
31 | {
32 | float2 st = in.textureCoordinate;
33 | float y = st.x;
34 | float3 color = float3(y);
35 | float pct = plot(st);
36 | color = (1.0-pct)*color+pct*float3(0.0,1.0,0.0);
37 | return float4(color,1.0);
38 |
39 | //return float4(sin(frameState.time * 4) / 2 + 0.5, 0, 0, 1);
40 | //return float4(in.textureCoordinate.x, 0, 0, 1);
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/Support.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "include/Common.h"
4 | #include "include/Support.h"
5 |
6 | using namespace metal;
7 |
8 | float3 GammaCorrect(float3 color, float screenGamma) {
9 | // apply gamma correction (assume ambientColor, diffuseColor and specularColor have been linearized, i.e. have no gamma correction in them)
10 | const float3 colorGammaCorrected = pow(color, float3(1.0 / screenGamma));
11 | // use the gamma corrected color in the fragment
12 | return colorGammaCorrected;
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/include/Bindings.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Common.h"
4 |
5 | typedef NS_ENUM(NSInteger, CommonBindings) {
6 | CommonBindings_VerticesBuffer = 0,
7 | CommonBindings_FrameStateBuffer = 1,
8 | CommonBindings_TransformsBuffer = 2,
9 | };
10 |
11 | typedef NS_ENUM(NSInteger, SamplerBindings) {
12 | UnlitBaseColorSamplerIndex = 3
13 | };
14 |
15 | typedef NS_ENUM(NSInteger, BlinnPhongBindings) {
16 | BlinnPhongBindings_LightingModelArgumentBuffer = 4,
17 | BlinnPhongBindings_MaterialArgumentBuffer = 5,
18 | BlinnPhongBindings_BlinnPhongModeConstant = 6
19 | };
20 |
21 | typedef NS_ENUM(NSInteger, UnlitShaderBindings) {
22 | UnlitShaderBindings_BaseColorTexture = 7,
23 | UnlitShaderBindings_BaseColorSampler = 8,
24 | UnlitShaderBindings_OffsetsBuffer = 9,
25 | };
26 |
27 | typedef NS_ENUM(NSInteger, CheckerBoardComputeBindings) {
28 | CheckerBoardComputeBindings_OutputTexture = 10,
29 | CheckerBoardComputeBindings_SizeBuffer = 11,
30 | };
31 |
32 | typedef NS_ENUM(NSInteger, VoronoiNoiseComputeBindings) {
33 | VoronoiNoiseComputeBindings_OutputTexture = 12,
34 | VoronoiNoiseComputeBindings_OffsetBuffer = 13,
35 | VoronoiNoiseComputeBindings_SizeBuffer = 14,
36 | VoronoiNoiseComputeBindings_ModeBuffer = 15,
37 | };
38 |
39 |
40 | typedef NS_ENUM(NSInteger, SimplexNoise2DBindings) {
41 | SimplexNoise2DBindings_OutputTexture = 16,
42 | };
43 |
44 |
45 | typedef NS_ENUM(NSInteger, GPULifeKernelBindings) {
46 | GPULifeKernelBindings_InputTexture = 17,
47 | GPULifeKernelBindings_OutputTexture = 18,
48 | };
49 |
50 |
51 | typedef NS_ENUM(NSInteger, VoxelsBindings) {
52 | VoxelsBindings_VoxelsBuffer = 19,
53 | VoxelsBindings_ColorPaletteTexture = 20,
54 | VoxelsBindings_OutputTexture = 21,
55 | VoxelsBindings_VoxelSizeBuffer = 22,
56 | VoxelsBindings_VerticesBuffer = 23,
57 | VoxelsBindings_IndicesBuffer = 24,
58 | VoxelsBindings_BlinnPhongLightingModelArgumentBuffer = BlinnPhongBindings_LightingModelArgumentBuffer,
59 | };
60 |
61 | typedef NS_ENUM(NSInteger, DebugShaderBindings) {
62 | DebugShaderBindings_ModeBuffer = 25,
63 | };
64 |
65 | typedef NS_ENUM(NSInteger, ParticleShadersBindings) {
66 | ParticleShadersBindings_EnvironmentBuffer = 26,
67 | ParticleShadersBindings_ParticlesBuffer = 27,
68 | };
69 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/include/BlinnPhongShaders.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef __METAL_VERSION__
4 | #include
5 | using namespace metal;
6 | #endif
7 |
8 | #import
9 |
10 | #import "Common.h"
11 |
12 | struct BlinnPhongLight {
13 | simd_float3 lightPosition;
14 | simd_float3 lightColor;
15 | float lightPower;
16 | };
17 |
18 | #ifdef __METAL_VERSION__
19 | struct BlinnPhongMaterialArgumentBuffer {
20 | texture2d ambientTexture;
21 | sampler ambientSampler;
22 | texture2d diffuseTexture;
23 | sampler diffuseSampler;
24 | texture2d specularTexture;
25 | sampler specularSampler;
26 | float shininess;
27 | };
28 |
29 | struct BlinnPhongLightingModelArgumentBuffer {
30 | float screenGamma; // TODO: Move
31 | int lightCount;
32 | simd_float3 ambientLightColor; // TODO
33 | device BlinnPhongLight *lights;
34 | };
35 |
36 | float3 CalculateBlinnPhong(float3 modelPosition,
37 | float3 interpolatedNormal,
38 | constant BlinnPhongLightingModelArgumentBuffer &lightingModel,
39 | float shininess,
40 | float3 ambientColor,
41 | float3 diffuseColor,
42 | float3 specularColor
43 | );
44 | #endif
45 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/include/Common.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef __METAL_VERSION__
4 | #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
5 | #define NSInteger metal::int32_t
6 | #include
7 | #else
8 | #import
9 | #endif
10 |
11 | #import
12 |
13 | struct FrameState {
14 | // simd_float4x4 projection; // camera space -> clip space
15 | // simd_float2 drawableSize;
16 | float time;
17 | // float lastTime;
18 | long frame;
19 | float desiredFPS;
20 | // apply gamma correction (assume ambientColor, diffuseColor and specularColor have been linearized, i.e. have no gamma correction in them)
21 | // Assume the monitor is calibrated to the sRGB color space
22 | float screenGamma; // 2.2
23 | };
24 |
25 |
26 | struct Transforms { // Rename to ModelState?
27 | simd_float4x4 modelView; // model space -> camera space
28 | simd_float3x3 modelNormal; // model space - used for non-uniform scaled normal transformation. See https://www.youtube.com/watch?v=esC1HnyD9Bk&list=PLplnkTzzqsZS3R5DjmCQsqupu43oS9CFN
29 | simd_float4x4 projection; // Move to WorldState?
30 | };
31 |
32 | struct Vertex {
33 | #ifdef __METAL_VERSION__
34 | float3 position [[attribute(0)]];
35 | float3 normal [[attribute(1)]];
36 | float2 textureCoordinate [[attribute(2)]];
37 | #else
38 | simd_float3 position;
39 | simd_float3 normal;
40 | simd_float2 textureCoordinate;
41 | // tangents etc
42 | #endif
43 | };
44 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/include/GLSLCompat.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef __METAL_VERSION__
4 | #include
5 | #endif
6 |
7 | using namespace metal;
8 |
9 | // GLSL Compat
10 |
11 | typedef float2 vec2;
12 | typedef float3 vec3;
13 | typedef float4 vec4;
14 |
15 | inline float2 mod(float2 lhs, float rhs) {
16 | return fmod(lhs, rhs);
17 | }
18 |
19 | inline float3 mod(float3 lhs, float rhs) {
20 | return fmod(lhs, rhs);
21 | }
22 |
23 | inline float3 mod(float3 lhs, float3 rhs) {
24 | return fmod(lhs, rhs);
25 | }
26 |
27 | inline float3 frac(float3 v) {
28 | return fract(v);
29 | }
30 |
31 | inline float2 frac(float2 v) {
32 | return fract(v);
33 | }
34 |
35 | inline float frac(float v) {
36 | return fract(v);
37 | }
38 |
39 |
40 | // MARK: -
41 |
42 | //inline float random (vec2 st) {
43 | // return fract(sin(dot(st.xy,
44 | // vec2(12.9898,78.233)))*
45 | // 43758.5453123);
46 | //}
47 |
48 | //// MARK: -
49 | //
50 | //inline float rand2dTo1d(vec2 input) {
51 | // return random(input);
52 | //}
53 | //
54 | ////inline float rand3dTo1d(vec3 input) {
55 | //// return random(input);
56 | ////}
57 | //
58 | //inline vec2 rand2dTo2d(vec2 input) {
59 | // auto x = random(vec2(input.x * 2, input.y));
60 | // auto y = random(vec2(input.x * 2 + 1, input.y));
61 | // return vec2(x, y);
62 | //}
63 | //
64 | //inline float3 rand1dTo3d(float input) {
65 | // return float3(
66 | // random(input * 3),
67 | // random(input * 3 + 1),
68 | // random(input * 3 + 2)
69 | // );
70 | //}
71 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/include/ParticleShaders.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #import
4 |
5 | struct Particle {
6 | simd_float3 position;
7 | simd_float3 oldPosition;
8 | simd_float3 acceleration;
9 | float age;
10 | float lifetime;
11 | };
12 |
13 | struct ParticlesEnvironment {
14 | simd_float3 gravity;
15 | float timestep;
16 | };
17 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/include/Random.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "GLSLCompat.h"
4 |
5 | // https://www.ronja-tutorials.com/post/024-white-noise/
6 |
7 | //get a scalar random value from a 3d value
8 | inline float rand3dTo1d(float3 value, float3 dotDir = float3(12.9898, 78.233, 37.719)){
9 | //make value smaller to avoid artefacts
10 | float3 smallValue = sin(value);
11 | //get scalar value from 3d vector
12 | float random = dot(smallValue, dotDir);
13 | //make value more random by making it bigger and then taking the factional part
14 | random = frac(sin(random) * 143758.5453);
15 | return random;
16 | }
17 |
18 | inline float rand2dTo1d(float2 value, float2 dotDir = float2(12.9898, 78.233)){
19 | float2 smallValue = sin(value);
20 | float random = dot(smallValue, dotDir);
21 | random = frac(sin(random) * 143758.5453);
22 | return random;
23 | }
24 |
25 | inline float rand1dTo1d(float3 value, float mutator = 0.546){
26 | float3 random = frac(sin(value + mutator) * 143758.5453);
27 | return random.x;
28 | }
29 |
30 | //to 2d functions
31 |
32 | inline float2 rand3dTo2d(float3 value){
33 | return float2(
34 | rand3dTo1d(value, float3(12.989, 78.233, 37.719)),
35 | rand3dTo1d(value, float3(39.346, 11.135, 83.155))
36 | );
37 | }
38 |
39 | inline float2 rand2dTo2d(float2 value){
40 | return float2(
41 | rand2dTo1d(value, float2(12.989, 78.233)),
42 | rand2dTo1d(value, float2(39.346, 11.135))
43 | );
44 | }
45 |
46 | inline float2 rand1dTo2d(float value){
47 | return float2(
48 | rand2dTo1d(value, 3.9812),
49 | rand2dTo1d(value, 7.1536)
50 | );
51 | }
52 |
53 | //to 3d functions
54 |
55 | inline float3 rand3dTo3d(float3 value){
56 | return float3(
57 | rand3dTo1d(value, float3(12.989, 78.233, 37.719)),
58 | rand3dTo1d(value, float3(39.346, 11.135, 83.155)),
59 | rand3dTo1d(value, float3(73.156, 52.235, 09.151))
60 | );
61 | }
62 |
63 | inline float3 rand2dTo3d(float2 value){
64 | return float3(
65 | rand2dTo1d(value, float2(12.989, 78.233)),
66 | rand2dTo1d(value, float2(39.346, 11.135)),
67 | rand2dTo1d(value, float2(73.156, 52.235))
68 | );
69 | }
70 |
71 | inline float3 rand1dTo3d(float value){
72 | return float3(
73 | rand1dTo1d(value, 3.9812),
74 | rand1dTo1d(value, 7.1536),
75 | rand1dTo1d(value, 5.7241)
76 | );
77 | }
78 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/include/Shaders.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | // This is the main umbrella header for shaders module
4 |
5 | #include "Common.h"
6 | #include "BlinnPhongShaders.h"
7 | #include "Voxels.h"
8 | #include "Bindings.h"
9 | #include "Support.h"
10 | #include "ParticleShaders.h"
11 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/include/Support.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #import
4 |
5 | #ifdef __METAL_VERSION__
6 |
7 | #include
8 |
9 | using namespace metal;
10 |
11 | extern float3 GammaCorrect(float3 color, float screenGamma);
12 | #endif
13 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Classic/include/Voxels.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef __METAL_VERSION__
4 | #include
5 | #else
6 | #import
7 | #endif
8 |
9 | #import
10 |
11 | struct VoxelVertex {
12 | #ifdef __METAL_VERSION__
13 | half3 position [[attribute(0)]]; // 6 bytes
14 | half3 normal [[attribute(1)]]; // 6 bytes
15 | half2 textureCoordinate [[attribute(2)]]; // 4 bytes
16 | ushort colorIndex [[attribute(3)]]; // 2 bytes
17 | ushort unused [[attribute(4)]];
18 | #else
19 | // simd_half3 position;
20 | // simd_half3 normal;
21 | // simd_half2 textureCoordinate;
22 | // unsigned short colorIndex
23 | // unsigned short unused;
24 | // tangents etc
25 | #endif
26 | };
27 |
28 | // PackedVoxelVertex and VoxelVertex have to have the same structure
29 | #ifdef __METAL_VERSION__
30 | struct PackedVoxelVertex {
31 | packed_half3 position;
32 | packed_half3 normal;
33 | packed_half2 textureCoordinate;
34 | ushort colorIndex;
35 | ushort unused;
36 | };
37 |
38 | struct MagicaVoxel {
39 | uchar3 position;
40 | uchar color;
41 | };
42 | #endif
43 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Empty.c:
--------------------------------------------------------------------------------
1 | // This file intentionally left empty.
2 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/FlatShader.metal:
--------------------------------------------------------------------------------
1 | #import "include/RenderKitShaders.h"
2 | #import "include/FlatShader.h"
3 |
4 | typedef SimpleVertex Vertex;
5 |
6 | struct Fragment {
7 | float4 position [[position]]; // in projection space
8 | float3 modelPosition;
9 | float3 interpolatedNormalFlat[[flat]];
10 | float3 interpolatedNormal;
11 | float2 textureCoordinate;
12 | ushort instance_id[[flat]];
13 | };
14 |
15 | // MARK: -
16 |
17 | [[vertex]]
18 | Fragment flatShaderVertexShader(
19 | Vertex in [[stage_in]],
20 | ushort instance_id[[instance_id]],
21 | constant CameraUniforms &camera [[buffer(1)]],
22 | constant ModelTransforms *modelTransforms [[buffer(2)]]
23 | )
24 | {
25 | const ModelTransforms modelTransform = modelTransforms[instance_id];
26 | const float4 modelVertex = modelTransform.modelViewMatrix * float4(in.position, 1.0);
27 | return {
28 | .position = camera.projectionMatrix * modelVertex,
29 | .modelPosition = float3(modelVertex) / modelVertex.w,
30 | .interpolatedNormalFlat = modelTransform.modelNormalMatrix * in.normal,
31 | .interpolatedNormal = modelTransform.modelNormalMatrix * in.normal,
32 | .textureCoordinate = in.textureCoordinate,
33 | .instance_id = instance_id
34 | };
35 | }
36 |
37 | [[fragment]]
38 | vector_float4 flatShaderFragmentShader(
39 | Fragment in [[stage_in]],
40 | constant LightUniforms &lighting [[buffer(1)]],
41 | constant FlatMaterial *materials [[buffer(2)]],
42 | array, 128> textures [[texture(0)]]
43 | )
44 | {
45 | auto material = materials[in.instance_id];
46 | const auto diffuseMaterialColor = material.diffuseColor.xyz;
47 | const auto ambientMaterialColor= material.ambientColor.xyz;
48 |
49 | // Compute diffuse color
50 | const auto normal = normalize(in.interpolatedNormalFlat);
51 | const auto lightDirection = lighting.lightPosition - in.modelPosition;
52 | const auto lightDistanceSquared = length_squared(lightDirection);
53 | const auto lambertian = max(dot(lightDirection, normal), 0.0);
54 | const auto diffuseColor = diffuseMaterialColor * lambertian * lighting.lightColor * lighting.lightPower / lightDistanceSquared;
55 |
56 | // Compute ambient color
57 | const auto ambientColor = lighting.ambientLightColor * ambientMaterialColor;
58 |
59 | return float4(diffuseColor + ambientColor, 1.0);
60 | }
61 |
62 | // https://en.wikipedia.org/wiki/Blinn–Phong_reflection_model
63 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/GridShader.metal:
--------------------------------------------------------------------------------
1 | #import "include/RenderKitShaders.h"
2 | #import "include/GraphToy.h"
3 |
4 | using namespace metal;
5 | using namespace graphtoy;
6 |
7 | struct Fragment {
8 | float4 position [[position]]; // in projection space
9 | float3 modelPosition;
10 | float2 textureCoordinate;
11 | };
12 |
13 | // MARK: -
14 |
15 | [[vertex]]
16 | Fragment gridVertexShader(
17 | SimpleVertex in [[stage_in]],
18 | ushort instance_id[[instance_id]],
19 | constant CameraUniforms &cameraUniforms [[buffer(1)]],
20 | constant ModelTransforms *instancedModelUniforms [[buffer(2)]]
21 | )
22 | {
23 | const ModelTransforms modelUniforms = instancedModelUniforms[instance_id];
24 | const float4 modelVertex = modelUniforms.modelViewMatrix * float4(in.position, 1.0);
25 | return {
26 | .position = cameraUniforms.projectionMatrix * modelVertex,
27 | .modelPosition = float3(modelVertex) / modelVertex.w,
28 | .textureCoordinate = in.textureCoordinate,
29 | };
30 | }
31 |
32 | #define ddx dfdx
33 | #define ddy dfdy
34 | #define frac fract
35 |
36 | // https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8
37 | [[fragment]]
38 | float4 gridFragmentShader(
39 | Fragment in [[stage_in]])
40 | {
41 | auto uv = in.textureCoordinate;
42 | const float2 lineWidth = {1.0, 1.0 };
43 | float4 uvDDXY = float4(ddx(uv), ddy(uv)); //
44 | float2 uvDeriv = float2(length(uvDDXY.xz), length(uvDDXY.yw)); //
45 | bool2 invertLine = lineWidth > 0.5;
46 | float2 targetWidth = (invertLine.x || invertLine.y) ? 1.0 - lineWidth : lineWidth;
47 | float2 drawWidth = clamp(targetWidth, uvDeriv, 0.5);
48 | float2 lineAA = uvDeriv * 1.5;
49 | float2 gridUV = abs(frac(uv) * 2.0 - 1.0);
50 | gridUV = (invertLine.x || invertLine.y) ? gridUV : 1.0 - gridUV;
51 | float2 grid2 = smoothstep(drawWidth + lineAA, drawWidth - lineAA, gridUV);
52 | grid2 *= saturate(targetWidth / drawWidth);
53 | grid2 = lerp(grid2, targetWidth, saturate(uvDeriv * 2.0 - 1.0));
54 | grid2 = (invertLine.x || invertLine.y) ? 1.0 - grid2 : grid2;
55 | float grid = lerp(grid2.x, 1.0, grid2.y);
56 |
57 |
58 | return { grid, grid, grid, 1.0 };
59 | }
60 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Noise.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include "include/GLSLCompat.h"
3 | //#include "include/Shaders.h"
4 |
5 | using namespace metal;
6 | using namespace glslCompatible;
7 |
8 | inline vec3 mod289(vec3 x) {
9 | return x - floor(x * (1.0 / 289.0)) * 289.0;
10 | }
11 |
12 | inline vec2 mod289(vec2 x) {
13 | return x - floor(x * (1.0 / 289.0)) * 289.0;
14 | }
15 |
16 | inline vec3 permute(vec3 x) {
17 | return mod289(((x*34.0)+10.0)*x);
18 | }
19 |
20 | float snoise(vec2 v)
21 | {
22 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
23 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
24 | -0.577350269189626, // -1.0 + 2.0 * C.x
25 | 0.024390243902439); // 1.0 / 41.0
26 | // First corner
27 | vec2 i = floor(v + dot(v, C.yy) );
28 | vec2 x0 = v - i + dot(i, C.xx);
29 |
30 | // Other corners
31 | vec2 i1;
32 | //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
33 | //i1.y = 1.0 - i1.x;
34 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
35 | // x0 = x0 - 0.0 + 0.0 * C.xx ;
36 | // x1 = x0 - i1 + 1.0 * C.xx ;
37 | // x2 = x0 - 1.0 + 2.0 * C.xx ;
38 | vec4 x12 = x0.xyxy + C.xxzz;
39 | x12.xy -= i1;
40 |
41 | // Permutations
42 | i = mod289(i); // Avoid truncation effects in permutation
43 | vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
44 | + i.x + vec3(0.0, i1.x, 1.0 ));
45 |
46 | vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
47 | m = m*m ;
48 | m = m*m ;
49 |
50 | // Gradients: 41 points uniformly over a line, mapped onto a diamond.
51 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
52 |
53 | vec3 x = 2.0 * fract(p * C.www) - 1.0;
54 | vec3 h = abs(x) - 0.5;
55 | vec3 ox = floor(x + 0.5);
56 | vec3 a0 = x - ox;
57 |
58 | // Normalise gradients implicitly by scaling m
59 | // Approximation of: m *= inversesqrt( a0*a0 + h*h );
60 | m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
61 |
62 | // Compute final noise value at P
63 | vec3 g;
64 | g.x = a0.x * x0.x + h.x * x0.y;
65 | g.yz = a0.yz * x12.xz + h.yz * x12.yw;
66 | return 130.0 * dot(m, g);
67 | }
68 |
69 | // MARK: -
70 |
71 | kernel void simplexNoise2D(uint2 gid [[thread_position_in_grid]], texture2d outputTexture [[texture(1)]]) {
72 | auto noise = snoise(float2(gid.x, gid.y));
73 | outputTexture.write(float4(noise, 0, 0, 1), gid);
74 | }
75 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/PanoramicShaders.metal:
--------------------------------------------------------------------------------
1 | #import "include/RenderKitShaders.h"
2 | #import "include/PanoramicShaders.h"
3 |
4 | typedef SimpleVertex Vertex;
5 |
6 | struct Fragment {
7 | float4 position [[position]]; // in projection space
8 | float2 textureCoordinate;
9 | };
10 |
11 | // MARK: -
12 |
13 | [[vertex]]
14 | Fragment panoramicVertexShader(
15 | Vertex in [[stage_in]],
16 | constant CameraUniforms &cameraUniforms [[buffer(1)]],
17 | constant float4x4 &modelViewMatrix [[buffer(2)]]
18 | )
19 | {
20 | const float4 modelVertex = modelViewMatrix * float4(in.position, 1.0);
21 | // const float4 modelVertex = float4(in.position, 1.0);
22 | return {
23 | .position = cameraUniforms.projectionMatrix * modelVertex,
24 | .textureCoordinate = in.textureCoordinate
25 | };
26 | }
27 |
28 | [[fragment]]
29 | vector_float4 panoramicFragmentShader(
30 | Fragment in [[stage_in]],
31 | constant PanoramaFragmentUniforms &uniforms[[buffer(0)]],
32 | array, 12> tiles [[texture(0)]]
33 | )
34 | {
35 | float4 color;
36 | // Special-case single texture panoramas.
37 | if (tiles.size() == 1) {
38 | const auto texture = tiles[0];
39 | color = texture.sample(RenderKitShaders::basicSampler, in.textureCoordinate);
40 | }
41 | else {
42 | const float2 denormalizedTextureCoordinate = in.textureCoordinate * float2(uniforms.gridSize);
43 | const ushort textureIndex = clamp(ushort(denormalizedTextureCoordinate.y) * uniforms.gridSize.x + ushort(denormalizedTextureCoordinate.x), 0, ushort(tiles.size()) - 1);
44 | const float2 textureCoordinate = denormalizedTextureCoordinate - floor(denormalizedTextureCoordinate);
45 | const auto texture = tiles[textureIndex];
46 | // const float d = 1.0 / float(texture.get_width());
47 | // if (textureCoordinate.x <= d || textureCoordinate.x >= 1 - d || textureCoordinate.y < d || textureCoordinate.y > 1 - d) {
48 | // return float4(1, 0, 0, 1);
49 | // }
50 | color = texture.sample(RenderKitShaders::basicSampler, textureCoordinate);
51 | }
52 | color *= uniforms.colorFactor;
53 | return color;
54 | }
55 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/ParticlesShader.metal:
--------------------------------------------------------------------------------
1 | #import "include/RenderKitShaders.h"
2 |
3 | typedef SimpleVertex Vertex;
4 |
5 | struct Fragment {
6 | float4 position [[position]]; // in projection space
7 | float2 textureCoordinate;
8 | };
9 |
10 | // MARK: -
11 |
12 | [[vertex]]
13 | Fragment particleVertexShader(
14 | Vertex in [[stage_in]],
15 | ushort instance_id[[instance_id]],
16 | constant CameraUniforms &cameraUniforms [[buffer(1)]],
17 | constant float2 *positions [[buffer(2)]]
18 | )
19 | {
20 | const float particleSize = 10.0;
21 |
22 | auto position = positions[instance_id];
23 | const float4 modelVertex = float4(in.position * particleSize * 2 + float3(position, 0), 1);
24 | return {
25 | .position = cameraUniforms.projectionMatrix * modelVertex,
26 | .textureCoordinate = in.textureCoordinate
27 | };
28 | }
29 |
30 | [[fragment]]
31 | vector_float4 particleFragmentShader(
32 | Fragment in [[stage_in]]
33 | )
34 | {
35 | const float d = 0.25 - distance_squared(in.textureCoordinate, float2(0.5, 0.5));
36 | return float4(1, 0, 0, d > 0 ? 1.0 : 0.0);
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/ReallyFlatShader.metal:
--------------------------------------------------------------------------------
1 | #import "include/RenderKitShaders.h"
2 |
3 | typedef SimpleVertex Vertex;
4 |
5 | struct Fragment {
6 | float4 position [[position]]; // in projection space
7 | };
8 |
9 | // MARK: -
10 |
11 | [[vertex]]
12 | Fragment reallyFlatVertexShader(
13 | Vertex in [[stage_in]],
14 | ushort instance_id[[instance_id]],
15 | constant CameraUniforms &cameraUniforms [[buffer(1)]],
16 | constant float2 &position [[buffer(2)]]
17 | )
18 | {
19 | const float4 modelVertex = float4(in.position + float3(position, 0), 1);
20 | return {
21 | .position = cameraUniforms.projectionMatrix * modelVertex,
22 | };
23 | }
24 |
25 | [[fragment]]
26 | vector_float4 reallyFlatFragmentShader(
27 | Fragment in [[stage_in]]
28 | )
29 | {
30 | return float4(1, 0, 0, 1);
31 | }
32 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Scratch/DemoBlit.metal:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | using namespace metal;
5 |
6 | struct Vertex {
7 | vector_float3 position [[attribute(0)]];
8 | vector_float3 normal [[attribute(1)]];
9 | vector_float2 textureCoords [[attribute(2)]];
10 | };
11 |
12 | struct DemoBlitVertexUniforms {
13 | simd_float3x4 transform;
14 | };
15 |
16 | struct Fragment {
17 | vector_float4 position [[position]];
18 | float2 textureCoords;
19 | };
20 |
21 | // MARK: -
22 |
23 | enum DemoBlitID {
24 | kDemoBlitID_Vertices = 0,
25 | kDemoBlitID_VertexShaderUniforms = 1,
26 | kDemoBlitID_BaseColorTexture = 0,
27 | kDemoBlitID_BaseColorSampler = 0,
28 | };
29 |
30 | // MARK: -
31 |
32 | [[vertex]]
33 | Fragment demoBlitVertexShader(
34 | Vertex vertexIn [[stage_in]],
35 | constant DemoBlitVertexUniforms &uniforms [[buffer(kDemoBlitID_VertexShaderUniforms)]]
36 | )
37 | {
38 | return {
39 | .position = vector_float4(vector_float4(vertexIn.position, 1.0) * uniforms.transform, 1.0),
40 | .textureCoords = vertexIn.textureCoords
41 | };
42 | }
43 |
44 | [[fragment]]
45 | vector_float4 demoBlitFragmentShader(
46 | Fragment fragmentIn [[stage_in]],
47 | texture2d texture [[texture(kDemoBlitID_BaseColorTexture)]],
48 | sampler sampler[[sampler(kDemoBlitID_BaseColorSampler)]]
49 | )
50 | {
51 | return texture.sample(sampler, fragmentIn.textureCoords);
52 | }
53 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/Scratch/MeshShaders.metal:
--------------------------------------------------------------------------------
1 | //
2 | //#include
3 | //#include
4 | //
5 | //using namespace metal;
6 | //
7 | //struct CurvePayload {
8 | //
9 | //};
10 | //
11 | //[[object]]
12 | //void objectShader(
13 | // object_data CurvePayload *payloadOutput [[payload]],
14 | // const device void *inputData [[buffer(0)]],
15 | // uint thread_index_in_threadgroup [[thread_index_in_threadgroup]],
16 | // uint triangleID [[threadgroup_position_in_grid]],
17 | // metal::mesh_grid_properties mgp
18 | // ushort instance_id [[instance_id]]
19 | //
20 | // )
21 | //{
22 | //// if (hairID < kHairsPerBlock)
23 | //// payloadOutput[hairID] = generateCurveData(inputData, hairID, triangleID);
24 | //// if (thread_index_in_threadgroup == 0)
25 | //// mgp.set_threadgroups_per_grid(uint3(kHairPerBlockX, kHairPerBlockY, 1));
26 | //}
27 | //
28 | //struct VertexData { float4 position [[position]]; };
29 | //struct PrimitiveData { float4 color; };
30 | //
31 | //using triangle_mesh_t = metal::mesh<
32 | // VertexData, // Vertex type
33 | // PrimitiveData, // Primitive type
34 | // 10, // Maximum vertices
35 | // 6, // Maximum primitives
36 | // metal::topology::triangle // Topology
37 | //>;
38 | //
39 | //[[mesh]] void myMeshShader(triangle_mesh_t outputMesh,
40 | // uint tid [[thread_index_in_threadgroup]])
41 | //{
42 | //
43 | //// if (tid < kVertexCount)
44 | //// outputMesh.set_vertex(tid, calculateVertex(tid));
45 | ////
46 | //// if (tid < kIndexCount)
47 | //// outputMesh.set_index(tid, calculateIndex(tid));
48 | ////
49 | //// if (tid < kPrimitiveCount)
50 | //// outputMesh.set_primitive(tid, calculatePrimitive(tid));
51 | ////
52 | ////if (tid == 0)
53 | //// outputMesh.set_primitive_count(kPrimitiveCount);
54 | //
55 | //}
56 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/UnlitShader.metal:
--------------------------------------------------------------------------------
1 | #import "include/RenderKitShaders.h"
2 | #import "include/UnlitShader.h"
3 |
4 | typedef SimpleVertex Vertex;
5 |
6 | struct Fragment {
7 | float4 position [[position]]; // in projection space
8 | float2 textureCoordinate;
9 | ushort instance_id [[flat]]; // TODO: is flat needed?
10 | };
11 |
12 | // MARK: -
13 |
14 | [[vertex]]
15 | Fragment unlitVertexShader(
16 | Vertex in [[stage_in]],
17 | ushort instance_id[[instance_id]],
18 | constant CameraUniforms &camera[[buffer(1)]],
19 | constant ModelTransforms *models[[buffer(2)]] // TODO: rename to modelTransforms
20 | )
21 | {
22 | const ModelTransforms model = models[instance_id];
23 | const float4 modelVertex = model.modelViewMatrix * float4(in.position, 1.0);
24 | return {
25 | .position = camera.projectionMatrix * modelVertex,
26 | .textureCoordinate = in.textureCoordinate,
27 | .instance_id = instance_id,
28 | };
29 | }
30 |
31 | [[fragment]]
32 | vector_float4 unlitFragmentShader(
33 | Fragment in [[stage_in]],
34 | constant UnlitMaterial *materials [[buffer(0)]],
35 | array, 1> textures [[texture(0)]]
36 | )
37 | {
38 | float4 color;
39 | auto material = materials[in.instance_id];
40 | if (material.textureIndex == -1) {
41 | color = materials[in.instance_id].color;
42 | }
43 | else {
44 | auto texture = textures[material.textureIndex];
45 | color = texture.sample(RenderKitShaders::basicSampler, in.textureCoordinate);
46 | }
47 | return color;
48 | }
49 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/VolumeShaders.metal:
--------------------------------------------------------------------------------
1 | #import "include/RenderKitShaders.h"
2 | #import "include/VolumeShaders.h"
3 |
4 | typedef SimpleVertex Vertex;
5 |
6 | struct VertexOut {
7 | float4 position [[position]]; // in projection space
8 | float3 textureCoordinate;
9 | };
10 | typedef VertexOut FragmentIn;
11 |
12 | // MARK: -
13 |
14 | [[vertex]]
15 | VertexOut volumeVertexShader(
16 | Vertex in [[stage_in]],
17 | constant CameraUniforms &cameraUniforms [[buffer(1)]],
18 | constant VolumeTransforms &transforms [[buffer(2)]],
19 | constant VolumeInstance *instances [[buffer(3)]],
20 | ushort instance_id [[instance_id]]
21 | )
22 | {
23 | const VolumeInstance instance = instances[instance_id];
24 | const float4 offset = float4(0, 0, instance.offsetZ, 1.0);
25 | const float3 textureCoordinate = float3(in.textureCoordinate, instance.textureZ);
26 |
27 | // Kill rotation from modelView matrix.
28 | // TODO: This also kills scale.
29 | float4x4 modelViewMatrix = transforms.modelViewMatrix;
30 | // TODO: Hack the scale in here until i can bothered to fix this.
31 | modelViewMatrix[0][0] = 2.0;
32 | modelViewMatrix[0][1] = 0.0;
33 | modelViewMatrix[0][2] = 0.0;
34 | modelViewMatrix[1][0] = 0.0;
35 | modelViewMatrix[1][1] = 2.0;
36 | modelViewMatrix[1][2] = 0.0;
37 | modelViewMatrix[2][0] = 0.0;
38 | modelViewMatrix[2][1] = 0.0;
39 | modelViewMatrix[2][2] = 2.0;
40 |
41 | float4x4 textureMatrix = transforms.textureMatrix;
42 | float3 rotatedTextureCoordinate = (textureMatrix * float4(textureCoordinate.xyz, 1.0)).xzy;
43 | const float4 modelVertex = modelViewMatrix * float4(in.position + offset.xyz, 1.0);
44 | const float4 clipSpace = cameraUniforms.projectionMatrix * modelVertex;
45 | return {
46 | .position = clipSpace,
47 | .textureCoordinate = rotatedTextureCoordinate,
48 | };
49 | }
50 |
51 | // MARK: -
52 |
53 | [[fragment]]
54 | float4 volumeFragmentShader(
55 | FragmentIn in [[stage_in]],
56 | texture3d texture [[texture(0)]],
57 | texture1d transferFunctionTexture [[texture(1)]],
58 | constant VolumeFragmentUniforms &uniforms [[buffer(0)]]
59 | )
60 | {
61 | if (
62 | in.textureCoordinate.x < 0 || in.textureCoordinate.x > 1.0
63 | || in.textureCoordinate.y < 0 || in.textureCoordinate.y > 1.0
64 | || in.textureCoordinate.z < 0 || in.textureCoordinate.z > 1.0
65 | ) {
66 | discard_fragment();
67 | }
68 | constexpr struct sampler basicSampler(coord::normalized, address::clamp_to_edge, filter::linear);
69 | const float normalizedValue = texture.sample(basicSampler, in.textureCoordinate).r / float(uniforms.maxValue);
70 | auto color = transferFunctionTexture.sample(basicSampler, normalizedValue);
71 |
72 | // Alpha adjusted by number of instances so we don't blow out the brightness.
73 | return color * float4(1, 1, 1, 1 / float(uniforms.instanceCount) * uniforms.alpha);
74 | }
75 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/immersiveShaders.metal:
--------------------------------------------------------------------------------
1 | #import "include/RenderKitShaders.h"
2 | #import "include/ImmersiveShaders.h"
3 |
4 | using namespace metal;
5 |
6 | typedef struct
7 | {
8 | float3 position [[attribute(VertexAttributePosition)]];
9 | float2 texCoord [[attribute(VertexAttributeTexcoord)]];
10 | } Vertex;
11 |
12 | typedef struct
13 | {
14 | float4 position [[position]];
15 | float2 texCoord;
16 | } VertexOut;
17 |
18 | typedef VertexOut ShaderIn;
19 |
20 | vertex VertexOut vertexShader(
21 | Vertex in [[stage_in]],
22 | ushort amp_id [[amplification_id]],
23 | constant UniformsArray &uniformsArray [[buffer(BufferIndexUniforms)]])
24 | {
25 | const Uniforms uniforms = uniformsArray.uniforms[amp_id];
26 | const float4 position = float4(in.position, 1.0);
27 | return {
28 | .position = uniforms.projectionMatrix * uniforms.modelViewMatrix * position,
29 | .texCoord = in.texCoord,
30 | };
31 | }
32 |
33 | fragment float4 fragmentShader(
34 | ShaderIn in [[stage_in]],
35 | texture2d colorMap [[texture(TextureIndexColor)]])
36 | {
37 | constexpr sampler colorSampler(mip_filter::linear, mag_filter::linear, min_filter::linear);
38 | half4 colorSample = colorMap.sample(colorSampler, in.texCoord.xy);
39 | return float4(colorSample);
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/include/CommonTypes.h:
--------------------------------------------------------------------------------
1 | #import "RenderKitShaders.h"
2 | #import "MetalSupport.h"
3 |
4 | #ifdef __METAL_VERSION__
5 | struct SimpleVertex {
6 | float3 position [[attribute(0)]];
7 | float3 normal [[attribute(1)]];
8 | float2 textureCoordinate [[attribute(2)]];
9 | };
10 | #else
11 | typedef struct {
12 | float x;
13 | float y;
14 | float z;
15 | } PackedFloat3;
16 |
17 | struct SimpleVertex {
18 | PackedFloat3 packedPosition;
19 | PackedFloat3 packedNormal;
20 | float2 textureCoordinate;
21 | };
22 | #endif
23 |
24 | struct ModelTransforms {
25 | float4x4 modelViewMatrix; // model space -> camera space
26 | float3x3 modelNormalMatrix; // model space - used for non-uniform scaled normal transformation. See https://www.youtube.com/watch?v=esC1HnyD9Bk&list=PLplnkTzzqsZS3R5DjmCQsqupu43oS9CFN
27 | };
28 |
29 | // TODO: DEPRECATE
30 | struct ModelUniforms {
31 | float4x4 modelViewMatrix; // model space -> camera space
32 | float3x3 modelNormalMatrix; // model space - used for non-uniform scaled normal transformation. See https://www.youtube.com/watch?v=esC1HnyD9Bk&list=PLplnkTzzqsZS3R5DjmCQsqupu43oS9CFN
33 | float4 color;
34 | };
35 |
36 | struct CameraUniforms {
37 | float4x4 projectionMatrix;
38 | };
39 |
40 | struct LightUniforms {
41 | // Per diffuse light
42 | float3 lightPosition;
43 | float3 lightColor;
44 | float lightPower;
45 | // Per environment
46 | float3 ambientLightColor;
47 | };
48 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/include/FlatShader.h:
--------------------------------------------------------------------------------
1 | #import "RenderKitShaders.h"
2 |
3 | // TODO: Rename
4 | struct FlatMaterial {
5 | float4 diffuseColor;
6 | short diffuseTextureIndex; // or -1 for no texture
7 | float4 ambientColor;
8 | short ambientTextureIndex; // or -1 for no texture
9 | };
10 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/include/GLSLCompat.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef __METAL_VERSION__
4 | #include
5 | #endif
6 |
7 | using namespace metal;
8 |
9 | namespace glslCompatible {
10 | // GLSL Compat
11 |
12 | typedef float2 vec2;
13 | typedef float3 vec3;
14 | typedef float4 vec4;
15 |
16 | inline float2 mod(float2 lhs, float rhs) {
17 | return fmod(lhs, rhs);
18 | }
19 |
20 | inline float3 mod(float3 lhs, float rhs) {
21 | return fmod(lhs, rhs);
22 | }
23 |
24 | inline float3 mod(float3 lhs, float3 rhs) {
25 | return fmod(lhs, rhs);
26 | }
27 |
28 | inline float3 frac(float3 v) {
29 | return fract(v);
30 | }
31 |
32 | inline float2 frac(float2 v) {
33 | return fract(v);
34 | }
35 |
36 | inline float frac(float v) {
37 | return fract(v);
38 | }
39 |
40 |
41 | // MARK: -
42 |
43 | //inline float random (vec2 st) {
44 | // return fract(sin(dot(st.xy,
45 | // vec2(12.9898,78.233)))*
46 | // 43758.5453123);
47 | //}
48 |
49 | //// MARK: -
50 | //
51 | //inline float rand2dTo1d(vec2 input) {
52 | // return random(input);
53 | //}
54 | //
55 | ////inline float rand3dTo1d(vec3 input) {
56 | //// return random(input);
57 | ////}
58 | //
59 | //inline vec2 rand2dTo2d(vec2 input) {
60 | // auto x = random(vec2(input.x * 2, input.y));
61 | // auto y = random(vec2(input.x * 2 + 1, input.y));
62 | // return vec2(x, y);
63 | //}
64 | //
65 | //inline float3 rand1dTo3d(float input) {
66 | // return float3(
67 | // random(input * 3),
68 | // random(input * 3 + 1),
69 | // random(input * 3 + 2)
70 | // );
71 | //}
72 | }
73 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/include/ImmersiveShaders.h:
--------------------------------------------------------------------------------
1 | #import "MetalSupport.h"
2 |
3 | typedef NS_ENUM(NSInteger, BufferIndex)
4 | {
5 | BufferIndexMeshPositions = 0,
6 | BufferIndexMeshGenerics = 1,
7 | BufferIndexUniforms = 2
8 | };
9 |
10 | typedef NS_ENUM(NSInteger, VertexAttribute)
11 | {
12 | VertexAttributePosition = 0,
13 | VertexAttributeTexcoord = 1,
14 | };
15 |
16 | typedef NS_ENUM(NSInteger, TextureIndex)
17 | {
18 | TextureIndexColor = 0,
19 | };
20 |
21 | typedef struct
22 | {
23 | matrix_float4x4 projectionMatrix;
24 | matrix_float4x4 modelViewMatrix;
25 | } Uniforms;
26 |
27 | typedef struct
28 | {
29 | Uniforms uniforms[2];
30 | } UniformsArray;
31 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/include/MetalSupport.h:
--------------------------------------------------------------------------------
1 | #ifndef __METAL_VERSION__
2 |
3 | #import
4 | #import
5 |
6 | typedef simd_float2 float2;
7 | typedef simd_float3 float3;
8 | typedef simd_float4 float4;
9 | typedef simd_float3x3 float3x3;
10 | typedef simd_float4x4 float4x4;
11 |
12 | #else
13 |
14 | #import
15 | #import
16 |
17 | #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
18 | #define NSInteger metal::int32_t
19 |
20 | using namespace metal;
21 |
22 | namespace RenderKitShaders {
23 | constexpr sampler basicSampler(coord::normalized, address::clamp_to_edge, filter::linear);
24 | };
25 |
26 | template T srgb_to_linear(T c) {
27 | if (c <= 0.04045) {
28 | return c / 12.92;
29 | }
30 | else {
31 | return powr((c + 0.055) / 1.055, 2.4);
32 | }
33 | }
34 |
35 | inline float3 srgb_to_linear(float3 c) {
36 | return float3(srgb_to_linear(c.x), srgb_to_linear(c.y), srgb_to_linear(c.z));
37 | }
38 |
39 | inline float4 srgb_to_linear(float4 c) {
40 | return float4(srgb_to_linear(c.xyz), c.a);
41 | }
42 |
43 | template T linear_to_srgb(T c) {
44 | if (isnan(c)) {
45 | return 0.0;
46 | }
47 | else if (c > 1.0) {
48 | return 1.0;
49 | }
50 | else {
51 | return (c < 0.0031308f) ? (12.92f * c) : (1.055 * powr(c, 1.0 / 2.4) - 0.055);
52 | }
53 | }
54 |
55 | inline float3 linear_to_srgb(float3 c) {
56 | return float3(linear_to_srgb(c.x), linear_to_srgb(c.y), linear_to_srgb(c.z));
57 | }
58 |
59 | inline float4 linear_to_srgb(float4 c) {
60 | return float4(linear_to_srgb(c.xyz), c.a);
61 | }
62 | #endif
63 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/include/PanoramicShaders.h:
--------------------------------------------------------------------------------
1 | #import "RenderKitShaders.h"
2 |
3 | struct PanoramaFragmentUniforms {
4 | simd_ushort2 gridSize;
5 | float4 colorFactor;
6 | };
7 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/include/Random.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "GLSLCompat.h"
4 |
5 | using namespace glslCompatible;
6 |
7 | // https://www.ronja-tutorials.com/post/024-white-noise/
8 |
9 | //get a scalar random value from a 3d value
10 | inline float rand3dTo1d(float3 value, float3 dotDir = float3(12.9898, 78.233, 37.719)){
11 | //make value smaller to avoid artefacts
12 | float3 smallValue = sin(value);
13 | //get scalar value from 3d vector
14 | float random = dot(smallValue, dotDir);
15 | //make value more random by making it bigger and then taking the factional part
16 | random = frac(sin(random) * 143758.5453);
17 | return random;
18 | }
19 |
20 | inline float rand2dTo1d(float2 value, float2 dotDir = float2(12.9898, 78.233)){
21 | float2 smallValue = sin(value);
22 | float random = dot(smallValue, dotDir);
23 | random = frac(sin(random) * 143758.5453);
24 | return random;
25 | }
26 |
27 | inline float rand1dTo1d(float3 value, float mutator = 0.546){
28 | float3 random = frac(sin(value + mutator) * 143758.5453);
29 | return random.x;
30 | }
31 |
32 | //to 2d functions
33 |
34 | inline float2 rand3dTo2d(float3 value){
35 | return float2(
36 | rand3dTo1d(value, float3(12.989, 78.233, 37.719)),
37 | rand3dTo1d(value, float3(39.346, 11.135, 83.155))
38 | );
39 | }
40 |
41 | inline float2 rand2dTo2d(float2 value){
42 | return float2(
43 | rand2dTo1d(value, float2(12.989, 78.233)),
44 | rand2dTo1d(value, float2(39.346, 11.135))
45 | );
46 | }
47 |
48 | inline float2 rand1dTo2d(float value){
49 | return float2(
50 | rand2dTo1d(value, 3.9812),
51 | rand2dTo1d(value, 7.1536)
52 | );
53 | }
54 |
55 | //to 3d functions
56 |
57 | inline float3 rand3dTo3d(float3 value){
58 | return float3(
59 | rand3dTo1d(value, float3(12.989, 78.233, 37.719)),
60 | rand3dTo1d(value, float3(39.346, 11.135, 83.155)),
61 | rand3dTo1d(value, float3(73.156, 52.235, 09.151))
62 | );
63 | }
64 |
65 | inline float3 rand2dTo3d(float2 value){
66 | return float3(
67 | rand2dTo1d(value, float2(12.989, 78.233)),
68 | rand2dTo1d(value, float2(39.346, 11.135)),
69 | rand2dTo1d(value, float2(73.156, 52.235))
70 | );
71 | }
72 |
73 | inline float3 rand1dTo3d(float value){
74 | return float3(
75 | rand1dTo1d(value, 3.9812),
76 | rand1dTo1d(value, 7.1536),
77 | rand1dTo1d(value, 5.7241)
78 | );
79 | }
80 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/include/RenderKitShaders.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #import "CommonTypes.h"
4 | #import "MetalSupport.h"
5 | #import "ImmersiveShaders.h"
6 | #import "PanoramicShaders.h"
7 | #import "VolumeShaders.h"
8 | #import "FlatShader.h"
9 | #import "UnlitShader.h"
10 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/include/UnlitShader.h:
--------------------------------------------------------------------------------
1 | #import "RenderKitShaders.h"
2 |
3 | struct UnlitMaterial {
4 | float4 color;
5 | short textureIndex; // or -1 for no texture
6 | };
7 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/include/VolumeShaders.h:
--------------------------------------------------------------------------------
1 | #import "RenderKitShaders.h"
2 |
3 | #pragma pack(push, 1)
4 | struct VolumeTransforms {
5 | simd_float4x4 modelViewMatrix;
6 | simd_float4x4 textureMatrix;
7 | };
8 | #pragma pack(pop)
9 |
10 | #pragma pack(push, 1)
11 | struct VolumeFragmentUniforms {
12 | unsigned short instanceCount;
13 | unsigned short maxValue;
14 | float alpha;
15 | };
16 | #pragma pack(pop)
17 |
18 |
19 | #pragma pack(push, 1)
20 | struct VolumeInstance {
21 | float offsetZ;
22 | float textureZ;
23 | };
24 | #pragma pack(pop)
25 |
--------------------------------------------------------------------------------
/Sources/RenderKitShaders/voronoiNoise.metal:
--------------------------------------------------------------------------------
1 | #include
2 | #include "include/GLSLCompat.h"
3 | #include "include/Random.h"
4 | //#include "include/Shaders.h"
5 |
6 | using namespace metal;
7 |
8 | float2 voronoiNoise(float3 value) {
9 | float2 baseCell = floor(value.xy);
10 |
11 | float minDistToCell = 10;
12 | float2 closestCell;
13 | for(int x=-1; x<=1; x++){
14 | for(int y=-1; y<=1; y++){
15 | float2 cell = baseCell + float2(x, y);
16 | float2 cellPosition = cell + rand2dTo2d(cell);
17 | float2 toCell = cellPosition - value.xy;
18 | float distToCell = length(toCell);
19 | if(distToCell < minDistToCell){
20 | minDistToCell = distToCell;
21 | closestCell = cell;
22 | }
23 | }
24 | }
25 | float random = rand2dTo1d(closestCell);
26 | return float2(minDistToCell, random);
27 | }
28 |
29 | // https://www.ronja-tutorials.com/post/029-tiling-noise/
30 | float3 implVoronoiNoise(float3 value){
31 | float2 baseCell = floor(value.xy);
32 |
33 | //first pass to find the closest cell
34 | float minDistToCell = 10;
35 | float2 toClosestCell;
36 | float2 closestCell;
37 | for(int x1=-1; x1<=1; x1++){
38 | for(int y1=-1; y1<=1; y1++){
39 | float2 cell = baseCell + float2(x1, y1);
40 | float2 cellPosition = cell + rand2dTo2d(cell);
41 | float2 toCell = cellPosition - value.xy;
42 | float distToCell = length(toCell);
43 | if(distToCell < minDistToCell){
44 | minDistToCell = distToCell;
45 | closestCell = cell;
46 | toClosestCell = toCell;
47 | }
48 | }
49 | }
50 |
51 | //second pass to find the distance to the closest edge
52 | float minEdgeDistance = 10;
53 | for(int x2=-1; x2<=1; x2++){
54 | for(int y2=-1; y2<=1; y2++){
55 | float2 cell = baseCell + float2(x2, y2);
56 | float2 cellPosition = cell + rand2dTo2d(cell);
57 | float2 toCell = cellPosition - value.xy;
58 |
59 | float2 diffToClosestCell = abs(closestCell - cell);
60 | bool isClosestCell = diffToClosestCell.x + diffToClosestCell.y < 0.1;
61 | if(!isClosestCell){
62 | float2 toCenter = (toClosestCell + toCell) * 0.5;
63 | float2 cellDifference = normalize(toCell - toClosestCell);
64 | float edgeDistance = dot(toCenter, cellDifference);
65 | minEdgeDistance = min(minEdgeDistance, edgeDistance);
66 | }
67 | }
68 | }
69 |
70 | float random = rand2dTo1d(closestCell);
71 |
72 | // distance, "id", edge distance
73 | return float3(minDistToCell, random, minEdgeDistance);
74 | }
75 |
76 | // MARK: -
77 |
78 | struct VoronoiNoise {
79 | float2 size;
80 | float2 offset;
81 | short mode;
82 | texture2d outputTexture;
83 | };
84 |
85 | [[kernel]]
86 | void voronoiNoiseCompute(
87 | constant VoronoiNoise &argument[[buffer(0)]],
88 | uint2 gid [[thread_position_in_grid]]
89 | ) {
90 | auto noise = implVoronoiNoise(float3((float2(gid.x, gid.y) + argument.offset) / argument.size, 0));
91 | float4 color;
92 | if (argument.mode == 0) {
93 | float value = noise.x;
94 | color = float4(value, value, value, 1);
95 | }
96 | else if (argument.mode == 1) {
97 | color = float4(rand1dTo3d(noise.y), 1);
98 | }
99 | else if (argument.mode == 2) {
100 | float value = noise.z;
101 | color = float4(value, value, value, 1);
102 | }
103 | else if (argument.mode == 3) {
104 | float value = step(noise.z, 0.05);
105 | color = float4(value, value, value, 1);
106 | }
107 | else {
108 | color = float4(1, 0, 1, 1);
109 | }
110 | argument.outputTexture.write(color, gid);
111 | }
112 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | # TODO
2 |
3 | ## Short Term
4 |
5 | - [ ] Get GLTF Barramundi Rendering
6 | - [ ] Bindings are incorrect
7 | - [ ] Use Metal introspection to map names <-> bindings
8 | - [ ] Get point cloud view
9 |
10 | ## Medium Term
11 |
12 | - [ ] Huge tidy up: get most code into RenderKit
13 | - [ ] Start on uber PBR renderer
14 | - [ ] Compute based fluids system
15 |
--------------------------------------------------------------------------------
/Tests/RenderKitTests/CSGTests.swift:
--------------------------------------------------------------------------------
1 | @testable import RenderKitScratch
2 | import XCTest
3 |
4 | class CSGTests: XCTestCase {
5 | func test1() {
6 | let sphere = Sphere(center: .zero, radius: 10)
7 | let plane = Plane(normal: [0, 0, 1], w: 0)
8 | let csg = sphere.toCSG()
9 | print(csg.polygons.count)
10 | let (a, b) = csg.split(plane: plane)
11 | print(a.polygons.count)
12 | print(b.polygons.count)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Tests/RenderKitTests/RenderKit3Tests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import RenderKit
3 |
4 | final class RenderKitTests: XCTestCase {
5 | func testExample() throws {
6 | // XCTest Documentation
7 | // https://developer.apple.com/documentation/xctest
8 |
9 | // Defining Test Cases and Test Methods
10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Tests/RenderKitTests/ScratchTests.swift:
--------------------------------------------------------------------------------
1 | @testable import RenderKitScratch
2 | import XCTest
3 |
4 | final class ScratchTests: XCTestCase {
5 | // func testLineSegmentLength() throws {
6 | // let line = LineSegment3D(start: .init(x: 0, y: 0, z: 0), end: .init(x: 3, y: 4, z: 0))
7 | // XCTAssertEqual(line.length, 5)
8 | // }
9 | //
10 | // func testParallelLines() throws {
11 | // let line1 = Line3D(point: .init(x: 0, y: 0, z: 0), direction: .init(x: 1, y: 0, z: 0))
12 | // let line2 = Line3D(point: .init(x: 0, y: 1, z: 0), direction: .init(x: 1, y: 0, z: 0))
13 | // XCTAssertTrue(line1.parallel(to: line2))
14 | // }
15 | //
16 | // func testNonParallelLines() throws {
17 | // let line1 = Line3D(point: .init(x: 0, y: 0, z: 0), direction: .init(x: 1, y: 0, z: 0))
18 | // let line2 = Line3D(point: .init(x: 0, y: 1, z: 0), direction: .init(x: 0, y: 1, z: 0))
19 | // XCTAssertFalse(line1.parallel(to: line2))
20 | // }
21 | //
22 | // func testLineIntersects() throws {
23 | // let line1 = Line3D(point: .init(x: 0, y: 0, z: 0), direction: .init(x: 1, y: 0, z: 0))
24 | // let line2 = Line3D(point: .init(x: 0, y: 1, z: 0), direction: .init(x: 0, y: 1, z: 0))
25 | // XCTAssertTrue(line1.intersects(other: line2))
26 | // }
27 | //
28 | // func testLineNonIntersects() throws {
29 | // let line1 = Line3D(point: .init(x: 0, y: 0, z: 0), direction: .init(x: 1, y: 0, z: 0))
30 | // let line2 = Line3D(point: .init(x: 0, y: 1, z: 0), direction: .init(x: 1, y: 0, z: 0))
31 | // XCTAssertFalse(line1.intersects(other: line2))
32 | // }
33 | //
34 | // func testIntersectionPoint() throws {
35 | // let line1 = Line3D(point: .init(x: 0, y: 0, z: 0), direction: .init(x: 1, y: 0, z: 0))
36 | // let line2 = Line3D(point: .init(x: 0, y: 1, z: 0), direction: .init(x: 0, y: 1, z: 0))
37 | // let intersection = line1.intersectionPoint(other: line2)
38 | // XCTAssertEqual(intersection, .init(x: 0, y: 0, z: 0))
39 | // }
40 | }
41 |
--------------------------------------------------------------------------------
/Tests/RenderKitTests/VertexDescriptorTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import RenderKit
3 |
4 | final class VertexDescriptorTests: XCTestCase {
5 | func testVertexDescriptor() throws {
6 | let d = VertexDescriptor.packed(semantics: [.position, .normal, .textureCoordinate])
7 | XCTAssertEqual(d.layouts[0].attributes[0], .init(semantic: .position, format: .float3, offset: 0))
8 | XCTAssertEqual(d.layouts[0].attributes[1], .init(semantic: .normal, format: .float3, offset: 12))
9 | XCTAssertEqual(d.layouts[0].attributes[2], .init(semantic: .textureCoordinate, format: .float2, offset: 24))
10 | XCTAssertEqual(d.layouts[0].stepFunction, .perVertex)
11 | XCTAssertEqual(d.layouts[0].stepRate, 1)
12 | XCTAssertEqual(d.layouts[0].stride, 32)
13 |
14 | let mtl = MTLVertexDescriptor(d)
15 | XCTAssertEqual(mtl.attributes[0].format, .float3)
16 | XCTAssertEqual(mtl.attributes[0].offset, 0)
17 | XCTAssertEqual(mtl.attributes[0].bufferIndex, 0)
18 | XCTAssertEqual(mtl.attributes[1].format, .float3)
19 | XCTAssertEqual(mtl.attributes[1].offset, 12)
20 | XCTAssertEqual(mtl.attributes[1].bufferIndex, 0)
21 | XCTAssertEqual(mtl.attributes[2].format, .float2)
22 | XCTAssertEqual(mtl.attributes[2].offset, 24)
23 | XCTAssertEqual(mtl.attributes[2].bufferIndex, 0)
24 | XCTAssertEqual(mtl.layouts[0].stepFunction, .perVertex)
25 | XCTAssertEqual(mtl.layouts[0].stepRate, 1)
26 | XCTAssertEqual(mtl.layouts[0].stride, 32)
27 |
28 | var d2 = try VertexDescriptor(mtl)
29 | XCTAssertNil(d2.layouts[0].attributes[0].semantic)
30 | XCTAssertNil(d2.layouts[0].attributes[1].semantic)
31 | XCTAssertNil(d2.layouts[0].attributes[2].semantic)
32 | d2.layouts[0].attributes[0].semantic = .position
33 | d2.layouts[0].attributes[1].semantic = .normal
34 | d2.layouts[0].attributes[2].semantic = .textureCoordinate
35 | XCTAssertEqual(d, d2)
36 |
37 | /*
38 |
39 | Buffer 0:
40 | stepFunction = MTLVertexStepFunctionPerVertex
41 | stride = 32
42 | Attribute 0:
43 | offset = 0
44 | format = MTLAttributeFormatFloat3
45 | Attribute 1:
46 | offset = 12
47 | format = MTLAttributeFormatFloat3
48 | Attribute 2:
49 | offset = 24
50 | format = MTLAttributeFormatFloat2
51 | */
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Tests/RenderKitTests/WingedEdgeTests.swift:
--------------------------------------------------------------------------------
1 | @testable import RenderKitScratch
2 | import simd
3 | import XCTest
4 |
5 | final class CircularPairsTests: XCTestCase {
6 | func testCircularPairsEmpty() throws {
7 | let array: [Int] = []
8 | let result = Array(array.circularPairs())
9 | XCTAssertTrue(result.isEmpty)
10 | }
11 |
12 | func testCircularPairsMultiple() throws {
13 | let array = [1, 2, 3, 4]
14 | let result = Array(array.circularPairs())
15 | XCTAssertEqual(result.count, 4)
16 | XCTAssertEqual(result.map(\.0), [1, 2, 3, 4])
17 | XCTAssertEqual(result.map(\.1), [2, 3, 4, 1])
18 | }
19 |
20 | func testCircularPairsSingle() throws {
21 | let array = [1]
22 | let result = Array(array.circularPairs())
23 | XCTAssertEqual(result.count, 1)
24 | XCTAssertEqual(result.map(\.0), [1])
25 | XCTAssertEqual(result.map(\.1), [1])
26 | }
27 | }
28 |
29 | final class WingedEdgeTests: XCTestCase {
30 | func testWingedEdge() throws {
31 | var collection = WingedEdgeCollection()
32 | collection.add(face: [
33 | SIMD3(0, 0, 0),
34 | SIMD3(1, 0, 0),
35 | SIMD3(1, 1, 0),
36 | ])
37 | XCTAssertEqual(collection.faces.count, 1)
38 | XCTAssertEqual(collection.vertices.count, 3)
39 | XCTAssertEqual(collection.edges.count, 3)
40 | XCTAssertTrue(collection.isValid)
41 |
42 | let device = MTLCreateSystemDefaultDevice()!
43 | let mesh = try collection.toMesh(device: device)
44 | print(mesh)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------