Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SDL
Submodule SDL updated 1 files
+1 −1 external/sdl-gpu
14 changes: 2 additions & 12 deletions Sources/AVPlayerLayer+Mac.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//

import AVFoundation
internal import var SDL_gpu.GPU_FORMAT_RGBA
internal import var SDL_gpu.GPU_FORMAT_BGRA

public typealias AVPlayer = AVFoundation.AVPlayer

Expand Down Expand Up @@ -88,7 +88,6 @@ public final class AVPlayerLayer: CALayer {
currentPlayerOutputSize = size
}

@_optimize(speed)
func updateVideoFrame() {
updatePlayerOutput(size: frame.size)
guard
Expand All @@ -109,16 +108,7 @@ public final class AVPlayerLayer: CALayer {

if contents?.width != width || contents?.height != height {
contentsScale = 1.0 // this doesn't work on init because we set contentsScale in UIView.init afterwards
contents = VideoTexture(width: width, height: height, format: GPU_FORMAT_RGBA)
}

// Swap R and B values to get RGBA pixels instead of BGRA:
let bufferSize = CVPixelBufferGetDataSize(pixelBuffer)
for i in stride(from: 0, to: bufferSize, by: 16) {
swap(&pixelBytes[i], &pixelBytes[i + 2])
swap(&pixelBytes[i+4], &pixelBytes[i + 6])
swap(&pixelBytes[i+8], &pixelBytes[i + 10])
swap(&pixelBytes[i+12], &pixelBytes[i + 14])
contents = VideoTexture(width: width, height: height, format: GPU_FORMAT_BGRA)
Comment on lines 110 to +111
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change dramatically improves video performance on macOS – esp. in debug builds

}

contents?.replacePixels(with: pixelBytes, bytesPerPixel: 4)
Expand Down
172 changes: 172 additions & 0 deletions Sources/CAGradientLayer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
internal import SDL
private import SDL_gpu

open class CAGradientLayer: CALayer {
public var colors: [CGColor] = [] {
didSet {
_colors = colors.map {
SIMD4<CGFloat>(
x: CGFloat($0.redValue) / 255,
y: CGFloat($0.greenValue) / 255,
z: CGFloat($0.blueValue) / 255,
w: CGFloat($0.alphaValue) / 255
)
}
setNeedsDisplay()
}
}

private var _colors: [SIMD4<CGFloat>] = []

public var locations: [Float] = [] {
didSet { setNeedsDisplay() }
}

public var startPoint = CGPoint(x: 0.5, y: 0.0) {
didSet { setNeedsDisplay() }
}

public var endPoint = CGPoint(x: 0.5, y: 1.0) {
didSet { setNeedsDisplay() }
}

@_optimize(speed)
override open func display() {
super.display()

if bounds.width.isZero || bounds.height.isZero {
return
}

if locations.count < colors.count {
if colors.count == 1 {
backgroundColor = colors[0]
colors = []
locations = []
} else {
locations = colors.indices.map { Float($0) / Float(colors.count - 1) }
}

}

if colors.isEmpty {
contents = nil
return
}

let width: Int32
let height: Int32

if startPoint.x == endPoint.x {
width = 1
height = Int32(bounds.height * self.contentsScale)
} else if startPoint.y == endPoint.y {
width = Int32(bounds.width * self.contentsScale)
height = 1
} else {
// pessimistic case where we need to fill the entire thing
width = Int32(bounds.width * self.contentsScale)
height = Int32(bounds.height * self.contentsScale)
}

guard let surface = SDL_CreateRGBSurfaceWithFormat(
0, // surface flags are always 0 in SDL2
width,
height,
32, // bit depth
UInt32(SDL_PIXELFORMAT_RGBA32)
) else {
return
}

SDL_LockSurface(surface)

let startPoint = SIMD2(x: self.startPoint.x, y: self.startPoint.y)
let endPoint = SIMD2(x: self.endPoint.x, y: self.endPoint.y)

for y in 0 ..< Int(height) {
let pixelPosY = y * Int(surface.pointee.pitch)

for x in 0 ..< Int(width) {
let pixel = surface.pointee.pixels.assumingMemoryBound(to: UInt8.self)
.advanced(by: pixelPosY)
.advanced(by: x * Int(surface.pointee.format.pointee.BytesPerPixel))

let p = SIMD2(x: CGFloat(x) / bounds.width, y: CGFloat(y) / bounds.height)

// Direction of gradient line
let dir = endPoint - startPoint
let len2 = dot(dir, dir)

var t: CGFloat = 0.0
if len2 > 0.00001 {
// Project (p - startPoint) onto the gradient direction
t = dot(p - startPoint, dir) / len2
}

t = max(0.0, min(t, 1.0))
let color = sampleGradient(t)

let normalized = color * 255
pixel[0] = UInt8(clamping: Int(normalized.x))
pixel[1] = UInt8(clamping: Int(normalized.y))
pixel[2] = UInt8(clamping: Int(normalized.z))
pixel[3] = UInt8(clamping: Int(normalized.w))
}
}

SDL_UnlockSurface(surface)

if
let contents,
contents.width != Int(bounds.width) ||
contents.height != Int(bounds.height)
{
contents.replacePixels(
with: surface.pointee.pixels.assumingMemoryBound(to: UInt8.self),
bytesPerPixel: Int(surface.pointee.format.pointee.BytesPerPixel)
)
} else {
contents = CGImage(surface: surface)
}
}

@_optimize(speed)
func sampleGradient(_ position: CGFloat) -> SIMD4<CGFloat> {
precondition(colors.count >= 2)
precondition(locations.count >= 2)

let t = Float(max(0.0, min(position, 1.0)))

if t <= locations.first! {
return _colors.first!
} else if t >= locations.last! {
return _colors.last!
}

// Find stops [i, i+1] containing t
for i in 0 ..< (colors.count - 1) {
let loc0 = locations[i]
let loc1 = locations[i + 1]

if t >= loc0 && t <= loc1 {
let localT = (t - loc0) / (loc1 - loc0)
return mix(_colors[i], _colors[i + 1], t: CGFloat(localT))
}
}

return _colors.last!
}
}


// GLSL-style mix for SIMD4
@inline(__always)
func mix(_ a: SIMD4<CGFloat>, _ b: SIMD4<CGFloat>, t: CGFloat) -> SIMD4<CGFloat> {
return a + (b - a) * t
}

@inline(__always)
func dot(_ a: SIMD2<CGFloat>, _ b: SIMD2<CGFloat>) -> CGFloat {
return a.x * b.x + a.y * b.y
}
4 changes: 2 additions & 2 deletions Sources/CALayer+ContentsGravity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ struct ContentsGravityTransformation {
/// Warning, this assumes `layer` has `contents` and will crash otherwise!
init(for layer: CALayer) {
let scaledContents = CGSize(
width: CGFloat(layer.contents!.width) / layer.contentsScale,
height: CGFloat(layer.contents!.height) / layer.contentsScale
width: (layer.contents.map { CGFloat($0.width) } ?? layer.bounds.width) / layer.contentsScale,
height: (layer.contents.map { CGFloat($0.height) } ?? layer.bounds.height) / layer.contentsScale
Comment on lines +25 to +26
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

turns out this is actually unused but it prevents the crash mentioned on line 22 (I could remove that comment now I guess)

)

let bounds = layer.bounds
Expand Down
Loading