Skip to content

Commit

Permalink
Add Rendering and Compute Tools (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
eugenebokhan authored Jan 14, 2024
1 parent be10141 commit 7505d17
Show file tree
Hide file tree
Showing 158 changed files with 9,992 additions and 1,272 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,5 @@ fastlane/test_output
iOSInjectionProject/
.swiftpm
*.DS_Store
Package.resolved
*.swiftformat
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 Eugene Bokhan
Copyright (c) 2024 Eugene Bokhan

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
103 changes: 97 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,109 @@
// swift-tools-version:5.3
// swift-tools-version:5.6

import PackageDescription

let package = Package(
name: "metal-tools",
platforms: [
.iOS(.v11),
.macOS(.v10_13)
.iOS(.v13),
.macOS(.v10_15),
.macCatalyst(.v13)
],
products: [
.library(name: "MetalTools",
targets: ["MetalTools"]),
.library(
name: "MetalTools",
targets: ["MetalTools"]
),
.library(
name: "MetalComputeTools",
targets: ["MetalComputeTools"]
),
.library(
name: "MetalRenderingTools",
targets: ["MetalRenderingTools"]
)
],
dependencies: [
.package(
url: "https://github.com/SwiftGFX/SwiftMath.git",
.upToNextMajor(from: "3.3.1")
)
],
targets: [
.target(name: "MetalTools")
.target(name: "MetalTools"),
.target(
name: "MetalComputeToolsSharedTypes",
publicHeadersPath: "."
),
.target(
name: "MetalComputeTools",
dependencies: [
.target(name: "MetalComputeToolsSharedTypes"),
.target(name: "MetalTools"),
.product(
name: "SwiftMath",
package: "SwiftMath"
)
],
resources: [
.process("Kernels/BitonicSort/BitonicSort.metal"),
.process("Kernels/EuclideanDistance/EuclideanDistance.metal"),
.process("Kernels/LookUpTable/LookUpTable.metal"),
.process("Kernels/MaskGuidedBlur/MaskGuidedBlur.metal"),
.process("Kernels/QuantizeDistanceField/QuantizeDistanceField.metal"),
.process("Kernels/RGBAToYCbCr/RGBAToYCbCr.metal"),
.process("Kernels/StdMeanNormalization/StdMeanNormalization.metal"),
.process("Kernels/TextureAddConstant/TextureAddConstant.metal"),
.process("Kernels/TextureAffineCrop/TextureAffineCrop.metal"),
.process("Kernels/TextureCopy/TextureCopy.metal"),
.process("Kernels/TextureDifferenceHighlight/TextureDifferenceHighlight.metal"),
.process("Kernels/TextureDivideByConstant/TextureDivideByConstant.metal"),
.process("Kernels/TextureInterpolation/TextureInterpolation.metal"),
.process("Kernels/TextureMask/TextureMask.metal"),
.process("Kernels/TextureMaskedMix/TextureMaskedMix.metal"),
.process("Kernels/TextureMax/TextureMax.metal"),
.process("Kernels/TextureMean/TextureMean.metal"),
.process("Kernels/TextureMin/TextureMin.metal"),
.process("Kernels/TextureMix/TextureMix.metal"),
.process("Kernels/TextureMultiplyAdd/TextureMultiplyAdd.metal"),
.process("Kernels/TextureResize/TextureResize.metal"),
.process("Kernels/TextureWeightedMix/TextureWeightedMix.metal"),
.process("Kernels/YCbCrToRGBA/YCbCrToRGBA.metal")
]
),
.target(
name: "MetalComputeToolsTestsResources",
path: "Tests/MetalComputeToolsTestsResources",
resources: [.copy("Shared")]
),
.testTarget(
name: "MetalComputeToolsTests",
dependencies: [
.target(name: "MetalComputeTools"),
.target(name: "MetalComputeToolsTestsResources")
],
resources: [.process("Shaders/Shaders.metal")]
),
.target(
name: "MetalRenderingToolsSharedTypes",
publicHeadersPath: "."
),
.target(
name: "MetalRenderingTools",
dependencies: [
.target(name: "MetalRenderingToolsSharedTypes"),
.target(name: "MetalComputeTools")
],
resources: [
.process("Renderers/Common/Common.metal"),
.process("Renderers/LinesRenderer/LinesRenderer.metal"),
.process("Renderers/MaskRenderer/MaskRenderer.metal"),
.process("Renderers/PointsRenderer/PointsRenderer.metal"),
.process("Renderers/RectangleRender/RectangleRender.metal"),
.process("Renderers/SimpleGeometryRender/SimpleGeometryRender.metal"),
.process("Renderers/TextRender/TextRender.metal"),
.copy("Resources/HelveticaNeue.mtlfontatlas")
]
)
]
)
82 changes: 80 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,81 @@
# metal-tools
# MetalTools

A set of metal tools and extensions I use in my everyday work.
## Description

MetalTools provides a convenient, Swifty way of working with Metal. This library is heavily used in computer vision startups [ZERO10](https://zero10.ar) and [Prisma](https://prisma-ai.com).

## Usage

### Dispatch command buffers in both sync/async manner

See how you can group encodings with Swift closures.

```swift
self.context.scheduleAndWait { buffer in
buffer.compute { encoder in
// compute command encoding logic
}

buffer.blit { encoder in
// blit command encoding logic
}
}
```

### Easily create textures from CGImage

```swift
let texture = try context.texture(
from: cgImage,
usage: [.shaderRead, .shaderWrite]
)
```

### Load a compute pipeline state for a function that sits in a framework

```swift
let library = context.shaderLibrary(for: Foo.self)
let computePipelineState = try lib.computePipelineState(function: "brightness")
```

### Allocate buffer by value type

```swift
let buffer = context.buffer(
for: InstanceUniforms.self,
count: 99,
options: .storageModeShared
)
```

### Serialize and deserialize MTLTexture

```swift
let encoder = JSONEncoder()
let data = try encoder.encode(texture.codable())

let decoder = JSONDecoder()
let decodableTexture = try decoder.decode(MTLTextureCodableBox.self, from: data)
let decodedTexture = try decodableTexture.texture(device: self.context.device)
```

### Setup blending mode in render passes

```swift
let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
renderPipelineDescriptor.colorAttachments[0].setup(blending: .alpha)
```

### Other things

- Create multi-sample render target pairs
- Create textures
- Create depth buffers
- Create depth/stencil states
- [Lots of ready-to-use compute kernels](Sources/MetalComputeTools/Kernels)
- [A couple of simple geometry renderers](Sources/MetalRenderingTools/Renderers)
- etc.

## License

MetalTools is licensed under [MIT license](LICENSE).
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import SwiftMath

extension Matrix3x3f {
static func aspectFitScale(
originalSize: SIMD2<Float>,
boundingSize: SIMD2<Float>
) -> Matrix3x3f {
var newSize = boundingSize
let mW = newSize.x / originalSize.x
let mH = newSize.y / originalSize.y

if mH < mW {
newSize.x = newSize.y / originalSize.y * originalSize.x
} else if mW < mH {
newSize.y = newSize.x / originalSize.x * originalSize.y
}

return .scale(
sx: newSize.x / originalSize.x,
sy: newSize.y / originalSize.y
)
}

static func aspectFillScale(
originalSize: SIMD2<Float>,
boundingSize: SIMD2<Float>
) -> Matrix3x3f {
var newSize = boundingSize
let mW = newSize.x / originalSize.x
let mH = newSize.y / originalSize.y

if mH > mW {
newSize.x = newSize.y / originalSize.y * originalSize.x
} else if mW > mH {
newSize.y = newSize.x / originalSize.x * originalSize.y
}

return .scale(
sx: newSize.x / originalSize.x,
sy: newSize.y / originalSize.y
)
}

static func fillScale(
originalSize: SIMD2<Float>,
boundingSize: SIMD2<Float>
) -> Matrix3x3f {
.scale(
sx: boundingSize.x / originalSize.x,
sy: boundingSize.y / originalSize.y
)
}

static func shear(sx: Float) -> Matrix3x3f {
var matrix = Matrix3x3f.identity
matrix[1][0] = sx
return matrix
}

static func shear(sy: Float) -> Matrix3x3f {
var matrix = Matrix3x3f.identity
matrix[0][1] = sy
return matrix
}
}
Loading

0 comments on commit 7505d17

Please sign in to comment.