swift: Implement lessons 1-7

This commit is contained in:
2025-06-10 22:26:10 +10:00
parent 99f2c3e9b4
commit b0ee1ee4c3
16 changed files with 2232 additions and 0 deletions

View File

@@ -0,0 +1,270 @@
/*
* SPDX-FileCopyrightText: (C) 2025 a dinosaur
* SPDX-License-Identifier: Zlib
*/
import SDLSwift
import Foundation
public struct NeHeCopyPass: ~Copyable
{
private var device: OpaquePointer
private var bundle: Bundle?
fileprivate var copies: [Copy]
internal init(_ ctx: borrowing NeHeContext)
{
self.device = ctx.device
self.bundle = ctx.bundle
self.copies = .init()
}
deinit
{
// Free transfer buffers
for copy in self.copies.reversed()
{
SDL_ReleaseGPUTransferBuffer(self.device, copy.xferBuffer)
}
}
internal func submit() throws(NeHeError)
{
guard let cmd = SDL_AcquireGPUCommandBuffer(self.device) else
{
let message = String(cString: SDL_GetError())
throw .sdlError("SDL_AcquireGPUCommandBuffer", message)
}
// Begin the copy pass
let pass = SDL_BeginGPUCopyPass(cmd)
// Upload data into the GPU buffer(s)
for copy in self.copies
{
var vtxSource = SDL_GPUTransferBufferLocation(transfer_buffer: copy.xferBuffer, offset: 0)
var vtxDestination = SDL_GPUBufferRegion(buffer: copy.buffer, offset: 0, size: copy.size)
SDL_UploadToGPUBuffer(pass, &vtxSource, &vtxDestination, false)
}
// End & submit the copy pass
SDL_EndGPUCopyPass(pass)
SDL_SubmitGPUCommandBuffer(cmd)
}
}
public extension NeHeCopyPass
{
mutating func createBuffer<E>(usage: SDL_GPUBufferUsageFlags, _ elements: ArraySlice<E>) throws(NeHeError) -> OpaquePointer
{
// Create data buffer
let size = UInt32(MemoryLayout<E>.stride * elements.count)
var info = SDL_GPUBufferCreateInfo(usage: usage, size: size, props: 0)
guard let buffer = SDL_CreateGPUBuffer(self.device, &info) else
{
throw .sdlError("SDL_CreateGPUBuffer", String(cString: SDL_GetError()))
}
// Create transfer buffer
var xferInfo = SDL_GPUTransferBufferCreateInfo(
usage: SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
size: size,
props: 0)
guard let xferBuffer = SDL_CreateGPUTransferBuffer(self.device, &xferInfo) else
{
let message = String(cString: SDL_GetError())
SDL_ReleaseGPUBuffer(self.device, buffer)
throw .sdlError("SDL_CreateGPUTransferBuffer", message)
}
// Map transfer buffer and copy the data
guard let map = SDL_MapGPUTransferBuffer(self.device, xferBuffer, false) else
{
let message = String(cString: SDL_GetError())
SDL_ReleaseGPUTransferBuffer(self.device, xferBuffer)
SDL_ReleaseGPUBuffer(self.device, buffer)
throw .sdlError("SDL_MapGPUTransferBuffer", message)
}
elements.withUnsafeBufferPointer
{
map.assumingMemoryBound(to: E.self).initialize(from: $0.baseAddress!, count: $0.count)
}
SDL_UnmapGPUTransferBuffer(self.device, xferBuffer)
self.copies.append(.init(
buffer: buffer,
xferBuffer: xferBuffer,
size: size))
return buffer
}
mutating func createTextureFrom(bmpResource name: String, flip: Bool = false, genMipmaps: Bool = false)
throws(NeHeError) -> OpaquePointer
{
// Load image into a surface
guard let bundle = self.bundle,
let pathURL = bundle.url(forResource: name, withExtension: "bmp")
else
{
throw .fatalError("Failed to load BMP resource")
}
let path = if #available(macOS 13.0, *) { pathURL.path(percentEncoded: false) } else { pathURL.path }
guard let image = SDL_LoadBMP(path) else
{
throw .sdlError("SDL_LoadBMP", String(cString: SDL_GetError()))
}
defer { SDL_DestroySurface(image) }
// Flip surface if requested
if flip
{
guard SDL_FlipSurface(image, SDL_FLIP_VERTICAL) else
{
throw .sdlError("SDL_FlipSurface", String(cString: SDL_GetError()))
}
}
// Upload texture to GPU
return try self.createTextureFrom(surface: image, genMipmaps: genMipmaps)
}
mutating func createTextureFrom(surface: UnsafeMutablePointer<SDL_Surface>,
genMipmaps: Bool) throws(NeHeError) -> OpaquePointer
{
var info = SDL_GPUTextureCreateInfo()
info.type = SDL_GPU_TEXTURETYPE_2D
info.format = SDL_GPU_TEXTUREFORMAT_INVALID
info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER
info.width = UInt32(surface.pointee.w)
info.height = UInt32(surface.pointee.h)
info.layer_count_or_depth = 1
info.num_levels = 1
let needsConvert: Bool
(needsConvert, info.format) = switch surface.pointee.format
{
case SDL_PIXELFORMAT_RGBA32: (false, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM)
case SDL_PIXELFORMAT_RGBA64: (false, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM)
case SDL_PIXELFORMAT_RGB565: (false, SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM)
case SDL_PIXELFORMAT_ARGB1555: (false, SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM)
case SDL_PIXELFORMAT_BGRA4444: (false, SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM)
case SDL_PIXELFORMAT_BGRA32: (false, SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM)
case SDL_PIXELFORMAT_RGBA64_FLOAT: (false, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT)
case SDL_PIXELFORMAT_RGBA128_FLOAT: (false, SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT)
default: (true, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM)
}
func BYTESPERPIXEL(_ format: SDL_PixelFormat) -> UInt32
{
let isFourCC = format.rawValue != 0 && (((format.rawValue >> 28) & 0xF) != 1)
return isFourCC
? [
SDL_PIXELFORMAT_YUY2,
SDL_PIXELFORMAT_UYVY,
SDL_PIXELFORMAT_YVYU,
SDL_PIXELFORMAT_P010
].contains(format) ? 2 : 1
: format.rawValue & 0xFF
}
let data: UnsafeRawBufferPointer
let conv: UnsafeMutablePointer<SDL_Surface>? = nil
if needsConvert
{
// Convert pixel format if required
guard let conv = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ABGR8888) else
{
throw .sdlError("SDL_ConvertSurface", String(cString: SDL_GetError()))
}
let numPixels = Int(conv.pointee.w) * Int(conv.pointee.h)
data = .init(start: conv.pointee.pixels, count: Int(BYTESPERPIXEL(conv.pointee.format)) * numPixels)
}
else
{
let numPixels = Int(surface.pointee.w) * Int(surface.pointee.h)
data = .init(start: surface.pointee.pixels, count: Int(BYTESPERPIXEL(surface.pointee.format)) * numPixels)
}
defer { SDL_DestroySurface(conv) }
if genMipmaps
{
info.usage |= SDL_GPU_TEXTUREUSAGE_COLOR_TARGET
// floor(log₂(max(𝑤,)) + 1
info.num_levels = 31 - UInt32(max(info.width, info.height).leadingZeroBitCount) + 1
}
return try self.createTextureFrom(pixels: data, createInfo: &info, genMipmaps: genMipmaps)
}
}
fileprivate extension NeHeCopyPass
{
func createTextureFrom(pixels: UnsafeRawBufferPointer,
createInfo info: inout SDL_GPUTextureCreateInfo, genMipmaps: Bool)
throws(NeHeError) -> OpaquePointer
{
guard let texture = SDL_CreateGPUTexture(self.device, &info) else
{
throw .sdlError("SDL_CreateGPUTexture", String(cString: SDL_GetError()))
}
// Create and copy image data to a transfer buffer
var xferInfo = SDL_GPUTransferBufferCreateInfo(
usage: SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
size: UInt32(pixels.count),
props: 0)
guard let xferBuffer = SDL_CreateGPUTransferBuffer(self.device, &xferInfo) else
{
SDL_ReleaseGPUTexture(self.device, texture)
throw .sdlError("SDL_CreateGPUTransferBuffer", String(cString: SDL_GetError()))
}
defer { SDL_ReleaseGPUTransferBuffer(self.device, xferBuffer) }
guard let map = SDL_MapGPUTransferBuffer(self.device, xferBuffer, false) else
{
SDL_ReleaseGPUTexture(self.device, texture)
throw .sdlError("SDL_MapGPUTransferBuffer", String(cString: SDL_GetError()))
}
map.initializeMemory(as: UInt8.self,
from: pixels.withMemoryRebound(to: UInt8.self, \.baseAddress!),
count: pixels.count)
SDL_UnmapGPUTransferBuffer(self.device, xferBuffer)
// Upload the transfer data to the GPU resources
guard let cmd = SDL_AcquireGPUCommandBuffer(self.device) else
{
SDL_ReleaseGPUTexture(self.device, texture)
throw .sdlError("SDL_AcquireGPUCommandBuffer", String(cString: SDL_GetError()))
}
let pass = SDL_BeginGPUCopyPass(cmd)
var source = SDL_GPUTextureTransferInfo()
source.transfer_buffer = xferBuffer
source.offset = 0
var destination = SDL_GPUTextureRegion()
destination.texture = texture
destination.w = info.width
destination.h = info.height
destination.d = info.layer_count_or_depth
SDL_UploadToGPUTexture(pass, &source, &destination, false)
SDL_EndGPUCopyPass(pass)
if genMipmaps
{
SDL_GenerateMipmapsForGPUTexture(cmd, texture)
}
SDL_SubmitGPUCommandBuffer(cmd)
return texture
}
}
fileprivate extension NeHeCopyPass
{
struct Copy
{
var buffer: OpaquePointer
var xferBuffer: OpaquePointer
var size: UInt32
}
}