From afc6c6cbf103c665fb9b69c0ba096e8b52da2db2 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Tue, 26 Sep 2023 12:27:35 -0500 Subject: [PATCH 01/20] Fetch wgpu-native v0.17.0.2 --- Makefile | 13 ++++++++----- README.md | 2 +- dub.json | 6 +++--- subprojects/wgpu.Makefile | 4 ++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 49889b4..f820c1a 100644 --- a/Makefile +++ b/Makefile @@ -30,15 +30,18 @@ endif ifndef LIB_WGPU_EXT $(error Unsupported target OS '$(OS)') endif -LIB_WGPU_SOURCE := subprojects/wgpu/libwgpu.$(LIB_WGPU_EXT) -LIB_WGPU := lib/libwgpu.$(LIB_WGPU_EXT) -wgpu: $(LIB_WGPU) +LIB_WGPU_SOURCE := subprojects/wgpu +LIB_WGPU := lib/libwgpu_native +wgpu: $(LIB_WGPU).a $(LIB_WGPU).$(LIB_WGPU_EXT) .PHONY: wgpu $(LIB_WGPU_SOURCE): subprojects/wgpu.Makefile @make -C subprojects -f wgpu.Makefile -$(LIB_WGPU): $(LIB_WGPU_SOURCE) +$(LIB_WGPU).a: $(LIB_WGPU_SOURCE)/libwgpu_native.a @mkdir -p lib - @cp $(LIB_WGPU_SOURCE) lib/. + @cp $(LIB_WGPU_SOURCE)/libwgpu_native.a lib/. +$(LIB_WGPU).$(LIB_WGPU_EXT): $(LIB_WGPU_SOURCE)/libwgpu_native.$(LIB_WGPU_EXT) + @mkdir -p lib + @cp $(LIB_WGPU_SOURCE)/libwgpu_native.$(LIB_WGPU_EXT) lib/. ################################################# # Examples diff --git a/README.md b/README.md index 3a82551..2d77ad2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ D bindings to [wgpu-native](https://github.com/gfx-rs/wgpu-native) as an idiomatic wrapper around the library. -Targets wgpu-native [`v0.10.4.1`](https://github.com/gfx-rs/wgpu-native/releases/tag/v0.10.4.1). +Targets wgpu-native [`v0.17.0.2`](https://github.com/gfx-rs/wgpu-native/releases/tag/v0.17.0.2). ## Usage diff --git a/dub.json b/dub.json index cded27d..12b3858 100644 --- a/dub.json +++ b/dub.json @@ -55,12 +55,12 @@ } ], "libs": [ - "wgpu" + "wgpu_native" ], "copyFiles-linux": [ - "lib/libwgpu.so" + "lib/libwgpu_native.so" ], "copyFiles-osx": [ - "lib/libwgpu.dylib" + "lib/libwgpu_native.dylib" ] } diff --git a/subprojects/wgpu.Makefile b/subprojects/wgpu.Makefile index 0041571..00a16ea 100644 --- a/subprojects/wgpu.Makefile +++ b/subprojects/wgpu.Makefile @@ -1,5 +1,5 @@ -# See https://github.com/gfx-rs/wgpu-native/tree/v0.10.4.1 -VERSION := v0.10.4.1 +# See https://github.com/gfx-rs/wgpu-native/tree/v0.17.0.2 +VERSION := v0.17.0.2 OS := $(shell uname -s) ARCH := $(shell uname -m) From cf7b3c99930e2320d2eeb7400f51aea792a2d0e8 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Tue, 26 Sep 2023 14:22:56 -0500 Subject: [PATCH 02/20] Update opaque pointers and enumerations for wgpu-native v0.17.0.2 --- dub.json | 2 +- source/wgpu/api.d | 128 ++++++++++++++++++++++++++++++++++---------- source/wgpu/enums.d | 100 +++++++++++++++++++++++++++++----- 3 files changed, 187 insertions(+), 43 deletions(-) diff --git a/dub.json b/dub.json index 12b3858..96b820d 100644 --- a/dub.json +++ b/dub.json @@ -50,7 +50,7 @@ ], "postBuildCommands-osx": [ "echo Fixing up libwgpu dylib path...", - "install_name_tool -change /Users/runner/work/wgpu-native/wgpu-native/target/x86_64-apple-darwin/debug/deps/libwgpu_native.dylib @executable_path/libwgpu.dylib bin/wgpu-d-test-unittest" + "install_name_tool -change /Users/runner/work/wgpu-native/wgpu-native/target/x86_64-apple-darwin/debug/deps/libwgpu_native.dylib @executable_path/libwgpu_native.dylib bin/wgpu-d-test-unittest" ] } ], diff --git a/source/wgpu/api.d b/source/wgpu/api.d index 76d2fea..5e4a0aa 100644 --- a/source/wgpu/api.d +++ b/source/wgpu/api.d @@ -41,19 +41,25 @@ static const uint MAX_MIP_LEVELS = 16; /// Maximum number of vertex buffers. static const uint MAX_VERTEX_BUFFERS = 16; -// Opaque Pointers +// Section: Opaque Pointers + // TODO: Implement a wrapper around `WGPUQuerySet` alias QuerySet = WGPUQuerySet; // TODO: Because dealing with, "What if this debug label goes out of scope and gets GC'd?" is becoming a headache, convert offending `struct`ures (those with complex wrappers that aren't simply opaque pointers) to classes to reap those reference semantics: https://forum.dlang.org/post/ixfpxfdmnahtytftwald@forum.dlang.org // TODO: Also, for the aforementioned class conversions, ensure `id` handles and constructors whose types with `Device`-only member initializers are `package` private. +/// alias ChainedStruct = WGPUChainedStruct; +/// alias ChainedStructOut = WGPUChainedStructOut; +/// alias AdapterProperties = WGPUAdapterProperties; /// See_Also: wgpu::BindGroupEntry alias BindGroupEntry = WGPUBindGroupEntry; +/// alias BlendComponent = WGPUBlendComponent; +/// alias BufferBindingLayout = WGPUBufferBindingLayout; /// Describes a `Buffer`. alias BufferDescriptor = WGPUBufferDescriptor; @@ -66,12 +72,15 @@ alias Color = WGPUColor; alias CommandBufferDescriptor = WGPUCommandBufferDescriptor; /// Describes a `CommandEncoder`. alias CommandEncoderDescriptor = WGPUCommandEncoderDescriptor; +/// alias CompilationMessage = WGPUCompilationMessage; -/// Describes a `ComputePass`. -alias ComputePassDescriptor = WGPUComputePassDescriptor; +/// +alias ComputePassTimestampWrite = WGPUComputePassTimestampWrite; +/// alias ConstantEntry = WGPUConstantEntry; /// Extent of a texture related operation. alias Extent3d = WGPUExtent3D; +/// alias InstanceDescriptor = WGPUInstanceDescriptor; /// Represents the sets of limits an adapter/device supports. /// @@ -100,15 +109,21 @@ alias Origin3d = WGPUOrigin3D; /// Describes a `PipelineLayout`. alias PipelineLayoutDescriptor = WGPUPipelineLayoutDescriptor; /// -alias PrimitiveDepthClampingState = WGPUPrimitiveDepthClampingState; +alias PrimitiveDepthClipControl = WGPUPrimitiveDepthClipControl; /// alias QuerySetDescriptor = WGPUQuerySetDescriptor; /// +alias QueueDescriptor = WGPUQueueDescriptor; +/// alias RenderBundleDescriptor = WGPURenderBundleDescriptor; /// alias RenderBundleEncoderDescriptor = WGPURenderBundleEncoderDescriptor; /// Describes a depth stencil attachment to a `RenderPass`. alias RenderPassDepthStencilAttachment = WGPURenderPassDepthStencilAttachment; +/// +alias RenderPassDescriptorMaxDrawCount = WGPURenderPassDescriptorMaxDrawCount; +/// +alias RenderPassTimestampWrite = WGPURenderPassTimestampWrite; /// Additional information required when requesting an adapter. /// See_Also: wgpu::RequestAdapterOptions alias RequestAdapterOptions = WGPURequestAdapterOptions; @@ -117,11 +132,13 @@ alias SamplerBindingLayout = WGPUSamplerBindingLayout; /// Describes a `Sampler`. alias SamplerDescriptor = WGPUSamplerDescriptor; /// +alias ShaderModuleCompilationHint = WGPUShaderModuleCompilationHint; +/// alias ShaderModuleDescriptor = WGPUShaderModuleDescriptor; /// -alias ShaderModuleSPIRVDescriptor = WGPUShaderModuleSPIRVDescriptor; +alias ShaderModuleSpirvDescriptor = WGPUShaderModuleSPIRVDescriptor; /// -alias ShaderModuleWGSLDescriptor = WGPUShaderModuleWGSLDescriptor; +alias ShaderModuleWgslDescriptor = WGPUShaderModuleWGSLDescriptor; /// alias StencilFaceState = WGPUStencilFaceState; /// @@ -129,13 +146,19 @@ alias StorageTextureBindingLayout = WGPUStorageTextureBindingLayout; /// alias SurfaceDescriptor = WGPUSurfaceDescriptor; /// +alias SurfaceDescriptorFromAndroidNativeWindow = WGPUSurfaceDescriptorFromAndroidNativeWindow; +/// alias SurfaceDescriptorFromCanvasHTMLSelector = WGPUSurfaceDescriptorFromCanvasHTMLSelector; /// alias SurfaceDescriptorFromMetalLayer = WGPUSurfaceDescriptorFromMetalLayer; /// +alias SurfaceDescriptorFromWaylandSurface = WGPUSurfaceDescriptorFromWaylandSurface; +/// alias SurfaceDescriptorFromWindowsHWND = WGPUSurfaceDescriptorFromWindowsHWND; /// -alias SurfaceDescriptorFromXlib = WGPUSurfaceDescriptorFromXlib; +alias SurfaceDescriptorFromXcbWindow = WGPUSurfaceDescriptorFromXcbWindow; +/// +alias SurfaceDescriptorFromXlibWindow = WGPUSurfaceDescriptorFromXlibWindow; /// Describes a `SwapChain`. alias SwapChainDescriptor = WGPUSwapChainDescriptor; /// @@ -159,11 +182,15 @@ alias BindGroupLayoutEntry = WGPUBindGroupLayoutEntry; alias BlendState = WGPUBlendState; /// alias CompilationInfo = WGPUCompilationInfo; +/// Describes a `ComputePass`. +alias ComputePassDescriptor = WGPUComputePassDescriptor; // TODO: View of a buffer which can be used to copy to/from a texture. /// View of a texture which can be used to copy to/from a buffer. alias ImageCopyBuffer = WGPUImageCopyBuffer; /// View of a texture which can be used to copy to/from a texture. alias ImageCopyTexture = WGPUImageCopyTexture; +/// +alias ProgrammableStageDescriptor = WGPUProgrammableStageDescriptor; /// Describes a color attachment to a `RenderPass`. alias RenderPassColorAttachment = WGPURenderPassColorAttachment; /// Describes a `Texture`. @@ -178,9 +205,6 @@ alias RenderPassDescriptor = WGPURenderPassDescriptor; /// Describes a `RenderPipeline`. /// See_Also: wgpu::RenderPipelineDescriptor alias RenderPipelineDescriptor = WGPURenderPipelineDescriptor; -// TODO: Wrap this into `Instance.requestAdapter` -/// -alias AdapterExtras = WGPUAdapterExtras; //static assert(false, std.traits.fullyQualifiedName!WGPUAdapterType); @@ -433,21 +457,23 @@ struct Instance { /// Use `Adapter.ready` to determine whether an Adapter was successfully requested. static Adapter requestAdapter( PowerPreference powerPreference = PowerPreference.highPerformance, + BackendType backendType = BackendType._null, Flag!"forceFallbackAdapter" forceFallbackAdapter = No.forceFallbackAdapter ) { return requestAdapter( - RequestAdapterOptions(null, /* compatibleSurface: */ null, powerPreference, forceFallbackAdapter) + RequestAdapterOptions(null, /* compatibleSurface: */ null, powerPreference, backendType, forceFallbackAdapter) ); } /// ditto static Adapter requestAdapter( const Surface compatibleSurface, PowerPreference powerPreference = PowerPreference.highPerformance, + BackendType backendType = BackendType._null, Flag!"forceFallbackAdapter" forceFallbackAdapter = No.forceFallbackAdapter ) @trusted { assert(compatibleSurface.id !is null, "Given compatible surface is not valid"); - return requestAdapter( - RequestAdapterOptions(null, cast(WGPUSurface) compatibleSurface.id, powerPreference, forceFallbackAdapter) - ); + return requestAdapter(RequestAdapterOptions( + null, cast(WGPUSurface) compatibleSurface.id, powerPreference, backendType, forceFallbackAdapter + )); } /// ditto static Adapter requestAdapter(RequestAdapterOptions options) @trusted { @@ -525,12 +551,12 @@ struct Adapter { auto device = new Device(label); const WGPUDeviceExtras extras = { chain: WGPUChainedStruct(null, cast(WGPUSType) WGPUNativeSType.WGPUSType_DeviceExtras), - label: device.label.toStringz, tracePath: tracePath is null ? null : tracePath.toStringz }; WGPURequiredLimits requiredLimits = { limits: limits }; WGPUDeviceDescriptor desc = { nextInChain: cast(ChainedStruct*) &extras, + label: device.label.toStringz, requiredLimits: &requiredLimits }; wgpuAdapterRequestDevice(id, &desc, &wgpu_request_device_callback, cast(void*) device); @@ -554,7 +580,7 @@ class Device { /// Release the given handle. void destroy() { - if (ready) wgpuDeviceDrop(id); + if (ready) wgpuDeviceRelease(id); id = null; } @@ -595,10 +621,14 @@ class Device { /// Check for resource cleanups and mapping callbacks. /// Params: + /// queue = The `Queue` to poll. + /// submissionIndex /// forceWait = Whether or not the call should block. - void poll(Flag!"forceWait" forceWait = No.forceWait) @trusted const { + /// Returns: `true` if the queue is empty, or `false` if there are more queue submissions still in flight. + bool poll(Queue queue, ulong submissionIndex, Flag!"forceWait" forceWait = No.forceWait) @trusted const { assert(id !is null); - wgpuDevicePoll(cast(WGPUDevice) id, forceWait); + auto wrappedSubmissionIndex = WGPUWrappedSubmissionIndex(queue.id, submissionIndex); + return wgpuDevicePoll(cast(WGPUDevice) id, forceWait, &wrappedSubmissionIndex); } /// Creates a shader module from SPIR-V source code. @@ -607,7 +637,7 @@ class Device { ShaderModule createShaderModule(const byte[] spv) @trusted { // TODO: assert SPIR-V magic number is at the beginning of the stream // TODO: assert input is not longer than `size_t.max` - const ShaderModuleSPIRVDescriptor spirv = { + const ShaderModuleSpirvDescriptor spirv = { chain: WGPUChainedStruct(null, cast(WGPUSType) SType.shaderModuleSpirvDescriptor), codeSize: spv.length.to!uint, code: spv.to!(uint[]).ptr, @@ -620,9 +650,9 @@ class Device { /// /// Shader modules are used to define programmable stages of a pipeline. ShaderModule createShaderModule(string wgsl) @trusted const { - const ShaderModuleWGSLDescriptor wgslDesc = { + const ShaderModuleWgslDescriptor wgslDesc = { chain: WGPUChainedStruct(null, cast(WGPUSType) SType.shaderModuleWgslDescriptor), - source: wgsl.toStringz, + code: wgsl.toStringz, }; auto desc = ShaderModuleDescriptor(cast(ChainedStruct*) &wgslDesc.chain); assert(id !is null); @@ -853,7 +883,8 @@ class Device { /// minFilter = How to filter the texture when it needs to be minified/made smaller. /// mipmapFilter = How to filter between mip map levels. Sampler createSampler( - AddressMode addressMode, FilterMode magFilter, FilterMode minFilter, FilterMode mipmapFilter = FilterMode.nearest + AddressMode addressMode, FilterMode magFilter, FilterMode minFilter, + MipmapFilterMode mipmapFilter = MipmapFilterMode.nearest ) { SamplerDescriptor desc = { addressModeU: addressMode, @@ -1333,12 +1364,51 @@ class Texture { case TextureFormat.bc6hrgbFloat: case TextureFormat.bc7rgbaUnorm: case TextureFormat.bc7rgbaUnormSrgb: + case TextureFormat.etc2Rgb8Unorm: + case TextureFormat.etc2Rgb8UnormSrgb: + case TextureFormat.etc2Rgb8A1Unorm: + case TextureFormat.etc2Rgb8A1UnormSrgb: + case TextureFormat.etc2Rgba8Unorm: + case TextureFormat.etc2Rgba8UnormSrgb: + case TextureFormat.eacR11Unorm: + case TextureFormat.eacR11Snorm: + case TextureFormat.eacRg11Unorm: + case TextureFormat.eacRg11Snorm: + case TextureFormat.astc4x4Unorm: + case TextureFormat.astc4x4UnormSrgb: + case TextureFormat.astc5x4Unorm: + case TextureFormat.astc5x4UnormSrgb: + case TextureFormat.astc5x5Unorm: + case TextureFormat.astc5x5UnormSrgb: + case TextureFormat.astc6x5Unorm: + case TextureFormat.astc6x5UnormSrgb: + case TextureFormat.astc6x6Unorm: + case TextureFormat.astc6x6UnormSrgb: + case TextureFormat.astc8x5Unorm: + case TextureFormat.astc8x5UnormSrgb: + case TextureFormat.astc8x6Unorm: + case TextureFormat.astc8x6UnormSrgb: + case TextureFormat.astc8x8Unorm: + case TextureFormat.astc8x8UnormSrgb: + case TextureFormat.astc10x5Unorm: + case TextureFormat.astc10x5UnormSrgb: + case TextureFormat.astc10x6Unorm: + case TextureFormat.astc10x6UnormSrgb: + case TextureFormat.astc10x8Unorm: + case TextureFormat.astc10x8UnormSrgb: + case TextureFormat.astc10x10Unorm: + case TextureFormat.astc10x10UnormSrgb: + case TextureFormat.astc12x10Unorm: + case TextureFormat.astc12x10UnormSrgb: + case TextureFormat.astc12x12Unorm: + case TextureFormat.astc12x12UnormSrgb: // FIXME: Supply pixel compression ratios for these texture formats assert(0, "Unknown compression ratio of " ~ descriptor.format.stringof); - // QUESTION: Depth formats of _at least_ 24 bits, therefore there's no guarenteed block size? + // QUESTION: Depth formats of _at least_ 24 bits, therefore there's no guaranteed block size? case TextureFormat.undefined: case TextureFormat.depth16Unorm: case TextureFormat.depth32Float: + case TextureFormat.depth32FloatStencil8: case TextureFormat.depth24Plus: case TextureFormat.depth24PlusStencil8: case TextureFormat.force32: @@ -1463,7 +1533,7 @@ struct TextureView { /// Release the handle to this texture view. void destroy() { - if (id !is null) wgpuTextureViewDrop(id); + if (id !is null) wgpuTextureViewRelease(id); id = null; } @@ -1555,7 +1625,7 @@ struct ShaderModule { /// Release the handle to this shader. void destroy() { - if (id !is null) wgpuShaderModuleDrop(id); + if (id !is null) wgpuShaderModuleRelease(id); id = null; } } @@ -1751,7 +1821,7 @@ class RenderPipeline { /// Release the given handle. void destroy() { - if (id !is null) wgpuRenderPipelineDrop(id); + if (id !is null) wgpuRenderPipelineRelease(id); id = null; } } @@ -1826,7 +1896,7 @@ struct RenderPass { /// void end() { - wgpuRenderPassEncoderEndPass(instance); + wgpuRenderPassEncoderEnd(instance); } // TODO: void wgpuRenderPassEncoderBeginOcclusionQuery(WGPURenderPassEncoder renderPassEncoder, uint32_t queryIndex); @@ -1965,7 +2035,7 @@ struct ComputePipeline { /// Release the given handle. void destroy() { - if (id !is null) wgpuComputePipelineDrop(id); + if (id !is null) wgpuComputePipelineRelease(id); id = null; } @@ -1996,7 +2066,7 @@ struct ComputePass { /// /// x, y and z denote the number of work groups to dispatch in each dimension. void dispatch(const uint x, const uint y, const uint z) { - wgpuComputePassEncoderDispatch(instance, x, y, z); + wgpuComputePassEncoderDispatchWorkgroups(instance, x, y, z); } // TODO: void wgpuComputePassEncoderDispatchIndirect(WGPUComputePassEncoder computePassEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset); diff --git a/source/wgpu/enums.d b/source/wgpu/enums.d index dd16c47..aa0c78a 100644 --- a/source/wgpu/enums.d +++ b/source/wgpu/enums.d @@ -258,6 +258,15 @@ enum FilterMode : WGPUFilterMode { force32 = cast(WGPUFilterMode) 0x7FFFFFFF } +/// +enum MipmapFilterMode : WGPUMipmapFilterMode { + /// + nearest, + /// + linear, + force32 = cast(WGPUMipmapFilterMode) 0x7FFFFFFF +} + /// Specifies the vertex order for faces to be considered front-facing. enum FrontFace : WGPUFrontFace { /// Clockwise ordered faces will be considered front-facing. @@ -481,19 +490,6 @@ enum TextureAspect : WGPUTextureAspect { force32 = cast(WGPUTextureAspect) 0x7FFFFFFF } -/// -enum TextureComponentType : WGPUTextureComponentType { - /// - _float, - /// - sint, - /// - _uint, - /// - depthComparison, - force32 = cast(WGPUTextureComponentType) 0x7FFFFFFF -} - /// enum TextureDimension : WGPUTextureDimension { /// @@ -592,6 +588,8 @@ enum TextureFormat : WGPUTextureFormat { /// depth32Float, /// + depth32FloatStencil8, + /// bc1rgbaUnorm, /// bc1rgbaUnormSrgb, @@ -619,6 +617,82 @@ enum TextureFormat : WGPUTextureFormat { bc7rgbaUnorm, /// bc7rgbaUnormSrgb, + /// + etc2Rgb8Unorm, + /// + etc2Rgb8UnormSrgb, + /// + etc2Rgb8A1Unorm, + /// + etc2Rgb8A1UnormSrgb, + /// + etc2Rgba8Unorm, + /// + etc2Rgba8UnormSrgb, + /// + eacR11Unorm, + /// + eacR11Snorm, + /// + eacRg11Unorm, + /// + eacRg11Snorm, + /// + astc4x4Unorm, + /// + astc4x4UnormSrgb, + /// + astc5x4Unorm, + /// + astc5x4UnormSrgb, + /// + astc5x5Unorm, + /// + astc5x5UnormSrgb, + /// + astc6x5Unorm, + /// + astc6x5UnormSrgb, + /// + astc6x6Unorm, + /// + astc6x6UnormSrgb, + /// + astc8x5Unorm, + /// + astc8x5UnormSrgb, + /// + astc8x6Unorm, + /// + astc8x6UnormSrgb, + /// + astc8x8Unorm, + /// + astc8x8UnormSrgb, + /// + astc10x5Unorm, + /// + astc10x5UnormSrgb, + /// + astc10x6Unorm, + /// + astc10x6UnormSrgb, + /// + astc10x8Unorm, + /// + astc10x8UnormSrgb, + /// + astc10x10Unorm, + /// + astc10x10UnormSrgb, + /// + astc12x10Unorm, + /// + astc12x10UnormSrgb, + /// + astc12x12Unorm, + /// + astc12x12UnormSrgb, force32 = cast(WGPUTextureFormat) 0x7FFFFFFF } From ef8b64bb84aaec592435c024306e80f0c497e92f Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Tue, 26 Sep 2023 14:23:22 -0500 Subject: [PATCH 03/20] Update dylib paths for examples --- examples/cube/dub.json | 2 +- examples/headless/dub.json | 2 +- examples/triangle/dub.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/cube/dub.json b/examples/cube/dub.json index 1ae9860..6c85391 100644 --- a/examples/cube/dub.json +++ b/examples/cube/dub.json @@ -24,6 +24,6 @@ ], "postBuildCommands-osx": [ "echo Fixing up libwgpu dylib path...", - "install_name_tool -change /Users/runner/work/wgpu-native/wgpu-native/target/x86_64-apple-darwin/debug/deps/libwgpu_native.dylib @executable_path/../lib/libwgpu.dylib `find $PACKAGE_DIR/../../bin -name '*cube'`" + "install_name_tool -change /Users/runner/work/wgpu-native/wgpu-native/target/x86_64-apple-darwin/debug/deps/libwgpu_native.dylib @executable_path/../lib/libwgpu_native.dylib `find $PACKAGE_DIR/../../bin -name '*cube'`" ] } diff --git a/examples/headless/dub.json b/examples/headless/dub.json index 71cc565..58e42bd 100644 --- a/examples/headless/dub.json +++ b/examples/headless/dub.json @@ -20,6 +20,6 @@ ], "postBuildCommands-osx": [ "echo Fixing up libwgpu dylib path...", - "install_name_tool -change /Users/runner/work/wgpu-native/wgpu-native/target/x86_64-apple-darwin/debug/deps/libwgpu_native.dylib @executable_path/../lib/libwgpu.dylib `find $PACKAGE_DIR/../../bin -name '*headless'`" + "install_name_tool -change /Users/runner/work/wgpu-native/wgpu-native/target/x86_64-apple-darwin/debug/deps/libwgpu_native.dylib @executable_path/../lib/libwgpu_native.dylib `find $PACKAGE_DIR/../../bin -name '*headless'`" ] } diff --git a/examples/triangle/dub.json b/examples/triangle/dub.json index 9eef82a..917449d 100644 --- a/examples/triangle/dub.json +++ b/examples/triangle/dub.json @@ -27,7 +27,7 @@ ], "postBuildCommands-osx": [ "echo Fixing up libwgpu dylib path...", - "install_name_tool -change /Users/runner/work/wgpu-native/wgpu-native/target/x86_64-apple-darwin/debug/deps/libwgpu_native.dylib @executable_path/lib/libwgpu.dylib `find $PACKAGE_DIR/../../bin -name '*triangle'`" + "install_name_tool -change /Users/runner/work/wgpu-native/wgpu-native/target/x86_64-apple-darwin/debug/deps/libwgpu_native.dylib @executable_path/lib/libwgpu_native.dylib `find $PACKAGE_DIR/../../bin -name '*triangle'`" ], "libs": ["glfw3"], "libs-linux": [ From faffb5199d024d102333e7c254f2d21b04dc4361 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 05:10:14 -0500 Subject: [PATCH 04/20] Download WebGPU binaries on Windows --- Makefile | 71 ++++++++++++++++++++++----------------- subprojects/wgpu.Makefile | 52 ++++++++++++++++++++++------ 2 files changed, 81 insertions(+), 42 deletions(-) diff --git a/Makefile b/Makefile index f820c1a..311e8ba 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ -OS := $(shell uname -s) +OS ?= $(shell uname -s) +rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2)) ifeq ($(OS),Darwin) CC := clang SED := gsed @@ -6,13 +7,16 @@ endif ifeq ($(OS),Linux) CC := gcc endif +ifeq ($(OS),Windows_NT) +CC := cl +endif ifndef CC $(error Unsupported target OS '$(OS)') endif ifndef SED SED := sed endif -SOURCES := $(shell find source -name '*.d') +SOURCES := $(call rwildcard,source/,*.d) LIBS_PATH := lib .DEFAULT_GOAL := docs @@ -22,47 +26,40 @@ all: docs # Subprojects ################################################# ifeq ($(OS),Darwin) -LIB_WGPU_EXT := dylib + LIB_WGPU := libwgpu_native.dylib endif ifeq ($(OS),Linux) -LIB_WGPU_EXT := so + LIB_WGPU := libwgpu_native.so endif -ifndef LIB_WGPU_EXT -$(error Unsupported target OS '$(OS)') +ifeq ($(OS),Windows_NT) + LIB_WGPU := wgpu_native.dll +endif +ifndef LIB_WGPU + $(error Unsupported target OS '$(OS)') endif LIB_WGPU_SOURCE := subprojects/wgpu -LIB_WGPU := lib/libwgpu_native -wgpu: $(LIB_WGPU).a $(LIB_WGPU).$(LIB_WGPU_EXT) +wgpu: $(LIB_WGPU_SOURCE) lib/$(LIB_WGPU) .PHONY: wgpu $(LIB_WGPU_SOURCE): subprojects/wgpu.Makefile @make -C subprojects -f wgpu.Makefile +ifneq ($(OS),Windows_NT) +wgpu: $(LIB_WGPU).a $(LIB_WGPU).a: $(LIB_WGPU_SOURCE)/libwgpu_native.a @mkdir -p lib @cp $(LIB_WGPU_SOURCE)/libwgpu_native.a lib/. -$(LIB_WGPU).$(LIB_WGPU_EXT): $(LIB_WGPU_SOURCE)/libwgpu_native.$(LIB_WGPU_EXT) +endif +lib/$(LIB_WGPU): $(LIB_WGPU_SOURCE)/$(LIB_WGPU) +ifeq ($(OS),Windows_NT) + @if not exist lib mkdir lib + @xcopy $(subst /,\\,$(LIB_WGPU_SOURCE))\\$(LIB_WGPU) lib /y >NUL +else @mkdir -p lib - @cp $(LIB_WGPU_SOURCE)/libwgpu_native.$(LIB_WGPU_EXT) lib/. + @cp $(LIB_WGPU_SOURCE)/$(LIB_WGPU) lib/. +endif ################################################# -# Examples +# Test Coverage ################################################# -example_utils_SOURCES := $(shell find examples/utils/source -name '*.d') -EXAMPLES := bin/cube bin/headless bin/triangle -examples: $(EXAMPLES) -.PHONY: examples - -cube_SOURCES := $(example_utils_SOURCES) $(shell find examples/cube/source -name '*.d') -bin/cube: $(SOURCES) $(cube_SOURCES) examples/cube/dub.json - cd examples/cube && dub build - -headless_SOURCES := $(shell find examples/headless/source -name '*.d') -bin/headless: $(SOURCES) $(headless_SOURCES) examples/headless/dub.json - cd examples/headless && dub build - -triangle_SOURCES := $(shell find examples/triangle/source -name '*.d') -bin/triangle: $(SOURCES) $(triangle_SOURCES) examples/triangle/dub.json - cd examples/triangle && dub build - cover: $(SOURCES) dub test --build=unittest-cov @@ -91,18 +88,30 @@ docs/sitemap.xml: $(SOURCES) @echo Done docs: docs/sitemap.xml +ifeq ($(OS),Windows_NT) + $(error Build documentation on *nix!) +endif .PHONY: docs +clean: +ifneq ($(OS),Windows_NT) clean: clean-docs - rm -rf bin lib +else + $(error Build documentation on *nix!) +endif dub clean + @rm -rf bin lib @echo "Cleaning code coverage reports..." - rm -f -- *.lst + @rm -f -- *.lst .PHONY: clean +DOCS_HTML := $(call rwildcard,docs/,*.html) clean-docs: +ifeq ($(OS),Windows_NT) + $(error Build documentation on *nix!) +endif @echo "Cleaning generated documentation..." @rm -f docs.json @rm -f docs/sitemap.xml docs/file_hashes.json - @rm -rf `find docs -name '*.html'` + @rm -rf $(DOCS_HTML) .PHONY: clean-docs diff --git a/subprojects/wgpu.Makefile b/subprojects/wgpu.Makefile index 00a16ea..241f814 100644 --- a/subprojects/wgpu.Makefile +++ b/subprojects/wgpu.Makefile @@ -1,30 +1,60 @@ # See https://github.com/gfx-rs/wgpu-native/tree/v0.17.0.2 VERSION := v0.17.0.2 -OS := $(shell uname -s) -ARCH := $(shell uname -m) +OS ?= $(shell uname -s) +ifeq ($(OS),Windows_NT) + ifeq ($(PROCESSOR_ARCHITEW6432),AMD64) + ARCH := x86_64 + else + ifeq ($(PROCESSOR_ARCHITECTURE),AMD64) + ARCH := x86_64 + endif + ifeq ($(PROCESSOR_ARCHITECTURE),x86) + ARCH := i686 + endif + endif + ifndef ARCH + $(error Unsupported target CPU architecture: $(OS)) + endif +endif +ARCH ?= $(shell uname -m) ifeq ($(OS),Darwin) RELEASE := macos - LIB_WGPU_EXT := dylib + LIB_WGPU := libwgpu_native.dylib endif ifeq ($(OS),Linux) RELEASE := linux - LIB_WGPU_EXT := so + LIB_WGPU := libwgpu_native.so +endif +ifeq ($(OS),Windows_NT) + RELEASE := windows + LIB_WGPU := wgpu_native.dll endif -# TODO: Add Windows support ifndef RELEASE $(error Unsupported WebGPU target OS: $(OS)) endif CONFIG ?= debug +.DEFAULT_GOAL := wgpu/$(LIB_WGPU) + BINARY_ARCHIVE := https://github.com/gfx-rs/wgpu-native/releases/download/$(VERSION)/wgpu-$(RELEASE)-$(ARCH)-$(CONFIG).zip $(info Using $(CONFIG) wgpu-native@$(VERSION) from: $(BINARY_ARCHIVE)) +ifeq ($(OS),Windows_NT) + ARCHIVE_ZIP := $(shell echo %USERPROFILE%/AppData/Local/Temp/wgpu.zip) +else + ARCHIVE_ZIP := /tmp/wgpu.zip +endif +$(ARCHIVE_ZIP): + @echo Downloading WebGPU Native binaries... + @curl -L $(BINARY_ARCHIVE) --output $(ARCHIVE_ZIP) -.DEFAULT_GOAL := wgpu/wgpu.h -wgpu/libwgpu.$(LIB_WGPU_EXT): +wgpu/$(LIB_WGPU): $(ARCHIVE_ZIP) wgpu/wgpu.h: - @echo "Downloading WebGPU Native binaries…" - @curl -L $(BINARY_ARCHIVE) --output /tmp/wgpu.zip - @echo "Unzipping WebGPU Native binaries…" - @unzip -q /tmp/wgpu.zip -d wgpu + @echo Unzipping WebGPU Native binaries... +ifeq ($(OS),Windows_NT) + @if not exist wgpu mkdir wgpu + @tar -xf $(ARCHIVE_ZIP) -C wgpu +else + @unzip -q $(ARCHIVE_ZIP) -d wgpu +endif From e608892a5663729f7da9b57426c26bc43953e435 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 05:38:42 -0500 Subject: [PATCH 05/20] Fix unit tests on Windows --- dub.json | 28 +++++++++++++++++----------- source/wgpu/api.d | 4 ++-- source/wgpu/enums.d | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/dub.json b/dub.json index 96b820d..4265c48 100644 --- a/dub.json +++ b/dub.json @@ -24,8 +24,8 @@ }, "systemDependencies-linux": "vulkan >= 1.1.0, patchelf", "systemDependencies-osx": "metal >= 2", - "systemDependencies-windows": "DirectX >= 11", - "preGenerateCommands-posix": [ + "systemDependencies-windows": "vulkan >= 1.1.0 or DirectX >= 11", + "preGenerateCommands": [ "make wgpu" ], "dflags": [ @@ -34,16 +34,10 @@ ], "configurations": [ { - "name": "library", - "lflags-posix": [ - "-L$PACKAGE_DIR/lib" - ] + "name": "library" }, { "name": "unittest", - "lflags-posix": [ - "-Llib" - ], "postBuildCommands-linux": [ "echo Fixing up libwgpu so path...", "patchelf --set-rpath '$$ORIGIN' bin/wgpu-d-test-unittest" @@ -54,13 +48,25 @@ ] } ], - "libs": [ - "wgpu_native" + "lflags-posix": ["-L$WGPU_D_PACKAGE_DIR/lib"], + "lflags-windows": ["/LIBPATH:$WGPU_D_PACKAGE_DIR/subprojects/wgpu"], + "libs": ["wgpu_native"], + "libs-windows": [ + "d3dcompiler", + "ws2_32", + "advapi32", + "user32", + "userenv", + "bcrypt", + "ntdll" ], "copyFiles-linux": [ "lib/libwgpu_native.so" ], "copyFiles-osx": [ "lib/libwgpu_native.dylib" + ], + "copyFiles-windows": [ + "lib/wgpu_native.dll" ] } diff --git a/source/wgpu/api.d b/source/wgpu/api.d index 5e4a0aa..7520394 100644 --- a/source/wgpu/api.d +++ b/source/wgpu/api.d @@ -148,13 +148,13 @@ alias SurfaceDescriptor = WGPUSurfaceDescriptor; /// alias SurfaceDescriptorFromAndroidNativeWindow = WGPUSurfaceDescriptorFromAndroidNativeWindow; /// -alias SurfaceDescriptorFromCanvasHTMLSelector = WGPUSurfaceDescriptorFromCanvasHTMLSelector; +alias SurfaceDescriptorFromCanvasHtmlSelector = WGPUSurfaceDescriptorFromCanvasHTMLSelector; /// alias SurfaceDescriptorFromMetalLayer = WGPUSurfaceDescriptorFromMetalLayer; /// alias SurfaceDescriptorFromWaylandSurface = WGPUSurfaceDescriptorFromWaylandSurface; /// -alias SurfaceDescriptorFromWindowsHWND = WGPUSurfaceDescriptorFromWindowsHWND; +alias SurfaceDescriptorFromWindowsHwnd = WGPUSurfaceDescriptorFromWindowsHWND; /// alias SurfaceDescriptorFromXcbWindow = WGPUSurfaceDescriptorFromXcbWindow; /// diff --git a/source/wgpu/enums.d b/source/wgpu/enums.d index aa0c78a..2e9a576 100644 --- a/source/wgpu/enums.d +++ b/source/wgpu/enums.d @@ -413,7 +413,7 @@ enum SType : WGPUSType { /// surfaceDescriptorFromMetalLayer, /// - surfaceDescriptorFromWindowsHWND, + surfaceDescriptorFromWindowsHwnd, /// surfaceDescriptorFromXlib, /// From b15f57f1cb557d03be1c2969459a0c2704f734c8 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 05:39:55 -0500 Subject: [PATCH 06/20] Add `Device.poll` overload for generic device queues --- source/wgpu/api.d | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/wgpu/api.d b/source/wgpu/api.d index 7520394..ca7c058 100644 --- a/source/wgpu/api.d +++ b/source/wgpu/api.d @@ -619,6 +619,15 @@ class Device { return Queue(wgpuDeviceGetQueue(cast(WGPUDevice) id)); } + /// Check for resource cleanups and mapping callbacks. + /// Params: + /// forceWait = Whether or not the call should block. + /// Returns: `true` if the queue is empty, or `false` if there are more queue submissions still in flight. + bool poll(Flag!"forceWait" forceWait = No.forceWait) @trusted const { + assert(id !is null); + return wgpuDevicePoll(cast(WGPUDevice) id, forceWait, null); + } + /// Check for resource cleanups and mapping callbacks. /// Params: /// queue = The `Queue` to poll. From c8a60ecba6d17f1a96b67e90b7aaadf9ad0ff8b2 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 07:07:00 -0500 Subject: [PATCH 07/20] Fix `BackendType` and `PowerPreference` enumerations --- source/wgpu/enums.d | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/source/wgpu/enums.d b/source/wgpu/enums.d index 2e9a576..fcc07bb 100644 --- a/source/wgpu/enums.d +++ b/source/wgpu/enums.d @@ -31,24 +31,35 @@ enum AddressMode : WGPUAddressMode { force32 = cast(WGPUAddressMode) 0x7FFFFFFF } -/// +/// Represents the graphics backends that wgpu can use. +/// See_Also: wgpu::Backends enum BackendType : WGPUBackendType { /// - _null, + undefined = WGPUBackendType_Undefined, + /// + _null = WGPUBackendType_Null, + /// + webGPU = WGPUBackendType_WebGPU, /// - webGPU, + d3d11 = WGPUBackendType_D3D11, /// - d3d11, + d3d12 = WGPUBackendType_D3D12, /// - d3d12, + metal = WGPUBackendType_Metal, /// - metal, + vulkan = WGPUBackendType_Vulkan, /// - vulkan, + openGL = WGPUBackendType_OpenGL, /// - openGL, + openGLES = WGPUBackendType_OpenGLES, + /// All the graphics APIs that for which wgpu offers first tier support. /// - openGLES, + /// Vulkan, Metal, DirectX 12, and Browser WebGPU + primary = cast(WGPUBackendType) (BackendType.vulkan | BackendType.metal | BackendType.d3d12 | BackendType.webGPU), + /// All the graphics APIs that for which wgpu offers second tier support. + /// + /// That is, OpenGL and DirectX 11. These may be unsupported or are still experimental. + secondary = cast(WGPUBackendType) (BackendType.openGL | BackendType.openGLES | BackendType.d3d11), force32 = cast(WGPUBackendType) 0x7FFFFFFF } @@ -315,6 +326,8 @@ enum PipelineStatisticName : WGPUPipelineStatisticName { /// enum PowerPreference : WGPUPowerPreference { + /// + undefined, /// lowPower, /// From 1dcfbcbb0b8ab12b8fdec0ed379bf0aca3df64bd Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 07:08:18 -0500 Subject: [PATCH 08/20] Fix `Instance.create` function and make adapter/device requests more robust --- source/wgpu/api.d | 60 ++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/source/wgpu/api.d b/source/wgpu/api.d index ca7c058..a91ede7 100644 --- a/source/wgpu/api.d +++ b/source/wgpu/api.d @@ -396,25 +396,33 @@ class FragmentState { extern (C) private void wgpu_request_adapter_callback( WGPURequestAdapterStatus status, WGPUAdapter id, const char* message, void* data ) { - assert(status == RequestAdapterStatus.success.asOriginalType); - assert(data !is null); + debug import std.stdio : write; + + assert(data !is null, "No adapter handle!"); auto adapter = cast(Adapter*) data; + adapter.status = cast(RequestAdapterStatus) status; + // TODO: Use the user-registered wgpu logging method instead + debug if (message !is null) message.fromStringz.to!string.write; + + if (status != RequestAdapterStatus.success.asOriginalType) return; assert(id !is null); adapter.id = id; - debug import std.stdio : writeln; - debug if (message !is null) message.fromStringz.to!string.writeln; } extern (C) private void wgpu_request_device_callback( WGPURequestDeviceStatus status, WGPUDevice id, const char* message, void* data ) { - assert(status == RequestDeviceStatus.success.asOriginalType); - assert(data !is null); + debug import std.stdio : write; + + assert(data !is null, "No device handle!"); auto device = cast(Device) data; + device.status = cast(RequestDeviceStatus) status; + // TODO: Use the user-registered wgpu logging method instead + debug if (message !is null) message.fromStringz.to!string.write; + + if (status != RequestDeviceStatus.success.asOriginalType) return; assert(id !is null); device.id = id; - debug import std.stdio : writeln; - debug if (message !is null) message.fromStringz.to!string.writeln; } /// Context for all other wgpu objects. Instance of wgpu. @@ -432,11 +440,18 @@ struct Instance { this.id = id; } + /// Release the given handle. + void destroy() { + if (id !is null) wgpuInstanceRelease(id); + id = null; + } + /// Create a new instance of wgpu. - static Instance create() { - assert(0, "Unimplemented in wgpu-native!"); - // auto desc = InstanceDescriptor(); - // return Instance(wgpuCreateInstance(&desc)); + /// Params: + /// backands = Controls from which backends wgpu will choose during instantiation. + static Instance create(BackendType backends = BackendType.primary) { + auto desc = InstanceDescriptor(); + return Instance(wgpuCreateInstance(&desc)); } /// Retrieves all available `Adapter`s that match the given `Backends`. @@ -455,9 +470,9 @@ struct Instance { /// /// Remarks: /// Use `Adapter.ready` to determine whether an Adapter was successfully requested. - static Adapter requestAdapter( - PowerPreference powerPreference = PowerPreference.highPerformance, - BackendType backendType = BackendType._null, + Adapter requestAdapter( + PowerPreference powerPreference = PowerPreference.undefined, + BackendType backendType = BackendType.primary, Flag!"forceFallbackAdapter" forceFallbackAdapter = No.forceFallbackAdapter ) { return requestAdapter( @@ -465,9 +480,9 @@ struct Instance { ); } /// ditto - static Adapter requestAdapter( - const Surface compatibleSurface, PowerPreference powerPreference = PowerPreference.highPerformance, - BackendType backendType = BackendType._null, + Adapter requestAdapter( + const Surface compatibleSurface, PowerPreference powerPreference = PowerPreference.undefined, + BackendType backendType = BackendType.primary, Flag!"forceFallbackAdapter" forceFallbackAdapter = No.forceFallbackAdapter ) @trusted { assert(compatibleSurface.id !is null, "Given compatible surface is not valid"); @@ -476,10 +491,9 @@ struct Instance { )); } /// ditto - static Adapter requestAdapter(RequestAdapterOptions options) @trusted { + Adapter requestAdapter(RequestAdapterOptions options) @trusted { Adapter adapter; - wgpuInstanceRequestAdapter(null, &options, &wgpu_request_adapter_callback, &adapter); - assert(adapter.ready); + wgpuInstanceRequestAdapter(this.id, &options, &wgpu_request_adapter_callback, &adapter); return adapter; } } @@ -492,6 +506,8 @@ struct Adapter { /// Handle identifier. WGPUAdapter id; + /// + RequestAdapterStatus status = RequestAdapterStatus.unknown; /// Whether this Adapter handle has finished being requested and is ready for use. bool ready() @property const { @@ -570,6 +586,8 @@ struct Adapter { /// See_Also: wgpu::Device class Device { package WGPUDevice id; + /// + RequestDeviceStatus status = RequestDeviceStatus.unknown; /// Label for this Device. string label; From b80a44270e8fb4136a5e62f4cb035b757c9fabbb Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 07:09:02 -0500 Subject: [PATCH 09/20] Implement `Instance.adapters` to enumerate available GPU adapters --- source/wgpu/api.d | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/source/wgpu/api.d b/source/wgpu/api.d index a91ede7..e338227 100644 --- a/source/wgpu/api.d +++ b/source/wgpu/api.d @@ -458,10 +458,17 @@ struct Instance { /// /// Params: /// backends = Backends from which to enumerate adapters. - static @property Adapter[] adapters(BackendType backends = BackendType._null) { + /// See_Also: wgpu::Instance.enumerate_adapters + @property Adapter[] adapters(BackendType backends = BackendType.primary) { + import std.algorithm : map; + import std.array : array; + assert(backends >= 0); - assert(0, "Unimplemented!"); - // TODO: Implement adapter enumerator as a custom range + auto options = WGPUInstanceEnumerateAdapterOptions(null, backends); + WGPUAdapter* adaptersPtr = null; + size_t count = wgpuInstanceEnumerateAdapters(this.id, &options, adaptersPtr); + if (adaptersPtr == null) return []; + return adaptersPtr[0 .. count].map!(x => (cast(Adapter) x)).array; } /// Retrieves a new Adapter, asynchronously. From d844eb15d5454d508d7c8de2a76f0054ddb2260a Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 09:58:02 -0500 Subject: [PATCH 10/20] Fix headless example --- examples/headless/source/app.d | 13 +++++++------ source/wgpu/api.d | 24 ++++++++++++++++++------ source/wgpu/enums.d | 29 +++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/examples/headless/source/app.d b/examples/headless/source/app.d index ac3fb59..b8355ce 100644 --- a/examples/headless/source/app.d +++ b/examples/headless/source/app.d @@ -1,3 +1,4 @@ +import std.conv : text; import std.stdio; import wgpu.api; @@ -6,14 +7,14 @@ import wgpu.api; void main() { writeln("Headless (windowless) Example"); - auto adapter = Instance.requestAdapter(PowerPreference.lowPower); - assert(adapter.ready, "Adapter instance was not initialized"); - writefln("Adapter properties: %s", adapter.properties); - writefln("Adapter limits: %s", adapter.limits); + auto instance = Instance.create(); + assert(instance.id, "Could not create WebGPU instance."); + + auto adapter = instance.requestAdapter(PowerPreference.lowPower); + assert(adapter.ready, "Adapter instance was not initialized: Adapter status: " ~ adapter.status.text); auto device = adapter.requestDevice(adapter.limits); - assert(device.ready, "Device is not ready"); - writefln("Device limits: %s", device.limits); + assert(device.ready, "Device is not ready: Device status: " ~ device.status.text); const width = 400; const height = 300; diff --git a/source/wgpu/api.d b/source/wgpu/api.d index e338227..70f895f 100644 --- a/source/wgpu/api.d +++ b/source/wgpu/api.d @@ -448,9 +448,16 @@ struct Instance { /// Create a new instance of wgpu. /// Params: - /// backands = Controls from which backends wgpu will choose during instantiation. - static Instance create(BackendType backends = BackendType.primary) { - auto desc = InstanceDescriptor(); + /// backends = Controls from which backends wgpu will choose during instantiation. + static Instance create(InstanceBackend backends = InstanceBackend.primary) { + const WGPUInstanceExtras extras = { + chain: WGPUChainedStruct(null, cast(WGPUSType) WGPUNativeSType.WGPUSType_InstanceExtras), + backends: backends, + // TODO: WGPUDx12Compiler dx12ShaderCompiler, + // TODO: const char * dxilPath, + // TODO: const char * dxcPath, + }; + auto desc = InstanceDescriptor(cast(ChainedStruct*) &extras); return Instance(wgpuCreateInstance(&desc)); } @@ -479,7 +486,7 @@ struct Instance { /// Use `Adapter.ready` to determine whether an Adapter was successfully requested. Adapter requestAdapter( PowerPreference powerPreference = PowerPreference.undefined, - BackendType backendType = BackendType.primary, + BackendType backendType = BackendType.undefined, Flag!"forceFallbackAdapter" forceFallbackAdapter = No.forceFallbackAdapter ) { return requestAdapter( @@ -489,7 +496,7 @@ struct Instance { /// ditto Adapter requestAdapter( const Surface compatibleSurface, PowerPreference powerPreference = PowerPreference.undefined, - BackendType backendType = BackendType.primary, + BackendType backendType = BackendType.undefined, Flag!"forceFallbackAdapter" forceFallbackAdapter = No.forceFallbackAdapter ) @trusted { assert(compatibleSurface.id !is null, "Given compatible surface is not valid"); @@ -1493,7 +1500,8 @@ class Texture { null, 0, // Offset paddedBytesPerRow, - size.depthOrArrayLayers == 1 ? 0 : rowsPerImage, + // FIXME: Make sure this is corect + size.depthOrArrayLayers * rowsPerImage, ); } @@ -1521,6 +1529,10 @@ class Texture { TextureViewDescriptor desc = { label: descriptor.label, format: descriptor.format, + baseMipLevel: 0, + mipLevelCount: descriptor.mipLevelCount, + baseArrayLayer: 0, + arrayLayerCount: size.depthOrArrayLayers, dimension: dimension.viewDimension(size.depthOrArrayLayers), aspect: TextureAspect.all, }; diff --git a/source/wgpu/enums.d b/source/wgpu/enums.d index fcc07bb..cf854fe 100644 --- a/source/wgpu/enums.d +++ b/source/wgpu/enums.d @@ -31,6 +31,31 @@ enum AddressMode : WGPUAddressMode { force32 = cast(WGPUAddressMode) 0x7FFFFFFF } +/// +enum InstanceBackend : WGPUInstanceBackend { + /// + vulkan = WGPUInstanceBackend_Vulkan, + /// + gl = WGPUInstanceBackend_GL, + /// + metal = WGPUInstanceBackend_Metal, + /// + dx12 = WGPUInstanceBackend_DX12, + /// + dx11 = WGPUInstanceBackend_DX11, + /// + browserWebGPU = WGPUInstanceBackend_BrowserWebGPU, + /// + primary = WGPUInstanceBackend_Primary, + /// + secondary = WGPUInstanceBackend_Secondary, + /// + all = WGPUInstanceBackend_Force32, + /// + none = WGPUInstanceBackend_None, + force32 = cast(WGPUInstanceBackend) 0x7FFFFFFF +} + /// Represents the graphics backends that wgpu can use. /// See_Also: wgpu::Backends enum BackendType : WGPUBackendType { @@ -302,6 +327,8 @@ enum IndexFormat : WGPUIndexFormat { /// enum LoadOp : WGPULoadOp { + /// + undefined, /// clear, /// @@ -485,6 +512,8 @@ enum StorageTextureAccess : WGPUStorageTextureAccess { /// enum StoreOp : WGPUStoreOp { + /// + undefined, /// store, /// From 6c4b9dd865afdc1d348643c75f1af74a71e41c19 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 10:14:11 -0500 Subject: [PATCH 11/20] Add an example of enumerating device adapters --- dub.json | 1 + examples/enumerate/.gitignore | 15 +++++++++ examples/enumerate/dub.json | 24 ++++++++++++++ examples/enumerate/source/app.d | 46 +++++++++++++++++++++++++ source/wgpu/api.d | 59 ++++++++++++++++++++++++++++++--- 5 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 examples/enumerate/.gitignore create mode 100644 examples/enumerate/dub.json create mode 100644 examples/enumerate/source/app.d diff --git a/dub.json b/dub.json index 4265c48..f647400 100644 --- a/dub.json +++ b/dub.json @@ -7,6 +7,7 @@ "license": "MIT", "copyright": "Copyright © 2020-2023, Chance Snow", "subPackages": [ + "examples/enumerate", "examples/headless", "examples/triangle", "examples/cube" diff --git a/examples/enumerate/.gitignore b/examples/enumerate/.gitignore new file mode 100644 index 0000000..926194f --- /dev/null +++ b/examples/enumerate/.gitignore @@ -0,0 +1,15 @@ +.dub +docs.json +__dummy.html +docs/ +/headless +headless.so +headless.dylib +headless.dll +headless.a +headless.lib +headless-test-* +*.exe +*.o +*.obj +*.lst diff --git a/examples/enumerate/dub.json b/examples/enumerate/dub.json new file mode 100644 index 0000000..40f831a --- /dev/null +++ b/examples/enumerate/dub.json @@ -0,0 +1,24 @@ +{ + "name": "enumerate", + "description": "Enumerate GPU adapters example", + "authors": [ + "Chance Snow" + ], + "license": "MIT", + "copyright": "Copyright © 2023, Chance Snow", + "targetType": "executable", + "targetPath": "../../bin", + "dependencies": { + "wgpu-d": { + "path": "../.." + } + }, + "preBuildCommands-osx": [ + "echo Fixing up libwgpu dylib path...", + "rm -f `find $PACKAGE_DIR/../../bin -name '*enumerate'`" + ], + "postBuildCommands-osx": [ + "echo Fixing up libwgpu dylib path...", + "install_name_tool -change /Users/runner/work/wgpu-native/wgpu-native/target/x86_64-apple-darwin/debug/deps/libwgpu_native.dylib @executable_path/../lib/libwgpu_native.dylib `find $PACKAGE_DIR/../../bin -name '*enumerate'`" + ] +} diff --git a/examples/enumerate/source/app.d b/examples/enumerate/source/app.d new file mode 100644 index 0000000..d5df954 --- /dev/null +++ b/examples/enumerate/source/app.d @@ -0,0 +1,46 @@ +import std.conv : text, to; +import std.stdio; +import std.string : fromStringz; + +import wgpu.api; + +// Adapted from https://github.com/gfx-rs/wgpu-rs/blob/f6123e4fe89f74754093c07b476099623b76dd08/examples/capture/main.rs +void main() { + writeln("Enumerating Device Adapters Example"); + + auto instance = Instance.create(); + assert(instance.id, "Could not create WebGPU instance."); + + writefln("Preferred backend type? %s", cast(BackendType) instance.report.backendType); + auto adapters = instance.adapters; + writefln("Found %d suitable adapters:", adapters.length); + for (int i = 0; i < adapters.length; i++) { + auto adapter = adapters[i]; + auto props = adapter.properties; + writefln("Adapter %d", i); + writefln("\tvendorID: %u", props.vendorID); + writefln("\tvendorName: %s", props.vendorName.fromStringz.to!string); + writefln("\tarchitecture: %s", props.architecture.fromStringz.to!string); + writefln("\tdeviceID: %u", props.deviceID); + writefln("\tname: %s", props.name.fromStringz.to!string); + writefln("\tdriverDescription: %s", props.driverDescription.fromStringz.to!string); + writefln("\tadapterType: %s", cast(AdapterType) props.adapterType); + writefln("\tbackendType: %s", cast(BackendType) props.backendType); + writeln(); + adapter.destroy(); + } + + auto adapter = instance.requestAdapter(PowerPreference.lowPower, BackendType.d3d12 | BackendType.vulkan); + assert(adapter.ready, "Adapter instance was not initialized: Adapter status: " ~ adapter.status.text); + writefln("Adapter limits:\n%s", adapter.limits.toString); + writeln(); + + auto device = adapter.requestDevice(adapter.limits); + assert(device.ready, "Device is not ready: Device status: " ~ device.status.text); + writefln("Device limits:\n%s", device.limits.toString); + writeln(); + + device.destroy(); + adapter.destroy(); + instance.destroy(); +} diff --git a/source/wgpu/api.d b/source/wgpu/api.d index 70f895f..f07748d 100644 --- a/source/wgpu/api.d +++ b/source/wgpu/api.d @@ -104,6 +104,47 @@ alias InstanceDescriptor = WGPUInstanceDescriptor; /// $(LI wgpu::Limits ) /// ) alias Limits = WGPULimits; + +string toString(Limits limits) { + import std.algorithm : joiner; + import std.array : array; + import std.format : format; + + return [ + "max texture dimension 1D: %u".format(limits.maxTextureDimension1D), + "max texture dimension 2D: %u".format(limits.maxTextureDimension2D), + "max texture dimension 3D: %u".format(limits.maxTextureDimension3D), + "max texture array layers: %u".format(limits.maxTextureArrayLayers), + "max bind groups: %u".format(limits.maxBindGroups), + "max bindings per bind group: %u".format(limits.maxBindingsPerBindGroup), + "max dynamic uniform buffers per pipeline layout: %u".format(limits.maxDynamicUniformBuffersPerPipelineLayout), + "max dynamic storage buffers per pipeline layout: %u".format(limits.maxDynamicStorageBuffersPerPipelineLayout), + "max sampled textures per shader stage: %u".format(limits.maxSampledTexturesPerShaderStage), + "max samplers per shader stage: %u".format(limits.maxSamplersPerShaderStage), + "max storage buffers per shader stage: %u".format(limits.maxStorageBuffersPerShaderStage), + "max storage textures per shader stage: %u".format(limits.maxStorageTexturesPerShaderStage), + "max uniform buffers per shader stage: %u".format(limits.maxUniformBuffersPerShaderStage), + "max uniform buffer binding size: %u bytes".format(limits.maxUniformBufferBindingSize), + "max storage buffer binding size: %u bytes".format(limits.maxStorageBufferBindingSize), + "min uniform buffer offset alignment: %u bytes".format(limits.minUniformBufferOffsetAlignment), + "min storage buffer offset alignment: %u bytes".format(limits.minStorageBufferOffsetAlignment), + "max vertex buffers: %u".format(limits.maxVertexBuffers), + "max buffer size: %u bytes".format(limits.maxBufferSize), + "max vertex attributes: %u".format(limits.maxVertexAttributes), + "max vertex buffer array stride: %u bytes".format(limits.maxVertexBufferArrayStride), + "max inter stage shader components: %u".format(limits.maxInterStageShaderComponents), + "max inter stage shader variables: %u".format(limits.maxInterStageShaderVariables), + "max color attachments: %u".format(limits.maxColorAttachments), + "max color attachment bytes per sample: %u".format(limits.maxColorAttachmentBytesPerSample), + "max compute workgroup storage size: %u bytes".format(limits.maxComputeWorkgroupStorageSize), + "max compute invocations per workgroup: %u".format(limits.maxComputeInvocationsPerWorkgroup), + "max compute workgroup size x: %u".format(limits.maxComputeWorkgroupSizeX), + "max compute workgroup size y: %u".format(limits.maxComputeWorkgroupSizeY), + "max compute workgroup size z: %u".format(limits.maxComputeWorkgroupSizeZ), + "max compute workgroups per dimension: %u".format(limits.maxComputeWorkgroupsPerDimension), + ].joiner("\n").array.to!string; +} + /// Origin of a copy to/from a texture. alias Origin3d = WGPUOrigin3D; /// Describes a `PipelineLayout`. @@ -461,21 +502,29 @@ struct Instance { return Instance(wgpuCreateInstance(&desc)); } + /// + auto @property report() { + WGPUGlobalReport report; + wgpuGenerateReport(this.id, &report); + return report; + } + /// Retrieves all available `Adapter`s that match the given `Backends`. /// /// Params: /// backends = Backends from which to enumerate adapters. /// See_Also: wgpu::Instance.enumerate_adapters - @property Adapter[] adapters(BackendType backends = BackendType.primary) { + @property Adapter[] adapters(InstanceBackend backends = InstanceBackend.all) { import std.algorithm : map; import std.array : array; assert(backends >= 0); auto options = WGPUInstanceEnumerateAdapterOptions(null, backends); - WGPUAdapter* adaptersPtr = null; - size_t count = wgpuInstanceEnumerateAdapters(this.id, &options, adaptersPtr); - if (adaptersPtr == null) return []; - return adaptersPtr[0 .. count].map!(x => (cast(Adapter) x)).array; + + size_t count = wgpuInstanceEnumerateAdapters(this.id, &options, null); + auto adapters = new WGPUAdapter[count]; + wgpuInstanceEnumerateAdapters(this.id, &options, cast(WGPUAdapter*) adapters.ptr); + return adapters.map!(id => Adapter(id)).array; } /// Retrieves a new Adapter, asynchronously. From daebb8b2c403e8aab6e560df956bcd717c918509 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 12:30:05 -0500 Subject: [PATCH 12/20] Compile glfw3 from source on Windows --- examples/triangle/dub.json | 5 +++-- examples/triangle/subprojects/glfw.Makefile | 23 ++++++++++++++++++++- examples/utils/dub.json | 5 +++-- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/examples/triangle/dub.json b/examples/triangle/dub.json index 917449d..2edb491 100644 --- a/examples/triangle/dub.json +++ b/examples/triangle/dub.json @@ -19,7 +19,7 @@ "subConfigurations": { "bindbc-glfw": "static" }, - "preGenerateCommands-posix": [ + "preGenerateCommands": [ "make -C subprojects -f glfw.Makefile" ], "preBuildCommands-osx": [ @@ -38,5 +38,6 @@ "-framework", "AppKit", "-framework", "Metal", "-framework", "QuartzCore" - ] + ], + "lflags-windows": ["/LIBPATH:$PACKAGE_DIR/subprojects/glfw/src"] } diff --git a/examples/triangle/subprojects/glfw.Makefile b/examples/triangle/subprojects/glfw.Makefile index 28fdb91..bfd2f73 100644 --- a/examples/triangle/subprojects/glfw.Makefile +++ b/examples/triangle/subprojects/glfw.Makefile @@ -1,5 +1,25 @@ VERSION := 3.3.6 +ifeq ($(OS),Windows_NT) + +.DEFAULT_GOAL := glfw/src/glfw3.lib + +ARCHIVE_ZIP := $(shell echo %USERPROFILE%/AppData/Local/Temp/glfw-$(VERSION).zip) +# See https://stackoverflow.com/a/7487697/1363247 +glfw: + @echo Downloading GLFW sources... + @curl -L https://github.com/glfw/glfw/releases/download/$(VERSION)/glfw-$(VERSION).zip --output $(ARCHIVE_ZIP) + @echo Unzipping GLFW sources... + @tar -xf $(ARCHIVE_ZIP) -C . + robocopy glfw-3.3.6 glfw /move /e /NFL /NDL /NJH /nc /ns /np + @rmdir glfw-3.3.6 /s /q +glfw/src/glfw3.lib: glfw + @echo Building GLFW... + cmake -B glfw glfw -DCMAKE_BUILD_TYPE=Release -DUSE_MSVC_RUNTIME_LIBRARY_DLL=OFF -DGLFW_BUILD_TESTS=OFF -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_DOCS=OFF -G"Unix Makefiles" + make -C glfw + +else + .DEFAULT_GOAL := glfw/src/libglfw3.a glfw/src/libglfw3.a: @@ -10,5 +30,6 @@ glfw/src/libglfw3.a: @mv glfw-3.3.6 glfw @echo "Building GLFW…" @cd glfw && \ - cmake . -DGLFW_BUILD_TESTS=OFF -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_DOCS=OFF && \ + cmake -B glfw glfw -DCMAKE_BUILD_TYPE=Release -DGLFW_BUILD_TESTS=OFF -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_DOCS=OFF -G"Unix Makefiles" make +endif diff --git a/examples/utils/dub.json b/examples/utils/dub.json index 54c745a..e62f95b 100644 --- a/examples/utils/dub.json +++ b/examples/utils/dub.json @@ -19,7 +19,7 @@ "subConfigurations": { "bindbc-glfw": "static" }, - "preGenerateCommands-posix": [ + "preGenerateCommands": [ "make -C subprojects -f glfw.Makefile" ], "libs": ["glfw3"], @@ -31,5 +31,6 @@ "-framework", "AppKit", "-framework", "Metal", "-framework", "QuartzCore" - ] + ], + "lflags-windows": ["/LIBPATH:$PACKAGE_DIR/subprojects/glfw/src"] } From d91b1e7646ef84a6f08d4b674132f719b5268bd9 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 12:49:57 -0500 Subject: [PATCH 13/20] Fix Surface creation --- examples/triangle/dub.json | 1 + examples/triangle/source/app.d | 25 ++++++++++++++++++------- examples/utils/dub.json | 1 + source/wgpu/api.d | 34 ---------------------------------- 4 files changed, 20 insertions(+), 41 deletions(-) diff --git a/examples/triangle/dub.json b/examples/triangle/dub.json index 2edb491..29b7cbc 100644 --- a/examples/triangle/dub.json +++ b/examples/triangle/dub.json @@ -33,6 +33,7 @@ "libs-linux": [ "X11" ], + "libs-windows": ["gdi32"], "lflags-posix": ["-L$PACKAGE_DIR/subprojects/glfw/src"], "lflags-osx": [ "-framework", "AppKit", diff --git a/examples/triangle/source/app.d b/examples/triangle/source/app.d index a1ee312..61a53bd 100644 --- a/examples/triangle/source/app.d +++ b/examples/triangle/source/app.d @@ -63,6 +63,8 @@ void main() { import std.string : toStringz; import std.typecons : Yes; + // Create GLFW window + const title = "Triangle Example"; title.writeln; @@ -82,6 +84,11 @@ void main() { assert(window !is null, "Could not create window:\n\t" ~ lastError); glfwSetWindowSizeLimits(window, 640, 480, GLFW_DONT_CARE, GLFW_DONT_CARE); + // Create WebGPU resources + + auto instance = Instance.create(); + assert(instance.id, "Could not create WebGPU instance."); + Surface surface; version (OSX) { import std.exception : enforce; @@ -90,20 +97,24 @@ void main() { auto metalLayer = CAMetalLayer.classOf.layer; nativeWindow.contentView.wantsLayer = true; nativeWindow.contentView.layer = metalLayer; - surface = Surface.fromMetalLayer(metalLayer.asId); + surface = Surface.fromMetalLayer(instance, metalLayer.asId); + } else { + version (Linux) surface = Surface.fromXlib(instance, glfwGetX11Display().enforce, glfwGetX11Window(window).enforce); + else { + version (Windows) { + import core.sys.windows.windows : GetModuleHandleA; + surface = Surface.fromWindowsHwnd(instance, GetModuleHandleA(null), glfwGetWin32Window(window)); + } + else static assert(0, "Unsupported target platform"); + } } - else version (Linux) surface = Surface.fromXlib(assert(glfwGetX11Display()), assert(glfwGetX11Window(window))); - // TODO: Integrate with Windows platform, i.e. Surface.fromWindowsHwnd - else static assert(0, "Unsupported target platform"); assert(surface.id !is null, "Could not create native surface"); - auto adapter = Instance.requestAdapter(surface, PowerPreference.lowPower); + auto adapter = instance.requestAdapter(surface, PowerPreference.lowPower); assert(adapter.ready, "Adapter instance was not initialized"); - writefln("Adapter properties: %s", adapter.properties); auto device = adapter.requestDevice(adapter.limits); assert(device.ready, "Device is not ready"); - writefln("Device limits: %s", device.limits); auto swapChainFormat = surface.preferredFormat(adapter); auto swapChain = device.createSwapChain( diff --git a/examples/utils/dub.json b/examples/utils/dub.json index e62f95b..2a50f6a 100644 --- a/examples/utils/dub.json +++ b/examples/utils/dub.json @@ -26,6 +26,7 @@ "libs-linux": [ "X11" ], + "libs-windows": ["gdi32"], "lflags-posix": ["-L$PACKAGE_DIR/subprojects/glfw/src"], "lflags-osx": [ "-framework", "AppKit", diff --git a/source/wgpu/api.d b/source/wgpu/api.d index f07748d..2754d02 100644 --- a/source/wgpu/api.d +++ b/source/wgpu/api.d @@ -1023,15 +1023,6 @@ struct Surface { version (OSX) { /// Create a new `Surface` from a Metal layer. - static Surface fromMetalLayer(void* layer, string label = null) { - auto metalLayer = SurfaceDescriptorFromMetalLayer( - ChainedStruct(null, cast(WGPUSType) SType.surfaceDescriptorFromMetalLayer.asOriginalType), - layer - ); - auto desc = SurfaceDescriptor(cast(ChainedStruct*) &metalLayer, label is null ? null : label.toStringz); - return Surface(wgpuInstanceCreateSurface(null, &desc)); - } - /// ditto static Surface fromMetalLayer(Instance instance, void* layer, string label = null) { auto metalLayer = SurfaceDescriptorFromMetalLayer( ChainedStruct(null, cast(WGPUSType) SType.surfaceDescriptorFromMetalLayer.asOriginalType), @@ -1042,21 +1033,10 @@ struct Surface { } } else version (D_Ddoc) { /// Create a new `Surface` from a Metal layer. - static Surface fromMetalLayer(void* layer, string label = null); - /// ditto static Surface fromMetalLayer(Instance instance, void* layer, string label = null); } version (Windows) { /// Create a new `Surface` from a Windows window handle. - static Surface fromWindowsHwnd(void* _hinstance, void* hwnd, string label = null) { - auto windowsHwnd = SurfaceDescriptorFromWindowsHwnd( - ChainedStruct(null, cast(WGPUSType) SType.surfaceDescriptorFromWindowsHwnd.asOriginalType), - _hinstance, hwnd - ); - auto desc = SurfaceDescriptor(cast(ChainedStruct*) &windowsHwnd, label is null ? null : label.toStringz); - return Surface(wgpuInstanceCreateSurface(null, &desc)); - } - /// ditto static Surface fromWindowsHwnd(Instance instance, void* _hinstance, void* hwnd, string label = null) { auto windowsHwnd = SurfaceDescriptorFromWindowsHwnd( ChainedStruct(null, cast(WGPUSType) SType.surfaceDescriptorFromWindowsHwnd.asOriginalType), @@ -1067,21 +1047,10 @@ struct Surface { } } else version (D_Ddoc) { /// Create a new `Surface` from a Windows window handle. - static Surface fromWindowsHwnd(void* _hinstance, void* hwnd, string label = null); - /// ditto static Surface fromWindowsHwnd(Instance instance, void* _hinstance, void* hwnd, string label = null); } version (linux) { /// Create a new `Surface` from a Xlib window handle. - static Surface fromXlib(void* display, uint window, string label = null) { - auto xlib = SurfaceDescriptorFromXlib( - ChainedStruct(null, cast(WGPUSType) SType.surfaceDescriptorFromXlib.asOriginalType), - display, window - ); - auto desc = SurfaceDescriptor(cast(ChainedStruct*) &xlib, label is null ? null : label.toStringz); - return Surface(wgpuInstanceCreateSurface(null, &desc)); - } - /// ditto static Surface fromXlib(Instance instance, void* display, uint window, string label = null) { auto xlib = SurfaceDescriptorFromXlib( ChainedStruct(null, cast(WGPUSType) SType.surfaceDescriptorFromXlib.asOriginalType), @@ -1091,9 +1060,6 @@ struct Surface { return Surface(wgpuInstanceCreateSurface(instance.id, &desc)); } } else version (D_Ddoc) { - /// Create a new `Surface` from a Xlib window handle. - static Surface fromXlib(void* display, uint window, string label = null); - /// ditto static Surface fromXlib(Instance instance, void* display, uint window, string label = null); } // TODO: Support Wayland with a `linux-wayland` version config once upstream wgpu-native supports it From 61414bbe18f04de9ebcf107ea6e9f5ef27ccc506 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 12:50:22 -0500 Subject: [PATCH 14/20] Fix triangle shaders --- examples/triangle/source/app.d | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/triangle/source/app.d b/examples/triangle/source/app.d index 61a53bd..127049e 100644 --- a/examples/triangle/source/app.d +++ b/examples/triangle/source/app.d @@ -20,24 +20,24 @@ extern(C) void wgpu_glfw_error_callback(int error, const char* description) noth } } -enum string triangleShader = `[[stage(vertex)]] -fn vs_main([[builtin(vertex_index)]] in_vertex_index: u32) -> [[builtin(position)]] vec4 { +enum string triangleShader = `@vertex +fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4 { let x = f32(i32(in_vertex_index) - 1); let y = f32(i32(in_vertex_index & 1u) * 2 - 1); return vec4(x, y, 0.0, 1.0); } -[[stage(fragment)]] -fn fs_main() -> [[location(0)]] vec4 { - return vec4(1.0, 0.0, 1.0, 1.0); +@fragment +fn fs_main() -> @location(0) vec4 { + return vec4(1.0, 0.0, 0.0, 1.0); }`; enum string fullScreenQuadShader = `struct VertexOutput { - [[builtin(position)]] clip_position: vec4; - [[location(0)]] tex_coords: vec2; + @builtin(position) clip_position: vec4; + @location(0) tex_coords: vec2; }; -[[stage(vertex)]] -fn vs_main([[builtin(vertex_index)]] in_vertex_index: u32) -> VertexOutput { +@vertex +fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> VertexOutput { var out: VertexOutput; let x = f32(i32(in_vertex_index) - 1); let y = f32(i32(in_vertex_index & 1u) * 2 - 1); @@ -46,13 +46,13 @@ fn vs_main([[builtin(vertex_index)]] in_vertex_index: u32) -> VertexOutput { return out; } -[[group(0), binding(0)]] +@group(0) @binding(0) var t_diffuse: texture_2d; -[[group(0), binding(1)]] +@group(0) @binding(1) var s_diffuse: sampler; -[[stage(fragment)]] -fn fs_main(in: VertexOutput) -> [[location(0)]] vec4 { +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4 { return textureSample(t_diffuse, s_diffuse, in.tex_coords); }`; From 1429cd8636bc3c5a023bbd0f84ae5cabd9620fb9 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 12:51:12 -0500 Subject: [PATCH 15/20] Fix undefined `glfwGetWin32Window` --- examples/triangle/source/app.d | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/triangle/source/app.d b/examples/triangle/source/app.d index 127049e..35d9d6e 100644 --- a/examples/triangle/source/app.d +++ b/examples/triangle/source/app.d @@ -307,6 +307,12 @@ auto bindDelegate(Func, string file = __FILE__, size_t line = __LINE__)(Func f) return &func; } +version (Windows) { + import core.sys.windows.windows : GetModuleHandleA, HWND; + // FIXME: What's wrong with https://github.com/BindBC/bindbc-glfw/blob/6529ce4f67f69839a93de5e0bbe1150fab30d633/source/bindbc/glfw/bindstatic.d#L172 + extern(C) @nogc nothrow HWND glfwGetWin32Window(GLFWwindow* window); +} + // mac OS interop with the Objective-C Runtime version (OSX) { import core.attribute : selector; From edfd95410411fa842bc406c191c77609213ec2f0 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 12:53:17 -0500 Subject: [PATCH 16/20] Fix `Surface.fromXlib` doc comment --- source/wgpu/api.d | 1 + 1 file changed, 1 insertion(+) diff --git a/source/wgpu/api.d b/source/wgpu/api.d index 2754d02..17f5fc6 100644 --- a/source/wgpu/api.d +++ b/source/wgpu/api.d @@ -1060,6 +1060,7 @@ struct Surface { return Surface(wgpuInstanceCreateSurface(instance.id, &desc)); } } else version (D_Ddoc) { + /// Create a new `Surface` from a Xlib window handle. static Surface fromXlib(Instance instance, void* display, uint window, string label = null); } // TODO: Support Wayland with a `linux-wayland` version config once upstream wgpu-native supports it From 4944057ba1f536e6a41ad462b3dc01aa8558ffde Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 13:07:00 -0500 Subject: [PATCH 17/20] Refactor shader sources --- examples/triangle/source/app.d | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/examples/triangle/source/app.d b/examples/triangle/source/app.d index 35d9d6e..5ea5c3b 100644 --- a/examples/triangle/source/app.d +++ b/examples/triangle/source/app.d @@ -21,39 +21,36 @@ extern(C) void wgpu_glfw_error_callback(int error, const char* description) noth } enum string triangleShader = `@vertex -fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4 { +fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4f { let x = f32(i32(in_vertex_index) - 1); let y = f32(i32(in_vertex_index & 1u) * 2 - 1); - return vec4(x, y, 0.0, 1.0); + return vec4f(x, y, 0.0, 1.0); } @fragment -fn fs_main() -> @location(0) vec4 { - return vec4(1.0, 0.0, 0.0, 1.0); +fn fs_main() -> @location(0) vec4f { + return vec4f(1.0, 0.0, 0.0, 1.0); }`; enum string fullScreenQuadShader = `struct VertexOutput { - @builtin(position) clip_position: vec4; - @location(0) tex_coords: vec2; + @builtin(position) clip_position: vec4f; + @location(0) tex_coords: vec2f; }; @vertex fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> VertexOutput { - var out: VertexOutput; let x = f32(i32(in_vertex_index) - 1); let y = f32(i32(in_vertex_index & 1u) * 2 - 1); - out.clip_position = vec4(x, y, 0.0, 1.0); - out.tex_coords = vec2(x, y); - return out; + return VertexOutput(vec4f(x, y, 0.0, 1.0), vec2f(x, y)); } @group(0) @binding(0) -var t_diffuse: texture_2d; +var diffuse: texture_2d; @group(0) @binding(1) -var s_diffuse: sampler; +var diffuseSampler: sampler; @fragment -fn fs_main(in: VertexOutput) -> @location(0) vec4 { - return textureSample(t_diffuse, s_diffuse, in.tex_coords); +fn fs_main(in: VertexOutput) -> @location(0) vec4f { + return textureSample(diffuse, diffuseSampler, in.tex_coords); }`; // Adapted from https://github.com/gfx-rs/wgpu-native/blob/v0.10.4.1/examples/triangle/main.c From 7e85602d032547b456e1036af73dd888b4b4d5ad Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 13:08:51 -0500 Subject: [PATCH 18/20] Organize windows system imports --- examples/triangle/source/app.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/triangle/source/app.d b/examples/triangle/source/app.d index 5ea5c3b..781c0c6 100644 --- a/examples/triangle/source/app.d +++ b/examples/triangle/source/app.d @@ -305,7 +305,7 @@ auto bindDelegate(Func, string file = __FILE__, size_t line = __LINE__)(Func f) } version (Windows) { - import core.sys.windows.windows : GetModuleHandleA, HWND; + import core.sys.windows.windows : HWND; // FIXME: What's wrong with https://github.com/BindBC/bindbc-glfw/blob/6529ce4f67f69839a93de5e0bbe1150fab30d633/source/bindbc/glfw/bindstatic.d#L172 extern(C) @nogc nothrow HWND glfwGetWin32Window(GLFWwindow* window); } From 5d6c82e13ca125ce6f7111fbf7636b4218a974ae Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 13:18:17 -0500 Subject: [PATCH 19/20] Fix targets for wgpu-native binaries --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 311e8ba..227f4d7 100644 --- a/Makefile +++ b/Makefile @@ -38,13 +38,13 @@ ifndef LIB_WGPU $(error Unsupported target OS '$(OS)') endif LIB_WGPU_SOURCE := subprojects/wgpu -wgpu: $(LIB_WGPU_SOURCE) lib/$(LIB_WGPU) +wgpu: lib/$(LIB_WGPU) .PHONY: wgpu -$(LIB_WGPU_SOURCE): subprojects/wgpu.Makefile +$(LIB_WGPU_SOURCE)/$(LIB_WGPU): subprojects/wgpu.Makefile @make -C subprojects -f wgpu.Makefile ifneq ($(OS),Windows_NT) -wgpu: $(LIB_WGPU).a -$(LIB_WGPU).a: $(LIB_WGPU_SOURCE)/libwgpu_native.a +wgpu: lib/libwgpu_native.a +lib/libwgpu_native.a: $(LIB_WGPU_SOURCE)/libwgpu_native.a @mkdir -p lib @cp $(LIB_WGPU_SOURCE)/libwgpu_native.a lib/. endif From 652c20b2a00e35400bf6d6ca1866a55c0cd56f17 Mon Sep 17 00:00:00 2001 From: Chance Snow Date: Thu, 28 Sep 2023 13:52:51 -0500 Subject: [PATCH 20/20] Use only static libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wgpu-native binaries ships with static libraries 🎉 --- Makefile | 17 ++++++----------- dub.json | 9 --------- subprojects/wgpu.Makefile | 15 ++++++++------- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/Makefile b/Makefile index 227f4d7..4938859 100644 --- a/Makefile +++ b/Makefile @@ -25,14 +25,15 @@ all: docs ################################################# # Subprojects ################################################# +# wgpu-native binaries ships with static libraries 🎉 ifeq ($(OS),Darwin) - LIB_WGPU := libwgpu_native.dylib + LIB_WGPU := libwgpu_native.a endif ifeq ($(OS),Linux) - LIB_WGPU := libwgpu_native.so + LIB_WGPU := libwgpu_native.a endif ifeq ($(OS),Windows_NT) - LIB_WGPU := wgpu_native.dll + LIB_WGPU := wgpu_native.lib endif ifndef LIB_WGPU $(error Unsupported target OS '$(OS)') @@ -40,15 +41,9 @@ endif LIB_WGPU_SOURCE := subprojects/wgpu wgpu: lib/$(LIB_WGPU) .PHONY: wgpu -$(LIB_WGPU_SOURCE)/$(LIB_WGPU): subprojects/wgpu.Makefile +$(LIB_WGPU_SOURCE): subprojects/wgpu.Makefile @make -C subprojects -f wgpu.Makefile -ifneq ($(OS),Windows_NT) -wgpu: lib/libwgpu_native.a -lib/libwgpu_native.a: $(LIB_WGPU_SOURCE)/libwgpu_native.a - @mkdir -p lib - @cp $(LIB_WGPU_SOURCE)/libwgpu_native.a lib/. -endif -lib/$(LIB_WGPU): $(LIB_WGPU_SOURCE)/$(LIB_WGPU) +lib/$(LIB_WGPU): $(LIB_WGPU_SOURCE) ifeq ($(OS),Windows_NT) @if not exist lib mkdir lib @xcopy $(subst /,\\,$(LIB_WGPU_SOURCE))\\$(LIB_WGPU) lib /y >NUL diff --git a/dub.json b/dub.json index f647400..640fadb 100644 --- a/dub.json +++ b/dub.json @@ -60,14 +60,5 @@ "userenv", "bcrypt", "ntdll" - ], - "copyFiles-linux": [ - "lib/libwgpu_native.so" - ], - "copyFiles-osx": [ - "lib/libwgpu_native.dylib" - ], - "copyFiles-windows": [ - "lib/wgpu_native.dll" ] } diff --git a/subprojects/wgpu.Makefile b/subprojects/wgpu.Makefile index 241f814..4db5ce4 100644 --- a/subprojects/wgpu.Makefile +++ b/subprojects/wgpu.Makefile @@ -18,20 +18,21 @@ ifeq ($(OS),Windows_NT) endif ARCH ?= $(shell uname -m) +# wgpu-native binaries ships with static libraries 🎉 ifeq ($(OS),Darwin) - RELEASE := macos - LIB_WGPU := libwgpu_native.dylib + RELEASE := macos + LIB_WGPU := libwgpu_native.a endif ifeq ($(OS),Linux) - RELEASE := linux - LIB_WGPU := libwgpu_native.so + RELEASE := linux + LIB_WGPU := libwgpu_native.a endif ifeq ($(OS),Windows_NT) - RELEASE := windows - LIB_WGPU := wgpu_native.dll + RELEASE := windows + LIB_WGPU := wgpu_native.lib endif ifndef RELEASE - $(error Unsupported WebGPU target OS: $(OS)) + $(error Unsupported WebGPU target OS: $(OS)) endif CONFIG ?= debug