Use the simd types where I can

This commit is contained in:
Eryn Wells 2018-10-07 19:32:29 -07:00
parent edce32e021
commit 8f1e1335bd
9 changed files with 107 additions and 144 deletions

View file

@ -29,6 +29,8 @@
C0FF7C992168062C0081B781 /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = C0BBE3A91F2E91D900E68524 /* Shaders.metal */; };
C0FF7C9A2168062C0081B781 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B906D41F45432700B5F89B /* Preferences.swift */; };
C0FF7C9B2168062C0081B781 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C091616F1F3F5AE6009C4263 /* PreferencesViewController.swift */; };
C0FF7C9D216A6DE00081B781 /* Math.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FF7C9C216A6DE00081B781 /* Math.swift */; };
C0FF7C9E216A6DE00081B781 /* Math.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FF7C9C216A6DE00081B781 /* Math.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -78,6 +80,7 @@
C0BBE3AB1F2E941200E68524 /* Renderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Renderer.swift; sourceTree = "<group>"; };
C0CE7BFF1F362C3F001516B6 /* Geometry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Geometry.swift; sourceTree = "<group>"; };
C0DF1D781F3789DC0038B0A0 /* Memory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Memory.swift; sourceTree = "<group>"; };
C0FF7C9C216A6DE00081B781 /* Math.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Math.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -167,6 +170,7 @@
C0BBE3A31F2E81C700E68524 /* Metaballs.swift */,
C0CE7BFF1F362C3F001516B6 /* Geometry.swift */,
C0DF1D781F3789DC0038B0A0 /* Memory.swift */,
C0FF7C9C216A6DE00081B781 /* Math.swift */,
C0BBE3AB1F2E941200E68524 /* Renderer.swift */,
C0BBE3A91F2E91D900E68524 /* Shaders.metal */,
C0B906D41F45432700B5F89B /* Preferences.swift */,
@ -345,6 +349,7 @@
files = (
C0FF7C992168062C0081B781 /* Shaders.metal in Sources */,
C0FF7C9A2168062C0081B781 /* Preferences.swift in Sources */,
C0FF7C9E216A6DE00081B781 /* Math.swift in Sources */,
C0FF7C972168062C0081B781 /* Memory.swift in Sources */,
C0FF7C9B2168062C0081B781 /* PreferencesViewController.swift in Sources */,
C0B906E91F455D1A00B5F89B /* MetaballsSaverView.swift in Sources */,
@ -367,6 +372,7 @@
C0FF7C912168062B0081B781 /* Renderer.swift in Sources */,
C0FF7C932168062B0081B781 /* Preferences.swift in Sources */,
C0BBE36B1F2E816500E68524 /* AppDelegate.swift in Sources */,
C0FF7C9D216A6DE00081B781 /* Math.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

@ -7,91 +7,18 @@
//
import Foundation
import simd
public struct Point {
var x: Float
var y: Float
var CGPoint: CGPoint {
return CoreGraphics.CGPoint(x: CGFloat(x), y: CGFloat(y))
}
init() {
self.init(x: 0, y: 0)
}
init(x: Float, y: Float) {
self.x = x
self.y = y
}
}
extension Point: CustomStringConvertible {
public var description: String {
return "(\(x), \(y))"
}
}
public struct Size {
var width: UInt16
var height: UInt16
public init() {
self.init(width: 0, height: 0)
}
public init(width: UInt16, height: UInt16) {
self.width = width
self.height = height
}
public typealias Size = packed_uint2
extension Size {
public init(size: CGSize) {
self.init(width: UInt16(size.width), height: UInt16(size.height))
self.init(UInt32(size.width), UInt32(size.height))
}
}
extension Size: CustomStringConvertible {
public var description: String {
return "(\(width), \(height))"
}
}
extension Size: Equatable {
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
/// `a == b` implies that `a != b` is `false`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
public static func ==(lhs: Size, rhs: Size) -> Bool {
return lhs.width == rhs.width && lhs.height == rhs.height
}
}
extension CGSize {
init(size: Size) {
self.init(width: CGFloat(size.width), height: CGFloat(size.height))
}
}
public struct Vector {
var dx: Float
var dy: Float
init() {
self.init(dx: 0, dy: 0)
}
init(dx: Float, dy: Float) {
self.dx = dx
self.dy = dy
}
}
extension Vector: CustomStringConvertible {
public var description: String {
return "(\(dx), \(dy))"
return "(\(x), \(y))"
}
}

63
MetaballsKit/Math.swift Normal file
View file

@ -0,0 +1,63 @@
//
// Math.swift
// Metaballs
//
// Created by Eryn Wells on 9/22/18.
// Copyright © 2018 Eryn Wells. All rights reserved.
//
import Cocoa
import Foundation
import simd
public typealias Float2 = packed_float2
public typealias Float3 = float3
public typealias Float4 = float4
public typealias Matrix3x3 = float3x3
public typealias Matrix4x4 = float4x4
extension Float2 {
var CGPoint: CGPoint {
return CoreGraphics.CGPoint(x: CGFloat(x), y: CGFloat(y))
}
}
extension Float2: CustomStringConvertible {
public var description: String {
return "(\(x), \(y))"
}
}
extension Float4 {
public init(r: Float, g: Float, b: Float, a: Float) {
self.init(r, g, b, a)
}
public init(color: NSColor) {
if let convertedColor = color.usingColorSpace(NSColorSpace.deviceRGB) {
self.init(Float(convertedColor.redComponent), Float(convertedColor.greenComponent), Float(convertedColor.blueComponent), Float(convertedColor.alphaComponent))
} else {
self.init()
}
}
}
extension Matrix4x4 {
/// Create a 4x4 orthographic projection matrix with the provided 6-tuple.
/// @see https://en.wikipedia.org/wiki/Orthographic_projection
static func orthographicProjection(top: Float32, left: Float32, bottom: Float32, right: Float32, near: Float32, far: Float32) -> Matrix4x4 {
let rows = [
Float4(2.0 / (right - left), 0.0, 0.0, -(right + left) / (right - left)),
Float4(0.0, 2.0 / (top - bottom), 0.0, -(top + bottom) / (top - bottom)),
Float4(0.0, 0.0, -2.0 / (far - near), -(far + near) / (far - near)),
Float4(0.0, 0.0, 0.0, 1.0)
]
return Matrix4x4(rows)
}
}
extension CGSize {
init(size: Size) {
self.init(width: CGFloat(size.x), height: CGFloat(size.y))
}
}

View file

@ -8,6 +8,7 @@
import Cocoa
import Foundation
import simd
extension UnsafeMutableRawPointer {
func writeAndAdvance<T>(value: inout T) -> UnsafeMutableRawPointer {
@ -17,38 +18,6 @@ extension UnsafeMutableRawPointer {
}
}
/// Metal's float4 type. 4 bytes per float, 16 bytes total, 16 byte aligned.
public struct Float4 {
public var x: Float = 0
public var y: Float = 0
public var z: Float = 0
public var w: Float = 0
public init() { }
public init(_ x: Float, _ y: Float, _ z: Float, _ w: Float) {
self.x = x
self.y = y
self.z = z
self.w = w
}
public init(r: Float, g: Float, b: Float, a: Float) {
x = r
y = g
z = b
w = a
}
init(color: NSColor) {
if let convertedColor = color.usingColorSpace(NSColorSpace.deviceRGB) {
self.init(Float(convertedColor.redComponent), Float(convertedColor.greenComponent), Float(convertedColor.blueComponent), Float(convertedColor.alphaComponent))
} else {
self.init()
}
}
}
extension NSColor {
convenience init(float4: Float4) {
self.init(deviceRed: CGFloat(float4.x), green: CGFloat(float4.y), blue: CGFloat(float4.z), alpha: CGFloat(float4.w))

View file

@ -13,7 +13,7 @@ public enum MetaballsError: Error {
case metalError(String)
}
public enum ColorStyle: UInt16 {
public enum ColorStyle: UInt32 {
/// Single flat color
case singleColor = 1
/// Two color horizontal gradient
@ -33,8 +33,8 @@ public struct Parameters {
}
// Simulation parameters
var size = Size(width: 0, height: 0)
var numberOfBalls: UInt16 = 0
var size = Size(0, 0)
var numberOfBalls: UInt32 = 0
private var _colorStyle = ColorStyle.singleColor.rawValue
@ -69,8 +69,8 @@ public struct Parameters {
public struct Ball {
let radius: Float
var position = Point()
var velocity = Vector()
var position = Float2()
var velocity = Float2()
internal var bounds: CGRect {
let diameter = CGFloat(radius * 2)
@ -78,8 +78,8 @@ public struct Ball {
}
internal mutating func update() {
position.x += velocity.dx
position.y += velocity.dy
position.x += velocity.x
position.y += velocity.y
}
}
@ -98,11 +98,11 @@ public class Field {
NSLog("Updating size of field: old:\(parameters.size), new:\(newValue)")
if parameters.size != newValue {
// Scale balls to new position and size.
let scale = parameters.size.width != 0 ? Float(newValue.width / parameters.size.width) : 1
let scale = parameters.size.x != 0 ? Float(newValue.x / parameters.size.x) : 1
balls = balls.map {
let r = $0.radius * scale
let p = randomPoint(forBallWithRadius: r)
let v = Vector(dx: $0.velocity.dx * scale, dy: $0.velocity.dy * scale)
let v = Float2($0.velocity.x * scale, $0.velocity.y * scale)
return Ball(radius: r, position: p, velocity: v)
}
@ -141,16 +141,16 @@ public class Field {
if !selfBounds.contains(balls[i].position.CGPoint) {
// Degenerate case. If the ball finds itself outside the bounds of the field, plop it back in the center.
balls[i].position = Point(x: Float(selfBounds.midX), y: Float(selfBounds.midY))
balls[i].position = Float2(x: Float(selfBounds.midX), y: Float(selfBounds.midY))
} else {
// Do collision detection with walls.
let ballBounds = balls[i].bounds
if !selfBounds.contains(ballBounds) {
if ballBounds.minX < selfBounds.minX || ballBounds.maxX > selfBounds.maxX {
balls[i].velocity.dx *= -1
balls[i].velocity.x *= -1
}
if ballBounds.minY < selfBounds.minY || ballBounds.maxY > selfBounds.maxY {
balls[i].velocity.dy *= -1
balls[i].velocity.y *= -1
}
}
}
@ -163,7 +163,7 @@ public class Field {
let dx = Float(5 - Int(arc4random_uniform(10)))
let dy = Float(5 - Int(arc4random_uniform(10)))
let velocity = Vector(dx: dx, dy: dy)
let velocity = Float2(dx, dy)
let ball = Ball(radius: radius, position: position, velocity: velocity)
balls.append(ball)
@ -178,14 +178,14 @@ public class Field {
balls.removeAll(keepingCapacity: true)
}
private func randomPoint(forBallWithRadius radius: Float) -> Point {
private func randomPoint(forBallWithRadius radius: Float) -> Float2 {
guard Float(bounds.width) > radius && Float(bounds.height) > radius else {
return Point()
return Float2()
}
let insetBounds = bounds.insetBy(dx: CGFloat(radius), dy: CGFloat(radius))
let x = Float(UInt32(insetBounds.minX) + arc4random_uniform(UInt32(insetBounds.width)))
let y = Float(UInt32(insetBounds.minY) + arc4random_uniform(UInt32(insetBounds.height)))
let position = Point(x: x, y: y)
let position = Float2(x: x, y: y)
return position
}
@ -205,7 +205,7 @@ public class Field {
}
if let parametersBuffer = parametersBuffer {
parameters.numberOfBalls = UInt16(balls.count)
parameters.numberOfBalls = UInt32(balls.count)
self.parameters.write(to: parametersBuffer)
}
}

View file

@ -42,7 +42,7 @@ extension UserDefaults {
public var colorStyle: ColorStyle? {
get {
let value = integer(forKey: "colorStyle")
if let colorStyle = ColorStyle(rawValue: UInt16(value)) {
if let colorStyle = ColorStyle(rawValue: UInt32(value)) {
return colorStyle
}
return nil

View file

@ -222,7 +222,7 @@ public class PreferencesViewController: NSViewController {
func postColorNotification() {
var info = [String:Any]()
if let item = styleMenu.selectedItem {
info["colorStyle"] = ColorStyle(rawValue: UInt16(item.tag))
info["colorStyle"] = ColorStyle(rawValue: UInt32(item.tag))
}
for (idx, cv) in colorViews.enumerated() {
info["color\(idx)"] = cv.colorWell.color

View file

@ -20,8 +20,8 @@ public protocol RendererDelegate {
}
struct Vertex {
let position: Point
let textureCoordinate: Point
let position: Float2
let textureCoordinate: Float2
}
public class Renderer: NSObject, MTKViewDelegate {
@ -101,13 +101,13 @@ public class Renderer: NSObject, MTKViewDelegate {
// Two triangles, plus texture coordinates.
let points: [Vertex] = [
Vertex(position: Point(x: 1, y: -1), textureCoordinate: Point(x: 1, y: 0)),
Vertex(position: Point(x: -1, y: -1), textureCoordinate: Point(x: 0, y: 0)),
Vertex(position: Point(x: -1, y: 1), textureCoordinate: Point(x: 0, y: 1)),
Vertex(position: Float2(x: 1, y: -1), textureCoordinate: Float2(x: 1, y: 0)),
Vertex(position: Float2(x: -1, y: -1), textureCoordinate: Float2(x: 0, y: 0)),
Vertex(position: Float2(x: -1, y: 1), textureCoordinate: Float2(x: 0, y: 1)),
Vertex(position: Point(x: 1, y: -1), textureCoordinate: Point(x: 1, y: 0)),
Vertex(position: Point(x: -1, y: 1), textureCoordinate: Point(x: 0, y: 1)),
Vertex(position: Point(x: 1, y: 1), textureCoordinate: Point(x: 1, y: 1))
Vertex(position: Float2(x: 1, y: -1), textureCoordinate: Float2(x: 1, y: 0)),
Vertex(position: Float2(x: -1, y: 1), textureCoordinate: Float2(x: 0, y: 1)),
Vertex(position: Float2(x: 1, y: 1), textureCoordinate: Float2(x: 1, y: 1))
]
field.update()

View file

@ -38,13 +38,11 @@ typedef enum {
} ColorStyle;
typedef struct {
short2 size;
ushort numberOfBalls;
ushort colorStyle;
packed_uint2 size;
uint numberOfBalls;
uint colorStyle;
float target;
float feather;
float4 colors[4];
} Parameters;
@ -90,12 +88,12 @@ sampleToColorShader(RasterizerData in [[stage_in]],
out = singleColor(sample, target, feather, parameters.colors[0]);
break;
case Gradient2Horizontal: {
const float blend = in.position.x / parameters.size.x;
const float blend = in.position.x / parameters.size[0];
out = gradient2(sample, target, feather, blend, parameters.colors[0], parameters.colors[1]);
break;
}
case Gradient2Vertical: {
const float blend = in.position.y / parameters.size.y;
const float blend = in.position.y / parameters.size[1];
out = gradient2(sample, target, feather, blend, parameters.colors[0], parameters.colors[1]);
break;
}