Skip to content

Commit

Permalink
Prepare for XAudio implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
STREGA committed Dec 3, 2023
1 parent 3dd2574 commit 00833a2
Show file tree
Hide file tree
Showing 30 changed files with 327 additions and 43 deletions.
134 changes: 134 additions & 0 deletions Dependencies/XAudio/XAudio2/Common/Error.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright © 2023 Dustin Collins (Strega's Gate)
* All Rights Reserved.
*
* http://stregasgate.com
*/

import WinSDK

extension Error {
public enum Kind {
case unknown
case hresult(WinSDK.HRESULT)
case text(String)
}
}

public struct Error: Swift.Error {
@usableFromInline
let kind: Kind

@inlinable @inline(__always)
internal init(_ kind: Kind) {
self.kind = kind
}

@inlinable @inline(__always)
internal init(_ hr: HRESULT) {
self.kind = .hresult(hr)
}

@inlinable @inline(__always)
internal init(_ string: String) {
self.kind = .text(string)
}
}

extension Error: CustomStringConvertible {
public var description: String {
switch kind {
case let .hresult(hr):
return hr.errorMessage
case let .text(text):
return text
default:
return "\(kind)"
}
}
}

extension Error: CustomDebugStringConvertible {
public var debugDescription: String {
return description
}
}

public extension HRESULT {
// Common
static let ok = WinSDK.S_OK
static let fail = HRESULT(bitPattern: 0x80004005)
static let invalidArgument = HRESULT(bitPattern: 0x80070057)
static let outOfMemory = HRESULT(bitPattern: 0x8007000E)
static let notImplemented = HRESULT(bitPattern: 0x80004001)
static let `false` = WinSDK.S_FALSE

// D3d12
static let adapterNotFound = HRESULT(bitPattern: 0x887E0001)
static let driverVersionMismatch = HRESULT(bitPattern: 0x887E0002)

// DXGI https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-error
static let invalidCall = HRESULT(dxgiCode: 0x887A0001)
static let wasStillDrawing = HRESULT(dxgiCode: 0x887A000A)
}

internal extension HRESULT {
@inlinable @inline(__always)
init(severity: UInt32, facility: UInt32, code: UInt32) {
self.init(bitPattern:(((severity<<31) | (facility<<16) | ((code)))))
}
@inlinable @inline(__always)
init(dxgiCode: UInt32) {
let dxgiFacility: UInt32 = 0x87a
self.init(severity: 1, facility: dxgiFacility, code: dxgiCode)
}
}

public extension HRESULT {
// https://docs.microsoft.com/en-us/windows/win32/api/winerror/nf-winerror-succeeded
@inlinable @inline(__always)
var isSuccess: Bool {
return self >= 0
}
// https://docs.microsoft.com/en-us/windows/win32/api/winerror/nf-winerror-failed
@inlinable @inline(__always)
var isFailure: Bool {
return self < 0
}

/// Throws Error(self) if isFailure is true
@inlinable @inline(__always)
func checkResult(_ source: Any?, _ function: StaticString) throws {
if isFailure {
if let source = source {
throw Error("\(type(of: source)) \(function) " + self.errorMessage)
}else{
throw Error("\(function) " + self.errorMessage)
}
}
}
}

public extension DWORD {
@inlinable @inline(__always)
var errorMessage: String {
let dwFlags: DWORD = DWORD(FORMAT_MESSAGE_ALLOCATE_BUFFER) | DWORD(FORMAT_MESSAGE_FROM_SYSTEM) | DWORD(FORMAT_MESSAGE_IGNORE_INSERTS)

var buffer: UnsafeMutablePointer<WCHAR>? = nil
let dwResult: DWORD = withUnsafeMutablePointer(to: &buffer) {
return $0.withMemoryRebound(to: WCHAR.self, capacity: 2) {
return FormatMessageW(dwFlags, nil, self, DWORD((WORD(SUBLANG_DEFAULT) << 10) | WORD(LANG_NEUTRAL)), $0, 0, nil)
}
}
guard dwResult > 0, let message = buffer else {
return "HRESULT(0x\(String(self, radix: 16)))"
}
defer {LocalFree(buffer)}
return "0x\(String(self, radix: 16)) - \(String(decodingCString: message, as: UTF16.self))"
}
}

public extension HRESULT {
@inlinable @inline(__always)
var errorMessage: String {DWORD(bitPattern: self).errorMessage}
}
99 changes: 99 additions & 0 deletions Dependencies/XAudio/XAudio2/Common/IUnknown.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright © 2023 Dustin Collins (Strega's Gate)
* All Rights Reserved.
*
* http://stregasgate.com
*/

import WinSDK

public class IUnknown {
@usableFromInline
internal let pUnk: UnsafeMutableRawPointer

@inlinable @inline(__always)
func perform<Type, ResultType>(as type: Type.Type, body: (_ pThis: UnsafeMutablePointer<Type>) throws -> ResultType) rethrows -> ResultType {
let pThis = pUnk.bindMemory(to: Type.self, capacity: 1)
return try body(pThis)
}

@inlinable @inline(__always)
func performFatally<Type, ResultType>(as type: Type.Type, body: (_ pThis: UnsafeMutablePointer<Type>) throws -> ResultType) -> ResultType {
do {
let pThis = pUnk.bindMemory(to: Type.self, capacity: 1)
return try body(pThis)
}
// catch let error as XAudio2.Error {
// fatalError(error.description)
// }
catch{
fatalError("\(error)")
}
}

@usableFromInline
internal enum MemoryManagement {
case alreadyRetained
case retain
}

@inlinable @inline(__always)
required internal init?(winSDKPointer pointer: UnsafeMutableRawPointer?, memoryManagement: MemoryManagement = .alreadyRetained) {
guard let pointer = pointer else {return nil}
self.pUnk = pointer
if memoryManagement == .retain {
self.retain()
}
}

@inlinable @inline(__always)
internal func retain() {
self.performFatally(as: WinSDK.IUnknown.self) {pThis in
_ = pThis.pointee.lpVtbl.pointee.AddRef(pThis)
}
}

@inlinable @inline(__always)
internal func release() {
self.performFatally(as: WinSDK.IUnknown.self) {pThis in
_ = pThis.pointee.lpVtbl.pointee.Release(pThis)
}
}

@inlinable @inline(__always)
public func fullRelease() {
self.performFatally(as: WinSDK.IUnknown.self) {pThis in
while pThis.pointee.lpVtbl.pointee.Release(pThis) > 0 {}
}
}

@inlinable @inline(__always)
public func queryInterface<T: IUnknown>(_ type: T.Type) -> T? {
return self.perform(as: WinSDK.IUnknown.self) { pThis in
var pointer: UnsafeMutableRawPointer? = nil
var iid: IID = type.interfaceID
let result: HRESULT = pThis.pointee.lpVtbl.pointee.QueryInterface(pThis, &iid, &pointer)

if result.isSuccess, let pointer: UnsafeMutableRawPointer = pointer {
return type.init(winSDKPointer: pointer, memoryManagement: .retain)
}
return nil
}
}

deinit {
self.release()
}

@inlinable @inline(__always)
class var interfaceID: WinSDK.IID {preconditionFailure("Must override!")}
}

extension IUnknown {
@usableFromInline
typealias RawValue = WinSDK.IUnknown
}
extension IUnknown.RawValue {
@inlinable @inline(__always)
static var interfaceID: WinSDK.IID {WinSDK.IID_IUnknown}
}
42 changes: 42 additions & 0 deletions Dependencies/XAudio/XAudio2/Common/String+Convenience.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright © 2023 Dustin Collins (Strega's Gate)
* All Rights Reserved.
*
* http://stregasgate.com
*/

import WinSDK

internal extension String {
@inlinable @inline(__always)
init(windowsUTF8 lpcstr: LPCSTR) {
self = withUnsafePointer(to: lpcstr) {
return $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: $0)) {
return String(cString: $0)
}
}
}

@inlinable @inline(__always)
var windowsUTF8: Array<CHAR> {
return self.withCString(encodedAs: UTF8.self) {
return $0.withMemoryRebound(to: CHAR.self, capacity: self.utf8.count + 1) {
return Array(UnsafeBufferPointer(start: $0, count: self.utf8.count + 1))
}
}
}

@inlinable @inline(__always)
init(windowsUTF16 lpcwstr: LPCWSTR) {
self.init(decodingCString: lpcwstr, as: UTF16.self)
}

@inlinable @inline(__always)
var windowsUTF16: Array<WCHAR> {
return self.withCString(encodedAs: UTF16.self) {
return $0.withMemoryRebound(to: WCHAR.self, capacity: self.utf16.count + 1) {
return Array(UnsafeBufferPointer(start: $0, count: self.utf16.count + 1))
}
}
}
}
Empty file.
5 changes: 5 additions & 0 deletions Dependencies/XAudio/XAudio2C/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module OpenGL_Linux [system] {
header "shim.h"
link "XAudio2C"
export *
}
1 change: 1 addition & 0 deletions Dependencies/XAudio/XAudio2C/shim.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include <xaudio2.h>
2 changes: 0 additions & 2 deletions Dependencies/XAudio29/XAudio29.swift

This file was deleted.

27 changes: 16 additions & 11 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,9 @@ let package = Package(
dependencies.append(contentsOf: [
.target(name: "Direct3D12",
condition: .when(platforms: [.windows])),
// XAudio is C++ and won't be available on all Swift versions
// so we'll use OpenAL as a fallback
.target(name: "OpenALSoft",
.target(name: "XAudio2",
condition: .when(platforms: [.windows])),
])
#if swift(>=5.9)
#warning("Reminder: Check XAudio2 C++ build support.")
#endif
#endif

#if os(Linux)
Expand Down Expand Up @@ -137,6 +132,10 @@ let package = Package(
],
swiftSettings: {
var settings: [SwiftSetting] = []

settings.append(
.define("GATEENGINE_USE_OPENAL", .when(platforms: [.linux]))
)

// MARK: Gate Engine options.
settings.append(contentsOf: [
Expand Down Expand Up @@ -309,14 +308,20 @@ let package = Package(
])

#if os(Windows)
targets.append(
targets.append(contentsOf: [
// Direct3D12
.target(name: "Direct3D12",
path: "Dependencies/Direct3D12",
swiftSettings: [
.define("Direct3D12ExcludeOriginalStyleAPI", .when(configuration: .release)),
])
)
]),
// XAudio2
.target(name: "XAudio2",
dependencies: ["XAudio2C"],
path: "Dependencies/XAudio/XAudio2"),
.systemLibrary(name: "XAudio2C",
path: "Dependencies/XAudio/XAudio2C"),
])
#endif

#if os(macOS)
Expand Down Expand Up @@ -355,7 +360,7 @@ let package = Package(
])
#endif

#if os(Linux) || os(Android) || os(Windows)
#if os(Linux) || os(Android)
targets.append(contentsOf: [
// OpenALSoft
.target(name: "OpenALSoft",
Expand Down Expand Up @@ -410,7 +415,7 @@ let package = Package(
cxxLanguageStandard: .gnucxx14
)

#if os(Linux) || os(Android) || os(Windows)
#if os(Linux) || os(Android)
var openALLinkerSettings: [LinkerSetting] {
var array: [LinkerSetting] = []

Expand Down
2 changes: 1 addition & 1 deletion Sources/GateEngine/Game.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public final class Game {

@MainActor public private(set) lazy var windowManager: WindowManager = WindowManager(self)
@MainActor @usableFromInline private(set) lazy var ecs: ECSContext = ECSContext(game: self)
@MainActor @usableFromInline private(set) lazy var hid: HID = HID()
@MainActor public private(set) lazy var hid: HID = HID()
public private(set) lazy var resourceManager: ResourceManager = {
return ResourceManager(game: self)
}()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* http://stregasgate.com
*/
#if (canImport(OpenALSoft) || canImport(LinuxSupport)) && !os(WASI)
#if GATEENGINE_USE_OPENAL
#if canImport(OpenALSoft)
import OpenALSoft
#elseif canImport(LinuxSupport)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* http://stregasgate.com
*/
#if (canImport(OpenALSoft) || canImport(LinuxSupport)) && !os(WASI)
#if GATEENGINE_USE_OPENAL
#if canImport(OpenALSoft)
import OpenALSoft
#elseif canImport(LinuxSupport)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* http://stregasgate.com
*/
#if (canImport(OpenALSoft) || canImport(LinuxSupport)) && !os(WASI)
#if GATEENGINE_USE_OPENAL
#if canImport(OpenALSoft)
import OpenALSoft
#elseif canImport(LinuxSupport)
Expand Down
Loading

0 comments on commit 00833a2

Please sign in to comment.