Skip to content

Instantly share code, notes, and snippets.

@nickav
Forked from mmozeiko/win32_webgpu.c
Last active February 6, 2025 15:39
Show Gist options
  • Save nickav/88c7a1344fe68da8a45ab25c4795b237 to your computer and use it in GitHub Desktop.
Save nickav/88c7a1344fe68da8a45ab25c4795b237 to your computer and use it in GitHub Desktop.
setting up and using WebGPU in C using Dawn and GLFW
//
// NOTE(nick):
//
// 1. Get Dawn:
//
// on Windows get a prebuilt copy of dawn from: https://github.com/mmozeiko/build-dawn
//
// on MacOS build from source with:
// git clone https://github.com/google/dawn
// cd dawn
// mkdir -p out/Release
// cd out/Release
// cmake ../.. -DCMAKE_BUILD_TYPE=Release
// make
//
// Place the library in the same directory as your source files.
//
// 2. Build
//
// on MacOS rename to the file to .m and build with:
// libs="$(pkg-config --cflags --libs glfw3) -framework Cocoa -framework AppKit -framework QuartzCore -L. -lwebgpu_dawn"
// clang -g -I glfwwebgpu.m $libs -o main
//
#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#define NOMINMAX
#include <windows.h>
#pragma comment (lib, "gdi32")
#pragma comment (lib, "user32")
#pragma comment (lib, "webgpu_dawn")
#endif // _WIN32
#include "webgpu.h"
#include "glfw3webgpu.c"
#include <GLFW/glfw3.h>
#define WEBGPU_STR(str) (WGPUStringView) { .data = str, .length = sizeof(str) - 1 }
#define Unused(x) ((void)(x))
#ifdef __APPLE__
static void FatalError(WGPUStringView message)
{
char msg[1024];
snprintf(msg, sizeof(msg), "%.*s", (int)message.length, message.data);
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"Fatal Error"];
[alert setInformativeText:[NSString stringWithUTF8String:msg]];
[alert setAlertStyle:NSAlertStyleCritical];
[alert runModal];
abort();
}
#endif // __APPLE__
#ifdef _WIN32
static void FatalError(WGPUStringView message)
{
char msg[1024];
snprintf(msg, sizeof(msg), "%.*s", (int)message.length, message.data);
MessageBoxA(NULL, msg, "Error", MB_ICONEXCLAMATION);
ExitProcess(0);
}
#endif // _WIN32
static void OnDeviceError(const WGPUDevice *device, WGPUErrorType type, WGPUStringView message, void* userdata1, void* userdata2)
{
FatalError(message);
}
static bool adapterRequestEnded = false;
static void OnAdapterRequestEnded(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void *userData)
{
if (status == WGPURequestAdapterStatus_Success)
{
WGPUAdapter *result = (WGPUAdapter*)userData;
if (*result == NULL)
{
*result = adapter;
}
}
else
{
printf("Could not get WebGPU adapter!\n");
FatalError(message);
}
adapterRequestEnded = true;
}
struct Vertex
{
float position[2];
float uv[2];
float color[3];
};
const uint32_t kVertexStride = sizeof(struct Vertex);
static const struct Vertex kVertexData[] =
{
{ { -0.00f, +0.75f }, { 25.0f, 50.0f }, { 1, 0, 0 } },
{ { +0.75f, -0.50f }, { 0.0f, 0.0f }, { 0, 1, 0 } },
{ { -0.75f, -0.50f }, { 50.0f, 0.0f }, { 0, 0, 1 } },
};
const uint32_t kTextureWidth = 2;
const uint32_t kTextureHeight = 2;
const WGPUTextureFormat kSwapChainFormat = WGPUTextureFormat_BGRA8Unorm;
void wgpuPollEvents(WGPUDevice device, bool yieldToWebBrowser)
{
Unused(device);
Unused(yieldToWebBrowser);
#if defined(WEBGPU_BACKEND_DAWN)
wgpuDeviceTick(device);
#elif defined(WEBGPU_BACKEND_WGPU)
wgpuDevicePoll(device, false, NULL);
#elif defined(WEBGPU_BACKEND_EMSCRIPTEN)
if (yieldToWebBrowser) {
emscripten_sleep(100);
}
#endif
}
int main()
{
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
GLFWwindow *window = glfwCreateWindow(1280, 800, "WebGPU Window", NULL, NULL);
WGPUInstance instance = wgpuCreateInstance(NULL);
WGPUSurface surface = glfwGetWGPUSurface(instance, window);
WGPURequestAdapterOptions options = {};
options.nextInChain = NULL;
options.compatibleSurface = surface;
WGPUAdapter adapter = NULL;
{
adapterRequestEnded = false;
WGPURequestAdapterOptions options =
{
.compatibleSurface = surface,
// .powerPreference = WGPUPowerPreference_HighPerformance,
};
wgpuInstanceRequestAdapter(instance, &options, &OnAdapterRequestEnded, &adapter);
// when using Emscripten, we need to hand the control back to the browser until the adapter is ready.
#ifdef __EMSCRIPTEN__
while (!adapterRequestEnded)
{
emscripten_sleep(100);
}
#endif // __EMSCRIPTEN__
if (!adapter)
{
FatalError(WEBGPU_STR("Failed to get WebGPU adapter"));
}
}
{
WGPUAdapterInfo info = {0};
wgpuAdapterGetInfo(adapter, &info);
const char* adapter_types[] =
{
[WGPUAdapterType_DiscreteGPU] = "Discrete GPU",
[WGPUAdapterType_IntegratedGPU] = "Integrated GPU",
[WGPUAdapterType_CPU] = "CPU",
[WGPUAdapterType_Unknown] = "unknown",
};
char temp[1024];
snprintf(temp, sizeof(temp),
"Device = %.*s\n"
"Description = %.*s\n"
"Vendor = %.*s\n"
"Architecture = %.*s\n"
"Adapter Type = %s\n",
(int)info.device.length, info.device.data,
(int)info.description.length, info.description.data,
(int)info.vendor.length, info.vendor.data,
(int)info.architecture.length, info.architecture.data,
adapter_types[info.adapterType]);
printf("%s", temp);
}
WGPUDevice device = NULL;
{
// if you want to be sure device will support things you'll use, you can specify requirements here:
//WGPUSupportedLimits supported = { 0 };
//wgpuAdapterGetLimits(adapter, &supported);
//supported.limits.maxTextureDimension2D = kTextureWidth;
//supported.limits.maxBindGroups = 1;
//supported.limits.maxBindingsPerBindGroup = 3; // uniform buffer for vertex shader, and texture + sampler for fragment
//supported.limits.maxSampledTexturesPerShaderStage = 1;
//supported.limits.maxSamplersPerShaderStage = 1;
//supported.limits.maxUniformBuffersPerShaderStage = 1;
//supported.limits.maxUniformBufferBindingSize = 4 * 4 * sizeof(float); // 4x4 matrix
//supported.limits.maxVertexBuffers = 1;
//supported.limits.maxBufferSize = sizeof(kVertexData);
//supported.limits.maxVertexAttributes = 3; // pos, texcoord, color
//supported.limits.maxVertexBufferArrayStride = kVertexStride;
//supported.limits.maxColorAttachments = 1;
WGPUDeviceDescriptor desc =
{
// notify on errors
.uncapturedErrorCallbackInfo2.callback = &OnDeviceError,
// extra features: https://dawn.googlesource.com/dawn/+/refs/heads/main/src/dawn/native/Features.cpp
//.requiredFeaturesCount = n
//.requiredFeatures = (WGPUFeatureName[]) { ... }
//.requiredLimits = &(WGPURequiredLimits) { .limits = supported.limits },
};
device = wgpuAdapterCreateDevice(adapter, &desc);
assert(device && "Failed to create WebGPU device");
}
WGPUQueue queue = wgpuDeviceGetQueue(device);
WGPUBuffer vbuffer;
{
WGPUBufferDescriptor desc =
{
.usage = WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst,
.size = sizeof(kVertexData),
};
vbuffer = wgpuDeviceCreateBuffer(device, &desc);
wgpuQueueWriteBuffer(queue, vbuffer, 0, kVertexData, sizeof(kVertexData));
}
// uniform buffer for one 4x4 float matrix
WGPUBuffer ubuffer;
{
WGPUBufferDescriptor desc =
{
.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst,
.size = 4 * 4 * sizeof(float), // 4x4 matrix
};
ubuffer = wgpuDeviceCreateBuffer(device, &desc);
}
WGPUTextureView texture_view;
{
// checkerboard texture, with 50% transparency on black colors
unsigned int pixels[] =
{
0x80000000, 0xffffffff,
0xffffffff, 0x80000000,
};
WGPUTextureDescriptor desc =
{
.usage = WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst,
.dimension = WGPUTextureDimension_2D,
.size = { kTextureWidth, kTextureHeight, 1 },
.format = WGPUTextureFormat_RGBA8Unorm,
.mipLevelCount = 1,
.sampleCount = 1,
};
WGPUTexture texture = wgpuDeviceCreateTexture(device, &desc);
// upload pixels
WGPUImageCopyTexture dst =
{
.texture = texture,
.mipLevel = 0,
};
WGPUTextureDataLayout src =
{
.offset = 0,
.bytesPerRow = kTextureWidth * 4, // 4 bytes per pixel
.rowsPerImage = kTextureHeight,
};
wgpuQueueWriteTexture(queue, &dst, pixels, sizeof(pixels), &src, &desc.size);
WGPUTextureViewDescriptor view_desc =
{
.format = desc.format,
.dimension = WGPUTextureViewDimension_2D,
.baseMipLevel = 0,
.mipLevelCount = 1,
.baseArrayLayer = 0,
.arrayLayerCount = 1,
.aspect = WGPUTextureAspect_All,
};
texture_view = wgpuTextureCreateView(texture, &view_desc);
wgpuTextureRelease(texture);
}
// compile shader
WGPUShaderModule shaders;
{
#if 1
// shader in WebGPU Shading Language (WGSL)
static const char wgsl[] =
"struct VertexIn { \n"
" @location(0) aPos : vec2f, \n"
" @location(1) aTex : vec2f, \n"
" @location(2) aCol : vec3f \n"
"} \n"
" \n"
"struct VertexOut { \n"
" @builtin(position) vPos : vec4f, \n"
" @location(0) vTex : vec2f, \n"
" @location(1) vCol : vec3f \n"
"} \n"
" \n"
"@group(0) @binding(0) var<uniform> uTransform : mat4x4f; \n"
"@group(0) @binding(1) var myTexture : texture_2d<f32>; \n"
"@group(0) @binding(2) var mySampler : sampler; \n"
" \n"
"@vertex \n"
"fn vs(in : VertexIn) -> VertexOut { \n"
" var out : VertexOut; \n"
" out.vPos = uTransform * vec4f(in.aPos, 0.0, 1.0); \n"
" out.vTex = in.aTex; \n"
" out.vCol = in.aCol; \n"
" return out; \n"
"} \n"
" \n"
"@fragment \n"
"fn fs(in : VertexOut) -> @location(0) vec4f { \n"
" var tex : vec4f = textureSample(myTexture, mySampler, in.vTex); \n"
" return vec4f(in.vCol, 1.0) * tex; \n"
"} \n"
;
WGPUShaderModuleDescriptor desc =
{
.nextInChain = &((WGPUShaderSourceWGSL)
{
.chain.sType = WGPUSType_ShaderSourceWGSL,
.code = WEBGPU_STR(wgsl),
}).chain,
};
#else
// alternative way of using SPIR-V shader binary code as input, non-standard for WebGPU spec
uint32_t code[4096];
// to create SPIR-V binary from WGSL source, first save code above into "shader.wgsl" file
// then run "tint.exe shader.wgsl -o shader.spv" to get "shader.spv" output file
// you can also create "shader.spv" file by compiling HLSL/GLSL code to SPIR-V using custom tools
FILE* f = fopen("shader.spv", "rb");
int code_size = (int)fread(code, sizeof(code[0]), sizeof(code)/sizeof(code[0]), f);
fclose(f);
WGPUShaderModuleDescriptor desc =
{
.nextInChain = &((WGPUShaderSourceSPIRV)
{
.chain.sType = WGPUSType_ShaderSourceSPIRV,
.codeSize = code_size,
.code = code,
}).chain,
};
#endif
shaders = wgpuDeviceCreateShaderModule(device, &desc);
// to get compiler error messages explicitly use wgpuShaderModuleGetCompilationInfo
// otherwise they will be reported with device error callback
}
WGPUSampler sampler;
{
WGPUSamplerDescriptor desc =
{
.addressModeU = WGPUAddressMode_Repeat,
.addressModeV = WGPUAddressMode_Repeat,
.addressModeW = WGPUAddressMode_Repeat,
.magFilter = WGPUFilterMode_Nearest,
.minFilter = WGPUFilterMode_Nearest,
.mipmapFilter = WGPUMipmapFilterMode_Nearest,
.lodMinClamp = 0.f,
.lodMaxClamp = 1.f,
.compare = WGPUCompareFunction_Undefined,
.maxAnisotropy = 1,
};
sampler = wgpuDeviceCreateSampler(device, &desc);
}
WGPUBindGroup bind_group;
WGPURenderPipeline pipeline;
{
WGPUBindGroupLayoutDescriptor bind_group_layout_desc =
{
.entryCount = 3,
.entries = (WGPUBindGroupLayoutEntry[])
{
// uniform buffer for vertex shader
{
.binding = 0,
.visibility = WGPUShaderStage_Vertex,
.buffer.type = WGPUBufferBindingType_Uniform,
},
// texture for fragment shader
{
.binding = 1,
.visibility = WGPUShaderStage_Fragment,
.texture.sampleType = WGPUTextureSampleType_Float,
.texture.viewDimension = WGPUTextureViewDimension_2D,
.texture.multisampled = 0,
},
// sampler for fragment shader
{
.binding = 2,
.visibility = WGPUShaderStage_Fragment,
.sampler.type = WGPUSamplerBindingType_Filtering,
},
},
};
WGPUBindGroupLayout bind_group_layout = wgpuDeviceCreateBindGroupLayout(device, &bind_group_layout_desc);
WGPUPipelineLayoutDescriptor pipeline_layout_desc =
{
.bindGroupLayoutCount = 1,
.bindGroupLayouts = (WGPUBindGroupLayout[]) { bind_group_layout },
};
WGPUPipelineLayout pipeline_layout = wgpuDeviceCreatePipelineLayout(device, &pipeline_layout_desc);
WGPURenderPipelineDescriptor pipeline_desc =
{
.layout = pipeline_layout,
// draw triangle list, no index buffer, no culling
.primitive =
{
.topology = WGPUPrimitiveTopology_TriangleList,
// .stripIndexFormat = WGPUIndexFormat_Uint16,
.frontFace = WGPUFrontFace_CCW,
.cullMode = WGPUCullMode_None,
},
// vertex shader
.vertex =
{
.module = shaders,
.entryPoint = WEBGPU_STR("vs"),
.bufferCount = 1,
.buffers = (WGPUVertexBufferLayout[])
{
// one vertex buffer as input
{
.arrayStride = kVertexStride,
.stepMode = WGPUVertexStepMode_Vertex,
.attributeCount = 3,
.attributes = (WGPUVertexAttribute[])
{
{ WGPUVertexFormat_Float32x2, offsetof(struct Vertex, position), 0 },
{ WGPUVertexFormat_Float32x2, offsetof(struct Vertex, uv), 1 },
{ WGPUVertexFormat_Float32x3, offsetof(struct Vertex, color), 2 },
},
},
},
},
// fragment shader
.fragment = &(WGPUFragmentState)
{
.module = shaders,
.entryPoint = WEBGPU_STR("fs"),
.targetCount = 1,
.targets = (WGPUColorTargetState[])
{
// writing to one output, with alpha-blending enabled
{
.format = kSwapChainFormat,
.blend = &(WGPUBlendState)
{
.color.operation = WGPUBlendOperation_Add,
.color.srcFactor = WGPUBlendFactor_SrcAlpha,
.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha,
.alpha.operation = WGPUBlendOperation_Add,
.alpha.srcFactor = WGPUBlendFactor_SrcAlpha,
.alpha.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha,
},
.writeMask = WGPUColorWriteMask_All,
},
},
},
// if depth/stencil buffer usage/testing is needed
//.depthStencil = &(WGPUDepthStencilState) { ... },
// no multisampling
.multisample =
{
.count = 1,
.mask = 0xffffffff,
.alphaToCoverageEnabled = 0,
},
};
pipeline = wgpuDeviceCreateRenderPipeline(device, &pipeline_desc);
wgpuPipelineLayoutRelease(pipeline_layout);
WGPUBindGroupDescriptor bind_group_desc =
{
.layout = bind_group_layout,
.entryCount = 3,
.entries = (WGPUBindGroupEntry[])
{
// uniform buffer for vertex shader
{ .binding = 0, .buffer = ubuffer, .offset = 0, .size = 4 * 4 * sizeof(float) },
// texure for fragment shader
{ .binding = 1, .textureView = texture_view },
// sampler for fragment shader
{ .binding = 2, .sampler = sampler },
},
};
bind_group = wgpuDeviceCreateBindGroup(device, &bind_group_desc);
wgpuBindGroupLayoutRelease(bind_group_layout);
}
// release resources that are now owned by pipeline and will not be used in this code later
wgpuSamplerRelease(sampler);
wgpuTextureViewRelease(texture_view);
glfwSwapInterval(1);
float angle = 0;
int current_width = 0;
int current_height = 0;
float then = glfwGetTime();
bool swap_chain = false;
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
wgpuPollEvents(device, false);
int width, height;
glfwGetFramebufferSize(window, &width, &height);
// process all internal async events or error callbacks
// this is native-code specific functionality, because in browser's WebGPU everything works with JS event loop
wgpuInstanceProcessEvents(instance);
// resize swap chain if needed
if (!swap_chain || width != current_width || height != current_height)
{
if (swap_chain)
{
// release old swap chain
wgpuSurfaceUnconfigure(surface);
swap_chain = false;
}
// resize to new size for non-zero window size
if (width != 0 && height != 0)
{
WGPUSurfaceConfiguration config =
{
.device = device,
.format = kSwapChainFormat,
.usage = WGPUTextureUsage_RenderAttachment,
.width = width,
.height = height,
.presentMode = WGPUPresentMode_Fifo, // WGPUPresentMode_Mailbox // WGPUPresentMode_Immediate
};
wgpuSurfaceConfigure(surface, &config);
swap_chain = true;
}
current_width = width;
current_height = height;
}
if (swap_chain)
{
float now = glfwGetTime();
float delta = then - now;
then = now;
// update 4x4 rotation matrix in uniform
{
angle += delta * 2.0f * (float)M_PI / 20.0f; // full rotation in 20 seconds
angle = fmodf(angle, 2.0f * (float)M_PI);
float aspect = (float)height / width;
float matrix[16] =
{
cosf(angle) * aspect, -sinf(angle), 0, 0,
sinf(angle) * aspect, cosf(angle), 0, 0,
0, 0, 0, 0,
0, 0, 0, 1,
};
wgpuQueueWriteBuffer(queue, ubuffer, 0, matrix, sizeof(matrix));
}
WGPUSurfaceTexture surfaceTex;
wgpuSurfaceGetCurrentTexture(surface, &surfaceTex);
if (surfaceTex.status != WGPUSurfaceGetCurrentTextureStatus_Success)
{
FatalError(WEBGPU_STR("Cannot acquire next swap chain texture!"));
}
WGPUTextureViewDescriptor surfaceViewDesc =
{
.format = wgpuTextureGetFormat(surfaceTex.texture),
.dimension = WGPUTextureViewDimension_2D,
.mipLevelCount = 1,
.arrayLayerCount = 1,
.aspect = WGPUTextureAspect_All,
.usage = WGPUTextureUsage_RenderAttachment,
};
WGPUTextureView surfaceView = wgpuTextureCreateView(surfaceTex.texture, &surfaceViewDesc);
assert(surfaceView);
WGPURenderPassDescriptor desc =
{
.colorAttachmentCount = 1,
.colorAttachments = (WGPURenderPassColorAttachment[])
{
// one output to write to, initially cleared with background color
{
.view = surfaceView,
.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED,
.loadOp = WGPULoadOp_Clear,
.storeOp = WGPUStoreOp_Store,
.clearValue = { 0.392, 0.584, 0.929, 1.0 }, // r,g,b,a
},
},
};
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, NULL);
// encode render pass
WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &desc);
{
wgpuRenderPassEncoderSetViewport(pass, 0.f, 0.f, (float)width, (float)height, 0.f, 1.f);
// draw the triangle using 3 vertices in vertex buffer
wgpuRenderPassEncoderSetPipeline(pass, pipeline);
wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group, 0, NULL);
wgpuRenderPassEncoderSetVertexBuffer(pass, 0, vbuffer, 0, WGPU_WHOLE_SIZE);
wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0);
}
wgpuRenderPassEncoderEnd(pass);
// submit to queue
WGPUCommandBuffer command = wgpuCommandEncoderFinish(encoder, NULL);
wgpuQueueSubmit(queue, 1, &command);
wgpuCommandBufferRelease(command);
wgpuCommandEncoderRelease(encoder);
wgpuTextureViewRelease(surfaceView);
// present to output
wgpuSurfacePresent(surface);
}
}
return 0;
}
/**
* This is an extension of GLFW for WebGPU, abstracting away the details of
* OS-specific operations.
*
* This file is part of the "Learn WebGPU for C++" book.
* https://eliemichel.github.io/LearnWebGPU
*
* Most of this code comes from the wgpu-native triangle example:
* https://github.com/gfx-rs/wgpu-native/blob/master/examples/triangle/main.c
*
* MIT License
* Copyright (c) 2022-2023 Elie Michel and the wgpu-native authors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <webgpu/webgpu.h>
#define WGPU_TARGET_MACOS 1
#define WGPU_TARGET_LINUX_X11 2
#define WGPU_TARGET_WINDOWS 3
#define WGPU_TARGET_LINUX_WAYLAND 4
#define WGPU_TARGET_EMSCRIPTEN 5
#if defined(__EMSCRIPTEN__)
#define WGPU_TARGET WGPU_TARGET_EMSCRIPTEN
#elif defined(_WIN32)
#define WGPU_TARGET WGPU_TARGET_WINDOWS
#elif defined(__APPLE__)
#define WGPU_TARGET WGPU_TARGET_MACOS
#elif defined(_GLFW_WAYLAND)
#define WGPU_TARGET WGPU_TARGET_LINUX_WAYLAND
#else
#define WGPU_TARGET WGPU_TARGET_LINUX_X11
#endif
#if WGPU_TARGET == WGPU_TARGET_MACOS
#include <Foundation/Foundation.h>
#include <QuartzCore/CAMetalLayer.h>
#endif
#include <GLFW/glfw3.h>
#if WGPU_TARGET == WGPU_TARGET_MACOS
#define GLFW_EXPOSE_NATIVE_COCOA
#elif WGPU_TARGET == WGPU_TARGET_LINUX_X11
#define GLFW_EXPOSE_NATIVE_X11
#elif WGPU_TARGET == WGPU_TARGET_LINUX_WAYLAND
#define GLFW_EXPOSE_NATIVE_WAYLAND
#elif WGPU_TARGET == WGPU_TARGET_WINDOWS
#define GLFW_EXPOSE_NATIVE_WIN32
#endif
#if !defined(__EMSCRIPTEN__)
#include <GLFW/glfw3native.h>
#endif
WGPUSurface glfwGetWGPUSurface(WGPUInstance instance, GLFWwindow* window) {
#if WGPU_TARGET == WGPU_TARGET_MACOS
{
id metal_layer = [CAMetalLayer layer];
NSWindow* ns_window = glfwGetCocoaWindow(window);
[ns_window.contentView setWantsLayer: YES];
[ns_window.contentView setLayer: metal_layer];
WGPUSurfaceDescriptorFromMetalLayer fromMetalLayer = {};
fromMetalLayer.chain.next = NULL;
fromMetalLayer.chain.sType = WGPUSType_SurfaceSourceMetalLayer;
fromMetalLayer.layer = metal_layer;
WGPUSurfaceDescriptor surfaceDescriptor = {};
surfaceDescriptor.nextInChain = &fromMetalLayer.chain;
// surfaceDescriptor.label = NULL;
return wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#elif WGPU_TARGET == WGPU_TARGET_LINUX_X11
{
Display* x11_display = glfwGetX11Display();
Window x11_window = glfwGetX11Window(window);
WGPUSurfaceDescriptorFromXlibWindow fromXlibWindow;
fromXlibWindow.chain.next = NULL;
fromXlibWindow.chain.sType = WGPUSType_SurfaceDescriptorFromXlibWindow;
fromXlibWindow.display = x11_display;
fromXlibWindow.window = x11_window;
WGPUSurfaceDescriptor surfaceDescriptor;
surfaceDescriptor.nextInChain = &fromXlibWindow.chain;
surfaceDescriptor.label = NULL;
return wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#elif WGPU_TARGET == WGPU_TARGET_LINUX_WAYLAND
{
struct wl_display* wayland_display = glfwGetWaylandDisplay();
struct wl_surface* wayland_surface = glfwGetWaylandWindow(window);
WGPUSurfaceDescriptorFromWaylandSurface fromWaylandSurface;
fromWaylandSurface.chain.next = NULL;
fromWaylandSurface.chain.sType = WGPUSType_SurfaceDescriptorFromWaylandSurface;
fromWaylandSurface.display = wayland_display;
fromWaylandSurface.surface = wayland_surface;
WGPUSurfaceDescriptor surfaceDescriptor;
surfaceDescriptor.nextInChain = &fromWaylandSurface.chain;
surfaceDescriptor.label = NULL;
return wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#elif WGPU_TARGET == WGPU_TARGET_WINDOWS
{
HWND hwnd = glfwGetWin32Window(window);
HINSTANCE hinstance = GetModuleHandle(NULL);
WGPUSurfaceDescriptorFromWindowsHWND fromWindowsHWND;
fromWindowsHWND.chain.next = NULL;
fromWindowsHWND.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND;
fromWindowsHWND.hinstance = hinstance;
fromWindowsHWND.hwnd = hwnd;
WGPUSurfaceDescriptor surfaceDescriptor;
surfaceDescriptor.nextInChain = &fromWindowsHWND.chain;
surfaceDescriptor.label = NULL;
return wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#elif WGPU_TARGET == WGPU_TARGET_EMSCRIPTEN
{
WGPUSurfaceDescriptorFromCanvasHTMLSelector fromCanvasHTMLSelector;
fromCanvasHTMLSelector.chain.next = NULL;
fromCanvasHTMLSelector.chain.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector;
fromCanvasHTMLSelector.selector = "canvas";
WGPUSurfaceDescriptor surfaceDescriptor;
surfaceDescriptor.nextInChain = &fromCanvasHTMLSelector.chain;
surfaceDescriptor.label = NULL;
return wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#else
#error "Unsupported WGPU_TARGET"
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment