mirror of
https://github.com/ScrelliCopter/NeHe-SDL_GPU.git
synced 2025-06-19 21:49:17 +10:00
swift: Implement lessons 1-7
This commit is contained in:
270
src/swift/NeHe/NeHeCopyPass.swift
Normal file
270
src/swift/NeHe/NeHeCopyPass.swift
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user