From 2f44430536cf391c2681fa801fbdfc2d83d437d4 Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Fri, 15 Aug 2025 23:01:43 +0200 Subject: [PATCH 01/14] Added native support for creating WebGPU surfaces Note that this commit only adds support when running on Wayland. --- include/GLFW/glfw3.h | 39 ++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 10 ++++---- src/internal.h | 42 ++++++++++++++++++++++++++++++++++ src/webgpu.c | 54 ++++++++++++++++++++++++++++++++++++++++++++ src/wl_platform.h | 2 ++ src/wl_window.c | 19 ++++++++++++++++ 6 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 src/webgpu.c diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 79b062884..516852a99 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -115,6 +115,10 @@ extern "C" { * VK_USE_PLATFORM_WIN32_KHR) so we offer our replacement symbols after it. */ +#if defined(GLFW_INCLUDE_WEBGPU) + #include +#endif /* WebGPU header */ + /* It is customary to use APIENTRY for OpenGL function pointer declarations on * all platforms. Additionally, the Windows OpenGL header needs APIENTRY. */ @@ -6525,6 +6529,41 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window #endif /*VK_VERSION_1_0*/ +#if defined(WEBGPU_H_) + +/*! @brief Provide the address of the `wgpuInstanceCreateSurface` function to GLFW. + * + * This function passes the address provided for the `wgpuInstanceCreateSurface` function + * to GLFW. + * + * @param[in] addr The address of the `wgpuInstanceCreateSurface` function. + * + * @since Added in version 3.5 + * + * @ingroup webgpu + */ +GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)); + +/*! @brief Creates a WebGPU surface for the specified window. + * + * This function creates a WebGPU surface for the specified window. + * + * If the surface could not be created this function returns `NULL`. + * + * It is the callers responsibility to destroy the surface. The surface + * must be destroyed using `wgpuSurfaceRelease`. + * + * @param[in] instance The WebGPU instance to create the surface in. + * @param[in] window The window to create the surface for. + * @return The handle of the surface. This is `NULL` if an error occurred. + * + * @since Added in version 3.5 + * + * @ingroup webgpu + */ +GLFWAPI WGPUSurface glfwCreateWindowWGPUSurface(WGPUInstance instance, GLFWwindow* window); + +#endif /*WEBGPU_H_*/ /************************************************************************* * Global definition cleanup diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2cbe8a733..0b482ac33 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,9 +3,10 @@ add_library(glfw ${GLFW_LIBRARY_TYPE} "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h" internal.h platform.h mappings.h - context.c init.c input.c monitor.c platform.c vulkan.c window.c - egl_context.c osmesa_context.c null_platform.h null_joystick.h - null_init.c null_monitor.c null_window.c null_joystick.c) + context.c init.c input.c monitor.c platform.c vulkan.c webgpu.c + window.c egl_context.c osmesa_context.c null_platform.h + null_joystick.h null_init.c null_monitor.c null_window.c + null_joystick.c) # The time, thread and module code is shared between all backends on a given OS, # including the null backend, which still needs those bits to be functional @@ -128,7 +129,8 @@ set_target_properties(glfw PROPERTIES C_STANDARD 99 C_EXTENSIONS OFF DEFINE_SYMBOL _GLFW_BUILD_DLL - FOLDER "GLFW3") + FOLDER "GLFW3" + EXPORT_COMPILE_COMMANDS ON) target_include_directories(glfw PUBLIC "$" diff --git a/src/internal.h b/src/internal.h index 4f097aa82..1ee044c93 100644 --- a/src/internal.h +++ b/src/internal.h @@ -331,6 +331,42 @@ typedef PFN_vkVoidFunction (APIENTRY * PFN_vkGetInstanceProcAddr)(VkInstance,con typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,uint32_t*,VkExtensionProperties*); #define vkGetInstanceProcAddr _glfw.vk.GetInstanceProcAddr +// forward declare WebGPU types +typedef struct WGPUInstanceImpl* WGPUInstance; +typedef struct WGPUSurfaceImpl* WGPUSurface; + +typedef struct WGPUStringView { + char const * data; + size_t length; +} WGPUStringView; + +typedef enum WGPUSType { + WGPUSType_ShaderSourceSPIRV = 0x00000001, + WGPUSType_ShaderSourceWGSL = 0x00000002, + WGPUSType_RenderPassMaxDrawCount = 0x00000003, + WGPUSType_SurfaceSourceMetalLayer = 0x00000004, + WGPUSType_SurfaceSourceWindowsHWND = 0x00000005, + WGPUSType_SurfaceSourceXlibWindow = 0x00000006, + WGPUSType_SurfaceSourceWaylandSurface = 0x00000007, + WGPUSType_SurfaceSourceAndroidNativeWindow = 0x00000008, + WGPUSType_SurfaceSourceXCBWindow = 0x00000009, + WGPUSType_Force32 = 0x7FFFFFFF +} WGPUSType; + +typedef struct WGPUChainedStruct { + struct WGPUChainedStruct const * next; + WGPUSType sType; +} WGPUChainedStruct; + +typedef struct WGPUSurfaceDescriptor { + WGPUChainedStruct const * nextInChain; + WGPUStringView label; +} WGPUSurfaceDescriptor; + +typedef WGPUSurface (*PFN_wgpuInstanceCreateSurface)(WGPUInstance, const WGPUSurfaceDescriptor*); + +#define wgpuInstanceCreateSurface _glfw.wgpu.instanceCreateSurface + #include "platform.h" #define GLFW_NATIVE_INCLUDE_NONE @@ -759,6 +795,8 @@ struct _GLFWplatform void (*getRequiredInstanceExtensions)(char**); GLFWbool (*getPhysicalDevicePresentationSupport)(VkInstance,VkPhysicalDevice,uint32_t); VkResult (*createWindowSurface)(VkInstance,_GLFWwindow*,const VkAllocationCallbacks*,VkSurfaceKHR*); + // webgpu + WGPUSurface (*createWindowWGPUSurface)(WGPUInstance, _GLFWwindow*); }; // Library global data @@ -875,6 +913,10 @@ struct _GLFWlibrary GLFWbool EXT_headless_surface; } vk; + struct { + PFN_wgpuInstanceCreateSurface instanceCreateSurface; + } wgpu; + struct { GLFWmonitorfun monitor; GLFWjoystickfun joystick; diff --git a/src/webgpu.c b/src/webgpu.c new file mode 100644 index 000000000..7a32cfe3b --- /dev/null +++ b/src/webgpu.c @@ -0,0 +1,54 @@ +//======================================================================== +// GLFW 3.5 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) Sebastian Dawid +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include + +GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)) { + _GLFW_REQUIRE_INIT() + _glfw.wgpu.instanceCreateSurface = addr; +} + +GLFWAPI WGPUSurface glfwCreateWindowWGPUSurface(WGPUInstance instance, GLFWwindow* handle) { + _GLFW_REQUIRE_INIT_OR_RETURN(NULL) + + _GLFWwindow* window = (_GLFWwindow*)handle; + + assert(window != NULL); + assert(instance != NULL); + assert(_glfw.wgpu.instanceCreateSurface != NULL); + + if (window->context.client != GLFW_NO_API) + { + _glfwInputError(GLFW_INVALID_VALUE, + "WebGPU: Window surface creation requires the window to have the client API set to GLFW_NO_API"); + return NULL; + }; + + return _glfw.platform.createWindowWGPUSurface(instance, window); +} diff --git a/src/wl_platform.h b/src/wl_platform.h index c3e456931..1f910319a 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -678,6 +678,8 @@ void _glfwGetRequiredInstanceExtensionsWayland(char** extensions); GLFWbool _glfwGetPhysicalDevicePresentationSupportWayland(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); VkResult _glfwCreateWindowSurfaceWayland(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); +WGPUSurface _glfwCreateWindowWGPUSurfaceWayland(WGPUInstance instance, _GLFWwindow* window); + void _glfwFreeMonitorWayland(_GLFWmonitor* monitor); void _glfwGetMonitorPosWayland(_GLFWmonitor* monitor, int* xpos, int* ypos); void _glfwGetMonitorContentScaleWayland(_GLFWmonitor* monitor, float* xscale, float* yscale); diff --git a/src/wl_window.c b/src/wl_window.c index b33a2262f..f19d5a73d 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -3326,6 +3326,25 @@ VkResult _glfwCreateWindowSurfaceWayland(VkInstance instance, return err; } +typedef struct WGPUSurfaceSourceWaylandSurface { + WGPUChainedStruct chain; + void * display; + void * surface; +} WGPUSurfaceSourceWaylandSurface; + +WGPUSurface _glfwCreateWindowWGPUSurfaceWayland(WGPUInstance instance, _GLFWwindow* window) { + WGPUSurfaceSourceWaylandSurface waylandSurface; + waylandSurface.chain.sType = WGPUSType_SurfaceSourceWaylandSurface; + waylandSurface.chain.next = NULL; + waylandSurface.surface = window->wl.surface; + waylandSurface.display = _glfw.wl.display; + + WGPUSurfaceDescriptor surfaceDescriptor; + surfaceDescriptor.nextInChain = &waylandSurface.chain; + surfaceDescriptor.label = (WGPUStringView){ NULL, SIZE_MAX }; + + return wgpuInstanceCreateSurface(instance, &surfaceDescriptor); +} ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// From 8dc6a1f00b4bc1bda2d00e2fcb758ada0301ae1c Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Fri, 15 Aug 2025 23:12:47 +0200 Subject: [PATCH 02/14] Add surface creation function to wl platform initialization --- src/wl_init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wl_init.c b/src/wl_init.c index ef9e45036..f6490c913 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -515,7 +515,8 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) .getEGLNativeWindow = _glfwGetEGLNativeWindowWayland, .getRequiredInstanceExtensions = _glfwGetRequiredInstanceExtensionsWayland, .getPhysicalDevicePresentationSupport = _glfwGetPhysicalDevicePresentationSupportWayland, - .createWindowSurface = _glfwCreateWindowSurfaceWayland + .createWindowSurface = _glfwCreateWindowSurfaceWayland, + .createWindowWGPUSurface = _glfwCreateWindowWGPUSurfaceWayland }; void* module = _glfwPlatformLoadModule("libwayland-client.so.0"); From cb0c28908f78ddef154e55a9fc3fda43f2829108 Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Fri, 15 Aug 2025 23:38:04 +0200 Subject: [PATCH 03/14] Try loading surface creation function via `dlsym` instead --- include/GLFW/glfw3.h | 13 ------------- src/init.c | 2 ++ src/internal.h | 2 ++ src/webgpu.c | 11 +++++++---- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 516852a99..f920bd7b1 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -6531,19 +6531,6 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window #if defined(WEBGPU_H_) -/*! @brief Provide the address of the `wgpuInstanceCreateSurface` function to GLFW. - * - * This function passes the address provided for the `wgpuInstanceCreateSurface` function - * to GLFW. - * - * @param[in] addr The address of the `wgpuInstanceCreateSurface` function. - * - * @since Added in version 3.5 - * - * @ingroup webgpu - */ -GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)); - /*! @brief Creates a WebGPU surface for the specified window. * * This function creates a WebGPU surface for the specified window. diff --git a/src/init.c b/src/init.c index dbd5a900c..d31d111e5 100644 --- a/src/init.c +++ b/src/init.c @@ -402,6 +402,8 @@ GLFWAPI int glfwInit(void) if (!_glfwSelectPlatform(_glfw.hints.init.platformID, &_glfw.platform)) return GLFW_FALSE; + _glfwLoadWGPUInstanceCreateSurfaceAddr(); + if (!_glfw.platform.init()) { terminate(); diff --git a/src/internal.h b/src/internal.h index 1ee044c93..4b8601401 100644 --- a/src/internal.h +++ b/src/internal.h @@ -1051,6 +1051,8 @@ GLFWbool _glfwInitVulkan(int mode); void _glfwTerminateVulkan(void); const char* _glfwGetVulkanResultString(VkResult result); +void _glfwLoadWGPUInstanceCreateSurfaceAddr(void); + size_t _glfwEncodeUTF8(char* s, uint32_t codepoint); char** _glfwParseUriList(char* text, int* count); diff --git a/src/webgpu.c b/src/webgpu.c index 7a32cfe3b..5b841bc27 100644 --- a/src/webgpu.c +++ b/src/webgpu.c @@ -28,11 +28,14 @@ #include #include - -GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)) { - _GLFW_REQUIRE_INIT() - _glfw.wgpu.instanceCreateSurface = addr; +#if defined(_GLFW_WIN32) +// TODO: Implement Window version +#else +#include +void _glfwLoadWGPUInstanceCreateSurfaceAddr() { + _glfw.wgpu.instanceCreateSurface = dlsym(RTLD_DEFAULT, "wgpuInstanceCreateSurface"); } +#endif GLFWAPI WGPUSurface glfwCreateWindowWGPUSurface(WGPUInstance instance, GLFWwindow* handle) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL) From 37812b7e36c392a68f617e820f3a113026af8e66 Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Fri, 15 Aug 2025 23:39:30 +0200 Subject: [PATCH 04/14] Revert "Try loading surface creation function via `dlsym` instead" This reverts commit cb0c28908f78ddef154e55a9fc3fda43f2829108. --- include/GLFW/glfw3.h | 13 +++++++++++++ src/init.c | 2 -- src/internal.h | 2 -- src/webgpu.c | 11 ++++------- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index f920bd7b1..516852a99 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -6531,6 +6531,19 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window #if defined(WEBGPU_H_) +/*! @brief Provide the address of the `wgpuInstanceCreateSurface` function to GLFW. + * + * This function passes the address provided for the `wgpuInstanceCreateSurface` function + * to GLFW. + * + * @param[in] addr The address of the `wgpuInstanceCreateSurface` function. + * + * @since Added in version 3.5 + * + * @ingroup webgpu + */ +GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)); + /*! @brief Creates a WebGPU surface for the specified window. * * This function creates a WebGPU surface for the specified window. diff --git a/src/init.c b/src/init.c index d31d111e5..dbd5a900c 100644 --- a/src/init.c +++ b/src/init.c @@ -402,8 +402,6 @@ GLFWAPI int glfwInit(void) if (!_glfwSelectPlatform(_glfw.hints.init.platformID, &_glfw.platform)) return GLFW_FALSE; - _glfwLoadWGPUInstanceCreateSurfaceAddr(); - if (!_glfw.platform.init()) { terminate(); diff --git a/src/internal.h b/src/internal.h index 4b8601401..1ee044c93 100644 --- a/src/internal.h +++ b/src/internal.h @@ -1051,8 +1051,6 @@ GLFWbool _glfwInitVulkan(int mode); void _glfwTerminateVulkan(void); const char* _glfwGetVulkanResultString(VkResult result); -void _glfwLoadWGPUInstanceCreateSurfaceAddr(void); - size_t _glfwEncodeUTF8(char* s, uint32_t codepoint); char** _glfwParseUriList(char* text, int* count); diff --git a/src/webgpu.c b/src/webgpu.c index 5b841bc27..7a32cfe3b 100644 --- a/src/webgpu.c +++ b/src/webgpu.c @@ -28,14 +28,11 @@ #include #include -#if defined(_GLFW_WIN32) -// TODO: Implement Window version -#else -#include -void _glfwLoadWGPUInstanceCreateSurfaceAddr() { - _glfw.wgpu.instanceCreateSurface = dlsym(RTLD_DEFAULT, "wgpuInstanceCreateSurface"); + +GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)) { + _GLFW_REQUIRE_INIT() + _glfw.wgpu.instanceCreateSurface = addr; } -#endif GLFWAPI WGPUSurface glfwCreateWindowWGPUSurface(WGPUInstance instance, GLFWwindow* handle) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL) From 205bf6c768c2de25b706af27152bbbc1381781bc Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Fri, 15 Aug 2025 23:57:40 +0200 Subject: [PATCH 05/14] Add WebGPU support to X11 --- src/x11_init.c | 3 ++- src/x11_platform.h | 2 ++ src/x11_window.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/x11_init.c b/src/x11_init.c index 6b34c2639..c90babe19 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1246,7 +1246,8 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) .getEGLNativeWindow = _glfwGetEGLNativeWindowX11, .getRequiredInstanceExtensions = _glfwGetRequiredInstanceExtensionsX11, .getPhysicalDevicePresentationSupport = _glfwGetPhysicalDevicePresentationSupportX11, - .createWindowSurface = _glfwCreateWindowSurfaceX11 + .createWindowSurface = _glfwCreateWindowSurfaceX11, + .createWindowWGPUSurface = _glfwCreateWindowWGPUSurfaceX11 }; // HACK: If the application has left the locale as "C" then both wide diff --git a/src/x11_platform.h b/src/x11_platform.h index 30326c5be..af1ad3145 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -963,6 +963,8 @@ void _glfwGetRequiredInstanceExtensionsX11(char** extensions); GLFWbool _glfwGetPhysicalDevicePresentationSupportX11(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); VkResult _glfwCreateWindowSurfaceX11(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); +WGPUSurface _glfwCreateWindowWGPUSurfaceX11(WGPUInstance instance, _GLFWwindow* window); + void _glfwFreeMonitorX11(_GLFWmonitor* monitor); void _glfwGetMonitorPosX11(_GLFWmonitor* monitor, int* xpos, int* ypos); void _glfwGetMonitorContentScaleX11(_GLFWmonitor* monitor, float* xscale, float* yscale); diff --git a/src/x11_window.c b/src/x11_window.c index 322349f08..5a60f7a02 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -3282,6 +3282,53 @@ VkResult _glfwCreateWindowSurfaceX11(VkInstance instance, } } +typedef struct WGPUSurfaceSourceXCBWindow { + WGPUChainedStruct chain; + void * connection; + uint32_t window; +} WGPUSurfaceSourceXCBWindow; + +typedef struct WGPUSurfaceSourceXlibWindow { + WGPUChainedStruct chain; + void * display; + uint64_t window; +} WGPUSurfaceSourceXlibWindow; + +WGPUSurface _glfwCreateWindowWGPUSurfaceX11(WGPUInstance instance, _GLFWwindow* window) +{ + WGPUSurfaceDescriptor surfaceDescriptor; + + if (_glfw.x11.x11xcb.handle) + { + WGPUSurfaceSourceXCBWindow xcbSurface; + xcbSurface.chain.sType = WGPUSType_SurfaceSourceXCBWindow; + xcbSurface.chain.next = NULL; + xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); + if (!connection) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to retrieve XCB connection"); + return NULL; + } + xcbSurface.connection = connection; + xcbSurface.window = window->x11.handle; + surfaceDescriptor.nextInChain = &xcbSurface.chain; + } + else + { + WGPUSurfaceSourceXlibWindow xlibSurface; + xlibSurface.chain.sType = WGPUSType_SurfaceSourceXlibWindow; + xlibSurface.chain.next = NULL; + xlibSurface.display = _glfw.x11.display; + xlibSurface.window = window->x11.handle; + surfaceDescriptor.nextInChain = &xlibSurface.chain; + } + + surfaceDescriptor.label = (WGPUStringView){ NULL, SIZE_MAX }; + + return wgpuInstanceCreateSurface(instance, &surfaceDescriptor); +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// From 4cbb4db70c5f2f15f4c11c9eb30e544995ded693 Mon Sep 17 00:00:00 2001 From: Sebastian Dawid Date: Sat, 16 Aug 2025 00:30:16 +0200 Subject: [PATCH 06/14] Add WebGPU support for Windows --- src/win32_init.c | 3 ++- src/win32_platform.h | 2 ++ src/win32_window.c | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/win32_init.c b/src/win32_init.c index 6b6e9d08e..1a66b976f 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -669,7 +669,8 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform) .getEGLNativeWindow = _glfwGetEGLNativeWindowWin32, .getRequiredInstanceExtensions = _glfwGetRequiredInstanceExtensionsWin32, .getPhysicalDevicePresentationSupport = _glfwGetPhysicalDevicePresentationSupportWin32, - .createWindowSurface = _glfwCreateWindowSurfaceWin32 + .createWindowSurface = _glfwCreateWindowSurfaceWin32, + .createWindowWGPUSurface = _glfwCreateWindowWGPUSurfaceWin32 }; *platform = win32; diff --git a/src/win32_platform.h b/src/win32_platform.h index 49cceba6d..905c86bbc 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -544,6 +544,8 @@ void _glfwGetRequiredInstanceExtensionsWin32(char** extensions); GLFWbool _glfwGetPhysicalDevicePresentationSupportWin32(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); VkResult _glfwCreateWindowSurfaceWin32(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); +WGPUSurface _glfwCreateWindowWGPUSurfaceWin32(WGPUInstance instance, _GLFWwindow* window); + void _glfwFreeMonitorWin32(_GLFWmonitor* monitor); void _glfwGetMonitorPosWin32(_GLFWmonitor* monitor, int* xpos, int* ypos); void _glfwGetMonitorContentScaleWin32(_GLFWmonitor* monitor, float* xscale, float* yscale); diff --git a/src/win32_window.c b/src/win32_window.c index 26f9684b7..e1a8411ab 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -2562,6 +2562,26 @@ VkResult _glfwCreateWindowSurfaceWin32(VkInstance instance, return err; } +typedef struct WGPUSurfaceSourceWindowsHWND { + WGPUChainedStruct chain; + void * hinstance; + void * hwnd; +} WGPUSurfaceSourceWindowsHWND; + +WGPUSurface _glfwCreateWindowWGPUSurfaceWin32(WGPUInstance instance, _GLFWwindow *window) { + WGPUSurfaceSourceWindowsHWND windowsSurface; + windowsSurface.chain.sType = WGPUSType_SurfaceSourceWindowsHWND; + windowsSurface.chain.next = NULL; + windowsSurface.hinstance = _glfw.win32.instance; + windowsSurface.hwnd = window->win32.handle; + + WGPUSurfaceDescriptor surfaceDescriptor; + surfaceDescriptor.nextInChain = &windowsSurface.chain; + surfaceDescriptor.label = (WGPUStringView){ NULL, SIZE_MAX }; + + return wgpuInstanceCreateSurface(instance, &surfaceDescriptor); +} + GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); From 44f8a80ade160087d9cb4341467683170f60acf1 Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Sat, 16 Aug 2025 00:46:54 +0200 Subject: [PATCH 07/14] Add WebGPU support for MacOS --- src/cocoa_init.m | 3 ++- src/cocoa_platform.h | 2 ++ src/cocoa_window.m | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 15dc4ec4c..0df7a330a 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -564,7 +564,8 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform) .getEGLNativeWindow = _glfwGetEGLNativeWindowCocoa, .getRequiredInstanceExtensions = _glfwGetRequiredInstanceExtensionsCocoa, .getPhysicalDevicePresentationSupport = _glfwGetPhysicalDevicePresentationSupportCocoa, - .createWindowSurface = _glfwCreateWindowSurfaceCocoa + .createWindowSurface = _glfwCreateWindowSurfaceCocoa, + .createWindowWGPUSurface = _glfwCreateWindowWGPUSurfaceCocoa }; *platform = cocoa; diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 4d1d66ae0..f7fa7b6f0 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -276,6 +276,8 @@ void _glfwGetRequiredInstanceExtensionsCocoa(char** extensions); GLFWbool _glfwGetPhysicalDevicePresentationSupportCocoa(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); VkResult _glfwCreateWindowSurfaceCocoa(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); +WGPUSurface _glfwCreateWindowWGPUSurfaceCocoa(WGPUInstance instance, _GLFWwindow* window); + void _glfwFreeMonitorCocoa(_GLFWmonitor* monitor); void _glfwGetMonitorPosCocoa(_GLFWmonitor* monitor, int* xpos, int* ypos); void _glfwGetMonitorContentScaleCocoa(_GLFWmonitor* monitor, float* xscale, float* yscale); diff --git a/src/cocoa_window.m b/src/cocoa_window.m index e69b5fe0c..9b5d1bc55 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -2020,6 +2020,26 @@ VkResult _glfwCreateWindowSurfaceCocoa(VkInstance instance, } // autoreleasepool } +typedef struct WGPUSurfaceSourceMetalLayer { + WGPUChainedStruct chain; + void * layer; +} WGPUSurfaceSourceMetalLayer; + +WGPUSurface _glfwCreateWindowWGPUSurfaceCocoa(WGPUInstance instance, _GLFWwindow* window) { + [window->ns.view setLayer:window->ns.layer]; + [window->ns.view setWantsLayer:YES]; + + WGPUSurfaceSourceMetalLayer metalSurface; + metalSurface.chain.next = NULL; + metalSurface.chain.sType = WGPUSType_SurfaceSourceMetalLayer; + metalSurface.layer = window->ns.layer; + + WGPUSurfaceDescriptor surfaceDescriptor; + surfaceDescriptor.nextInChain = &metalSurface.chain; + surfaceDescriptor.label = (WGPUStringView){ NULL, SIZE_MAX }; + + return wgpuInstanceCreateSurface(instance, &surfaceDescriptor); +} ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// From 54fcbc82205cd0ca81e9fb099840c49f25457ac9 Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Wed, 27 Aug 2025 00:46:35 +0200 Subject: [PATCH 08/14] Adjust formatting of new code to fit in with the rest of the code --- src/cocoa_window.m | 6 ++++-- src/internal.h | 12 ++++++++---- src/webgpu.c | 6 ++++-- src/win32_window.c | 6 ++++-- src/wl_window.c | 6 ++++-- src/x11_window.c | 6 ++++-- 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 9b5d1bc55..e5dc0af39 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -2020,12 +2020,14 @@ VkResult _glfwCreateWindowSurfaceCocoa(VkInstance instance, } // autoreleasepool } -typedef struct WGPUSurfaceSourceMetalLayer { +typedef struct WGPUSurfaceSourceMetalLayer +{ WGPUChainedStruct chain; void * layer; } WGPUSurfaceSourceMetalLayer; -WGPUSurface _glfwCreateWindowWGPUSurfaceCocoa(WGPUInstance instance, _GLFWwindow* window) { +WGPUSurface _glfwCreateWindowWGPUSurfaceCocoa(WGPUInstance instance, _GLFWwindow* window) +{ [window->ns.view setLayer:window->ns.layer]; [window->ns.view setWantsLayer:YES]; diff --git a/src/internal.h b/src/internal.h index 1ee044c93..5e409f5a4 100644 --- a/src/internal.h +++ b/src/internal.h @@ -335,12 +335,14 @@ typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const c typedef struct WGPUInstanceImpl* WGPUInstance; typedef struct WGPUSurfaceImpl* WGPUSurface; -typedef struct WGPUStringView { +typedef struct WGPUStringView +{ char const * data; size_t length; } WGPUStringView; -typedef enum WGPUSType { +typedef enum WGPUSType +{ WGPUSType_ShaderSourceSPIRV = 0x00000001, WGPUSType_ShaderSourceWGSL = 0x00000002, WGPUSType_RenderPassMaxDrawCount = 0x00000003, @@ -353,12 +355,14 @@ typedef enum WGPUSType { WGPUSType_Force32 = 0x7FFFFFFF } WGPUSType; -typedef struct WGPUChainedStruct { +typedef struct WGPUChainedStruct +{ struct WGPUChainedStruct const * next; WGPUSType sType; } WGPUChainedStruct; -typedef struct WGPUSurfaceDescriptor { +typedef struct WGPUSurfaceDescriptor +{ WGPUChainedStruct const * nextInChain; WGPUStringView label; } WGPUSurfaceDescriptor; diff --git a/src/webgpu.c b/src/webgpu.c index 7a32cfe3b..e4155220f 100644 --- a/src/webgpu.c +++ b/src/webgpu.c @@ -29,12 +29,14 @@ #include #include -GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)) { +GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)) +{ _GLFW_REQUIRE_INIT() _glfw.wgpu.instanceCreateSurface = addr; } -GLFWAPI WGPUSurface glfwCreateWindowWGPUSurface(WGPUInstance instance, GLFWwindow* handle) { +GLFWAPI WGPUSurface glfwCreateWindowWGPUSurface(WGPUInstance instance, GLFWwindow* handle) +{ _GLFW_REQUIRE_INIT_OR_RETURN(NULL) _GLFWwindow* window = (_GLFWwindow*)handle; diff --git a/src/win32_window.c b/src/win32_window.c index e1a8411ab..1a1f6ecdb 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -2562,13 +2562,15 @@ VkResult _glfwCreateWindowSurfaceWin32(VkInstance instance, return err; } -typedef struct WGPUSurfaceSourceWindowsHWND { +typedef struct WGPUSurfaceSourceWindowsHWND +{ WGPUChainedStruct chain; void * hinstance; void * hwnd; } WGPUSurfaceSourceWindowsHWND; -WGPUSurface _glfwCreateWindowWGPUSurfaceWin32(WGPUInstance instance, _GLFWwindow *window) { +WGPUSurface _glfwCreateWindowWGPUSurfaceWin32(WGPUInstance instance, _GLFWwindow *window) +{ WGPUSurfaceSourceWindowsHWND windowsSurface; windowsSurface.chain.sType = WGPUSType_SurfaceSourceWindowsHWND; windowsSurface.chain.next = NULL; diff --git a/src/wl_window.c b/src/wl_window.c index f19d5a73d..4a25b48aa 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -3326,13 +3326,15 @@ VkResult _glfwCreateWindowSurfaceWayland(VkInstance instance, return err; } -typedef struct WGPUSurfaceSourceWaylandSurface { +typedef struct WGPUSurfaceSourceWaylandSurface +{ WGPUChainedStruct chain; void * display; void * surface; } WGPUSurfaceSourceWaylandSurface; -WGPUSurface _glfwCreateWindowWGPUSurfaceWayland(WGPUInstance instance, _GLFWwindow* window) { +WGPUSurface _glfwCreateWindowWGPUSurfaceWayland(WGPUInstance instance, _GLFWwindow* window) +{ WGPUSurfaceSourceWaylandSurface waylandSurface; waylandSurface.chain.sType = WGPUSType_SurfaceSourceWaylandSurface; waylandSurface.chain.next = NULL; diff --git a/src/x11_window.c b/src/x11_window.c index 5a60f7a02..72a1a6b08 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -3282,13 +3282,15 @@ VkResult _glfwCreateWindowSurfaceX11(VkInstance instance, } } -typedef struct WGPUSurfaceSourceXCBWindow { +typedef struct WGPUSurfaceSourceXCBWindow +{ WGPUChainedStruct chain; void * connection; uint32_t window; } WGPUSurfaceSourceXCBWindow; -typedef struct WGPUSurfaceSourceXlibWindow { +typedef struct WGPUSurfaceSourceXlibWindow +{ WGPUChainedStruct chain; void * display; uint64_t window; From 8c2f255906e5d149f99269f69f02a8c803ebc50d Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Mon, 29 Sep 2025 11:35:26 +0200 Subject: [PATCH 09/14] Remove function to pass address of `wgpuInstanceCreateSurface`. I use an extern function with the `weak` attribute and an assert to ensure that the function is available at runtime instead. I also started to add an example using WebGPU. --- examples/CMakeLists.txt | 20 ++- examples/triangle-webgpu.c | 329 +++++++++++++++++++++++++++++++++++++ include/GLFW/glfw3.h | 13 -- src/internal.h | 2 +- src/webgpu.c | 10 +- 5 files changed, 351 insertions(+), 23 deletions(-) create mode 100644 examples/triangle-webgpu.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e7a037976..5ce9862b6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -25,6 +25,20 @@ set(GETOPT "${GLFW_SOURCE_DIR}/deps/getopt.h" set(TINYCTHREAD "${GLFW_SOURCE_DIR}/deps/tinycthread.h" "${GLFW_SOURCE_DIR}/deps/tinycthread.c") +include(FetchContent) + +set(WEBGPU_LINK_TYPE STATIC) +set(WEBGPU_BACKEND WGPU) +FetchContent_Declare( + webgpu + GIT_REPOSITORY https://github.com/eliemichel/WebGPU-distribution.git + GIT_TAG v0.3.0-gamma + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + EXCLUDE_FROM_ALL +) +FetchContent_MakeAvailable(webgpu) + add_executable(boing WIN32 MACOSX_BUNDLE boing.c ${ICON} ${GLAD_GL}) add_executable(gears WIN32 MACOSX_BUNDLE gears.c ${ICON} ${GLAD_GL}) add_executable(heightmap WIN32 MACOSX_BUNDLE heightmap.c ${ICON} ${GLAD_GL}) @@ -34,6 +48,7 @@ add_executable(sharing WIN32 MACOSX_BUNDLE sharing.c ${ICON} ${GLAD_GL}) add_executable(splitview WIN32 MACOSX_BUNDLE splitview.c ${ICON} ${GLAD_GL}) add_executable(triangle-opengl WIN32 MACOSX_BUNDLE triangle-opengl.c ${ICON} ${GLAD_GL}) add_executable(triangle-opengles WIN32 MACOSX_BUNDLE triangle-opengles.c ${ICON} ${GLAD_GLES2}) +add_executable(triangle-webgpu WIN32 MACOSX_BUNDLE triangle-webgpu.c ${ICON}) add_executable(wave WIN32 MACOSX_BUNDLE wave.c ${ICON} ${GLAD_GL}) add_executable(windows WIN32 MACOSX_BUNDLE windows.c ${ICON} ${GLAD_GL}) @@ -42,8 +57,10 @@ if (RT_LIBRARY) target_link_libraries(particles "${RT_LIBRARY}") endif() +target_link_libraries(triangle-webgpu webgpu) + set(GUI_ONLY_BINARIES boing gears heightmap particles sharing splitview - triangle-opengl triangle-opengles wave windows) + triangle-opengl triangle-opengles triangle-webgpu wave windows) set(CONSOLE_BINARIES offscreen) set_target_properties(${GUI_ONLY_BINARIES} ${CONSOLE_BINARIES} PROPERTIES @@ -68,6 +85,7 @@ if (APPLE) set_target_properties(sharing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Sharing") set_target_properties(triangle-opengl PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "OpenGL Triangle") set_target_properties(triangle-opengles PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "OpenGL ES Triangle") + set_target_properties(triangle-webgpu PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "WebGPU Triangle") set_target_properties(splitview PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "SplitView") set_target_properties(wave PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Wave") set_target_properties(windows PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Windows") diff --git a/examples/triangle-webgpu.c b/examples/triangle-webgpu.c new file mode 100644 index 000000000..e340327dd --- /dev/null +++ b/examples/triangle-webgpu.c @@ -0,0 +1,329 @@ +//======================================================================== +// WebGPU triangle example +// Copyright (c) Sebastian Dawid +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +//! [code] + +#include +#include +#include + +#include +#include +#include + +#define SHADER_SOURCE(...) #__VA_ARGS__ + +static const char* CODE = SHADER_SOURCE(); + +static void error_callback(int error, const char* description); +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods); + +static WGPULimits get_required_limits(WGPULimits* supported_limits); +static void adapter_callback(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata1, void* userdata2); +static void device_callback(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void* userdata1, void* userdata2); +static void device_lost_callback(const WGPUDevice* device, WGPUDeviceLostReason reason, WGPUStringView message, void* userdata1, void* userdata2); +static void uncaptured_error_callback(const WGPUDevice* device, WGPUErrorType error, WGPUStringView message, void* userdata1, void* userdata2); + +int main(void) +{ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) + exit(EXIT_FAILURE); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + + GLFWwindow* window = glfwCreateWindow(800, 600, "WebGPU Triangle", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwSetKeyCallback(window, key_callback); + + WGPUInstance instance = wgpuCreateInstance(NULL); + if (!instance) + { + fprintf(stderr, "Error: Failed to create WebGPU instance.\n"); + glfwDestroyWindow(window); + glfwTerminate(); + exit(EXIT_FAILURE); + } + + WGPUAdapter adapter; + WGPUSurface surface = glfwCreateWindowWGPUSurface(instance, window); + WGPUDevice device; + WGPUQueue queue; + bool success = false; + + WGPURequestAdapterOptions adapter_options = {0}; + adapter_options.nextInChain = NULL; + adapter_options.compatibleSurface = surface; + + WGPURequestAdapterCallbackInfo adapter_callback_info = {0}; + adapter_callback_info.nextInChain = NULL; + adapter_callback_info.mode = WGPUCallbackMode_AllowSpontaneous; + adapter_callback_info.callback = adapter_callback; + adapter_callback_info.userdata1 = &adapter; + adapter_callback_info.userdata2 = &success; + + wgpuInstanceRequestAdapter(instance, &adapter_options, adapter_callback_info); + + while (!success); // spin until we get the adapter + success = false; + + WGPULimits supported_limits; + wgpuAdapterGetLimits(adapter, &supported_limits); + WGPULimits required_limits = get_required_limits(&supported_limits); + + WGPUDeviceDescriptor device_descriptor = {0}; + device_descriptor.nextInChain = NULL; + device_descriptor.label = (WGPUStringView) { "device", 6 }; + device_descriptor.requiredFeatureCount = 0; + device_descriptor.requiredFeatures = NULL; + device_descriptor.requiredLimits = &required_limits; + + device_descriptor.defaultQueue.nextInChain = NULL; + device_descriptor.defaultQueue.label = (WGPUStringView) { "queue", 5 }; + + device_descriptor.deviceLostCallbackInfo.nextInChain = NULL; + device_descriptor.deviceLostCallbackInfo.mode = WGPUCallbackMode_AllowSpontaneous; + device_descriptor.deviceLostCallbackInfo.callback = device_lost_callback; + device_descriptor.deviceLostCallbackInfo.userdata1 = NULL; + device_descriptor.deviceLostCallbackInfo.userdata2 = NULL; + + WGPURequestDeviceCallbackInfo device_callback_info = {0}; + device_callback_info.nextInChain = NULL; + device_callback_info.mode = WGPUCallbackMode_AllowSpontaneous; + device_callback_info.callback = device_callback; + device_callback_info.userdata1 = &device; + device_callback_info.userdata2 = &success; + + wgpuAdapterRequestDevice(adapter, &device_descriptor, device_callback_info); + + while (!success); // spin until we get the dvice + success = false; + + queue = wgpuDeviceGetQueue(device); + + WGPUSurfaceCapabilities surface_capabilities = {0}; + surface_capabilities.nextInChain = NULL; + if (wgpuSurfaceGetCapabilities(surface, adapter, &surface_capabilities) != WGPUStatus_Success) + { + fprintf(stderr, "Error: Failed to get surface capabilities.\n"); + wgpuQueueRelease(queue); + wgpuDeviceRelease(device); + wgpuSurfaceRelease(surface); + glfwDestroyWindow(window); + glfwTerminate(); + exit(EXIT_FAILURE); + } + + WGPUTextureFormat format = {0}; + for (size_t i = 0; i < surface_capabilities.formatCount; ++i) + { + WGPUTextureFormat f = surface_capabilities.formats[i]; + if (f == WGPUTextureFormat_BGRA8UnormSrgb || f == WGPUTextureFormat_RGBA8UnormSrgb) + { + format = f; + break; + } + } + + WGPUSurfaceConfiguration surface_configuration = {0}; + surface_configuration.nextInChain = NULL; + surface_configuration.width = 800; + surface_configuration.height = 600; + surface_configuration.format = format; + surface_configuration.viewFormatCount = 0; + surface_configuration.viewFormats = NULL; + surface_configuration.usage = WGPUTextureUsage_RenderAttachment; + surface_configuration.presentMode = WGPUPresentMode_Fifo; + surface_configuration.alphaMode = WGPUCompositeAlphaMode_Auto; + surface_configuration.device = device; + + wgpuSurfaceConfigure(surface, &surface_configuration); + + wgpuAdapterRelease(adapter); + wgpuInstanceRelease(instance); + + WGPUSurfaceTexture surface_texture = {0}; + WGPUTextureView target_view = {0}; + while (!glfwWindowShouldClose(window)) + { + wgpuDevicePoll(device, false, NULL); + glfwPollEvents(); + + wgpuSurfaceGetCurrentTexture(surface, &surface_texture); + + if (surface_texture.status == WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal) + { + WGPUTextureViewDescriptor view_descriptor = {0}; + view_descriptor.nextInChain = NULL; + view_descriptor.label = (WGPUStringView) { NULL, SIZE_MAX }; + view_descriptor.format = wgpuTextureGetFormat(surface_texture.texture); + view_descriptor.dimension = WGPUTextureViewDimension_2D; + view_descriptor.baseMipLevel = 0; + view_descriptor.mipLevelCount = 1; + view_descriptor.baseArrayLayer = 0; + view_descriptor.arrayLayerCount = 1; + view_descriptor.aspect = WGPUTextureAspect_All; + view_descriptor.usage = WGPUTextureUsage_RenderAttachment; + + target_view = wgpuTextureCreateView(surface_texture.texture, &view_descriptor); + } + + if (target_view == NULL) + continue; + + wgpuTextureViewRelease(target_view); + target_view = NULL; + wgpuSurfacePresent(surface); + } + + wgpuQueueRelease(queue); + wgpuDeviceRelease(device); + wgpuSurfaceRelease(surface); + glfwDestroyWindow(window); + glfwTerminate(); + exit(EXIT_SUCCESS); +} + +void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +static const WGPULimits DEFAULT_LIMITS = { + .nextInChain = NULL, + .maxTextureDimension1D = WGPU_LIMIT_U32_UNDEFINED, + .maxTextureDimension2D = WGPU_LIMIT_U32_UNDEFINED, + .maxTextureDimension3D = WGPU_LIMIT_U32_UNDEFINED, + .maxTextureArrayLayers = WGPU_LIMIT_U32_UNDEFINED, + .maxBindGroups = WGPU_LIMIT_U32_UNDEFINED, + .maxBindGroupsPlusVertexBuffers = WGPU_LIMIT_U32_UNDEFINED, + .maxBindingsPerBindGroup = WGPU_LIMIT_U32_UNDEFINED, + .maxDynamicUniformBuffersPerPipelineLayout = WGPU_LIMIT_U32_UNDEFINED, + .maxDynamicStorageBuffersPerPipelineLayout = WGPU_LIMIT_U32_UNDEFINED, + .maxSampledTexturesPerShaderStage = WGPU_LIMIT_U32_UNDEFINED, + .maxSamplersPerShaderStage = WGPU_LIMIT_U32_UNDEFINED, + .maxStorageBuffersPerShaderStage = WGPU_LIMIT_U32_UNDEFINED, + .maxStorageTexturesPerShaderStage = WGPU_LIMIT_U32_UNDEFINED, + .maxUniformBuffersPerShaderStage = WGPU_LIMIT_U32_UNDEFINED, + .maxUniformBufferBindingSize = WGPU_LIMIT_U64_UNDEFINED, + .maxStorageBufferBindingSize = WGPU_LIMIT_U64_UNDEFINED, + .minUniformBufferOffsetAlignment = WGPU_LIMIT_U32_UNDEFINED, + .minStorageBufferOffsetAlignment = WGPU_LIMIT_U32_UNDEFINED, + .maxVertexBuffers = WGPU_LIMIT_U32_UNDEFINED, + .maxBufferSize = WGPU_LIMIT_U64_UNDEFINED, + .maxVertexAttributes = WGPU_LIMIT_U32_UNDEFINED, + .maxVertexBufferArrayStride = WGPU_LIMIT_U32_UNDEFINED, + .maxInterStageShaderVariables = WGPU_LIMIT_U32_UNDEFINED, + .maxColorAttachments = WGPU_LIMIT_U32_UNDEFINED, + .maxColorAttachmentBytesPerSample = WGPU_LIMIT_U32_UNDEFINED, + .maxComputeWorkgroupStorageSize = WGPU_LIMIT_U32_UNDEFINED, + .maxComputeInvocationsPerWorkgroup = WGPU_LIMIT_U32_UNDEFINED, + .maxComputeWorkgroupSizeX = WGPU_LIMIT_U32_UNDEFINED, + .maxComputeWorkgroupSizeY = WGPU_LIMIT_U32_UNDEFINED, + .maxComputeWorkgroupSizeZ = WGPU_LIMIT_U32_UNDEFINED, + .maxComputeWorkgroupsPerDimension = WGPU_LIMIT_U32_UNDEFINED +}; + +WGPULimits get_required_limits(WGPULimits* supported_limits) { + WGPULimits required_limits = DEFAULT_LIMITS; + + required_limits.maxVertexAttributes = 3; + required_limits.maxVertexBuffers = 1; + required_limits.maxBufferSize = 256 * 1024 * 1024; // 256 MiB + required_limits.maxVertexBufferArrayStride = sizeof(float) * 8; + required_limits.maxInterStageShaderVariables = 6; + + required_limits.maxBindGroups = 2; + required_limits.maxUniformBuffersPerShaderStage = 2; + required_limits.maxUniformBufferBindingSize = 4 * 16 * sizeof(float); + required_limits.maxSampledTexturesPerShaderStage = 1; + required_limits.maxSamplersPerShaderStage = 1; + + // require default maximum value for 2D textures + required_limits.maxTextureDimension1D = 8192; + required_limits.maxTextureDimension2D = 8192; + required_limits.maxTextureArrayLayers = 1; + + // explicitly forward minimum limits, since they may cause issues otherwise + required_limits.minStorageBufferOffsetAlignment = supported_limits->minStorageBufferOffsetAlignment; + required_limits.minUniformBufferOffsetAlignment = supported_limits->minUniformBufferOffsetAlignment; + + return required_limits; +} + +void adapter_callback( + WGPURequestAdapterStatus status, WGPUAdapter adapter, + WGPUStringView message, void* userdata1, void* userdata2 +) { + if (status == WGPURequestAdapterStatus_Success) { + *(WGPUAdapter*)userdata1 = adapter; + } else { + fprintf(stderr, "Failed to get WebGPU adapter: %s\n", message.data); + return; + } + *(bool*)userdata2 = true; +} + +void device_callback( + WGPURequestDeviceStatus status, WGPUDevice device, + WGPUStringView message, void* userdata1, void* userdata2 +) { + if (status == WGPURequestDeviceStatus_Success) { + *(WGPUDevice*)userdata1 = device; + } else { + fprintf(stderr, "Failed to get WebGPU device: %s\n", message.data); + return; + } + *(bool*)userdata2 = true; +} + +void device_lost_callback( + const WGPUDevice* device, WGPUDeviceLostReason reason, + WGPUStringView message, void* userdata1, void* userdata2 +) { + (void)userdata1; + (void)userdata2; + fprintf(stderr, "Device 0x%zx lost with reason 0x%zx: %s\n", (size_t)*device, (size_t)reason, message.data); +} + +void uncaptured_error_callback( + const WGPUDevice* device, WGPUErrorType error, + WGPUStringView message, void* userdata1, void* userdata2 +) { + (void)userdata1; + (void)userdata2; + fprintf(stderr, "Device 0x%zx has uncaptured error 0x%zx: %s\n", (size_t)*device, (size_t)error, message.data); +} diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index fe181bcde..1d31ced65 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -6536,19 +6536,6 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window #if defined(WEBGPU_H_) -/*! @brief Provide the address of the `wgpuInstanceCreateSurface` function to GLFW. - * - * This function passes the address provided for the `wgpuInstanceCreateSurface` function - * to GLFW. - * - * @param[in] addr The address of the `wgpuInstanceCreateSurface` function. - * - * @since Added in version 3.5 - * - * @ingroup webgpu - */ -GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)); - /*! @brief Creates a WebGPU surface for the specified window. * * This function creates a WebGPU surface for the specified window. diff --git a/src/internal.h b/src/internal.h index 284237e24..c0f433815 100644 --- a/src/internal.h +++ b/src/internal.h @@ -369,7 +369,7 @@ typedef struct WGPUSurfaceDescriptor typedef WGPUSurface (*PFN_wgpuInstanceCreateSurface)(WGPUInstance, const WGPUSurfaceDescriptor*); -#define wgpuInstanceCreateSurface _glfw.wgpu.instanceCreateSurface +extern WGPUSurface wgpuInstanceCreateSurface(WGPUInstance instance, const WGPUSurfaceDescriptor* descriptor) __attribute((weak)); #include "platform.h" diff --git a/src/webgpu.c b/src/webgpu.c index e4155220f..09bbe375f 100644 --- a/src/webgpu.c +++ b/src/webgpu.c @@ -1,7 +1,7 @@ //======================================================================== // GLFW 3.5 - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) Sebastian Dawid +// Copyright (c) Sebastian Dawid // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -29,12 +29,6 @@ #include #include -GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)) -{ - _GLFW_REQUIRE_INIT() - _glfw.wgpu.instanceCreateSurface = addr; -} - GLFWAPI WGPUSurface glfwCreateWindowWGPUSurface(WGPUInstance instance, GLFWwindow* handle) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL) @@ -43,7 +37,7 @@ GLFWAPI WGPUSurface glfwCreateWindowWGPUSurface(WGPUInstance instance, GLFWwindo assert(window != NULL); assert(instance != NULL); - assert(_glfw.wgpu.instanceCreateSurface != NULL); + assert(&wgpuInstanceCreateSurface != NULL); if (window->context.client != GLFW_NO_API) { From b0b39d678e2224689d6cba86249dd14d178e5562 Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Mon, 29 Sep 2025 11:41:35 +0200 Subject: [PATCH 10/14] Remove now unused `wgpu` member of `_glfw` --- examples/triangle-webgpu.c | 1 + src/internal.h | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/examples/triangle-webgpu.c b/examples/triangle-webgpu.c index e340327dd..c31131221 100644 --- a/examples/triangle-webgpu.c +++ b/examples/triangle-webgpu.c @@ -30,6 +30,7 @@ #include #include +#define GLFW_INCLUDE_NONE #include #define SHADER_SOURCE(...) #__VA_ARGS__ diff --git a/src/internal.h b/src/internal.h index c0f433815..8037f1d31 100644 --- a/src/internal.h +++ b/src/internal.h @@ -367,8 +367,6 @@ typedef struct WGPUSurfaceDescriptor WGPUStringView label; } WGPUSurfaceDescriptor; -typedef WGPUSurface (*PFN_wgpuInstanceCreateSurface)(WGPUInstance, const WGPUSurfaceDescriptor*); - extern WGPUSurface wgpuInstanceCreateSurface(WGPUInstance instance, const WGPUSurfaceDescriptor* descriptor) __attribute((weak)); #include "platform.h" @@ -916,10 +914,6 @@ struct _GLFWlibrary GLFWbool EXT_headless_surface; } vk; - struct { - PFN_wgpuInstanceCreateSurface instanceCreateSurface; - } wgpu; - struct { GLFWmonitorfun monitor; GLFWjoystickfun joystick; From 044774dcee440f080e3837a8165a1a17f4c90caf Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Mon, 29 Sep 2025 14:40:31 +0200 Subject: [PATCH 11/14] Provide weak default implementation for `wgpuInstanceCreateSurface` --- examples/triangle-webgpu.c | 10 +++++++++- src/internal.h | 4 +++- src/webgpu.c | 7 +++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/examples/triangle-webgpu.c b/examples/triangle-webgpu.c index c31131221..960515095 100644 --- a/examples/triangle-webgpu.c +++ b/examples/triangle-webgpu.c @@ -73,8 +73,16 @@ int main(void) exit(EXIT_FAILURE); } - WGPUAdapter adapter; WGPUSurface surface = glfwCreateWindowWGPUSurface(instance, window); + if (!surface) + { + fprintf(stderr, "Error: Failed to create WebGPU surface.\n"); + wgpuInstanceRelease(instance); + glfwDestroyWindow(window); + glfwTerminate(); + exit(EXIT_FAILURE); + } + WGPUAdapter adapter; WGPUDevice device; WGPUQueue queue; bool success = false; diff --git a/src/internal.h b/src/internal.h index 8037f1d31..eaedb1c68 100644 --- a/src/internal.h +++ b/src/internal.h @@ -367,7 +367,9 @@ typedef struct WGPUSurfaceDescriptor WGPUStringView label; } WGPUSurfaceDescriptor; -extern WGPUSurface wgpuInstanceCreateSurface(WGPUInstance instance, const WGPUSurfaceDescriptor* descriptor) __attribute((weak)); +#if !defined(_MSC_VER) +WGPUSurface __attribute__((weak)) wgpuInstanceCreateSurface(WGPUInstance instance, const WGPUSurfaceDescriptor* descriptor); +#endif #include "platform.h" diff --git a/src/webgpu.c b/src/webgpu.c index 09bbe375f..5af24b411 100644 --- a/src/webgpu.c +++ b/src/webgpu.c @@ -29,6 +29,13 @@ #include #include +#if !defined(_MSC_VER) +WGPUSurface __attribute__((weak)) wgpuInstanceCreateSurface(WGPUInstance instance, const WGPUSurfaceDescriptor* descriptor) +{ + return NULL; +} +#endif + GLFWAPI WGPUSurface glfwCreateWindowWGPUSurface(WGPUInstance instance, GLFWwindow* handle) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL) From 1471e07cebf3fd36f4d8271454b9c2b3e384442f Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Mon, 29 Sep 2025 15:30:00 +0200 Subject: [PATCH 12/14] Partially Revert "Remove function to pass address of `wgpuInstanceCreateSurface`." This reverts commit 8c2f255906e5d149f99269f69f02a8c803ebc50d. --- include/GLFW/glfw3.h | 13 +++++++++++++ src/internal.h | 9 ++++++--- src/webgpu.c | 9 ++++----- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 1d31ced65..fe181bcde 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -6536,6 +6536,19 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window #if defined(WEBGPU_H_) +/*! @brief Provide the address of the `wgpuInstanceCreateSurface` function to GLFW. + * + * This function passes the address provided for the `wgpuInstanceCreateSurface` function + * to GLFW. + * + * @param[in] addr The address of the `wgpuInstanceCreateSurface` function. + * + * @since Added in version 3.5 + * + * @ingroup webgpu + */ +GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)); + /*! @brief Creates a WebGPU surface for the specified window. * * This function creates a WebGPU surface for the specified window. diff --git a/src/internal.h b/src/internal.h index eaedb1c68..fbd3acb0a 100644 --- a/src/internal.h +++ b/src/internal.h @@ -367,9 +367,8 @@ typedef struct WGPUSurfaceDescriptor WGPUStringView label; } WGPUSurfaceDescriptor; -#if !defined(_MSC_VER) -WGPUSurface __attribute__((weak)) wgpuInstanceCreateSurface(WGPUInstance instance, const WGPUSurfaceDescriptor* descriptor); -#endif +typedef WGPUSurface (*PFN_wgpuInstanceCreateSurface)(WGPUInstance, const WGPUSurfaceDescriptor*); +#define wgpuInstanceCreateSurface _glfw.wgpu.instanceCreateSurface #include "platform.h" @@ -916,6 +915,10 @@ struct _GLFWlibrary GLFWbool EXT_headless_surface; } vk; + struct { + PFN_wgpuInstanceCreateSurface instanceCreateSurface; + } wgpu; + struct { GLFWmonitorfun monitor; GLFWjoystickfun joystick; diff --git a/src/webgpu.c b/src/webgpu.c index 5af24b411..e330bd8f9 100644 --- a/src/webgpu.c +++ b/src/webgpu.c @@ -29,12 +29,11 @@ #include #include -#if !defined(_MSC_VER) -WGPUSurface __attribute__((weak)) wgpuInstanceCreateSurface(WGPUInstance instance, const WGPUSurfaceDescriptor* descriptor) +GLFWAPI void glfwSetWGPUInstanceCreateSurfaceAddr(WGPUSurface (*addr)(WGPUInstance, const WGPUSurfaceDescriptor*)) { - return NULL; + _GLFW_REQUIRE_INIT() + _glfw.wgpu.instanceCreateSurface = addr; } -#endif GLFWAPI WGPUSurface glfwCreateWindowWGPUSurface(WGPUInstance instance, GLFWwindow* handle) { @@ -44,7 +43,7 @@ GLFWAPI WGPUSurface glfwCreateWindowWGPUSurface(WGPUInstance instance, GLFWwindo assert(window != NULL); assert(instance != NULL); - assert(&wgpuInstanceCreateSurface != NULL); + assert(_glfw.wgpu.instanceCreateSurface != NULL); if (window->context.client != GLFW_NO_API) { From 0111a5977e74060540abc359d1ae5a664d1e7e6e Mon Sep 17 00:00:00 2001 From: Sebastian Dawid Date: Tue, 30 Sep 2025 11:42:16 +0200 Subject: [PATCH 13/14] Implement rendering a red triangle in the `triangle-webgpu` example --- examples/CMakeLists.txt | 3 +- examples/triangle-webgpu.c | 129 ++++++++++++++++++++++++++++++++++++- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5ce9862b6..047494824 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -27,8 +27,6 @@ set(TINYCTHREAD "${GLFW_SOURCE_DIR}/deps/tinycthread.h" include(FetchContent) -set(WEBGPU_LINK_TYPE STATIC) -set(WEBGPU_BACKEND WGPU) FetchContent_Declare( webgpu GIT_REPOSITORY https://github.com/eliemichel/WebGPU-distribution.git @@ -58,6 +56,7 @@ if (RT_LIBRARY) endif() target_link_libraries(triangle-webgpu webgpu) +target_copy_webgpu_binaries(triangle-webgpu) set(GUI_ONLY_BINARIES boing gears heightmap particles sharing splitview triangle-opengl triangle-opengles triangle-webgpu wave windows) diff --git a/examples/triangle-webgpu.c b/examples/triangle-webgpu.c index 960515095..e94efd652 100644 --- a/examples/triangle-webgpu.c +++ b/examples/triangle-webgpu.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -35,7 +36,27 @@ #define SHADER_SOURCE(...) #__VA_ARGS__ -static const char* CODE = SHADER_SOURCE(); +static const char* CODE = SHADER_SOURCE( +@vertex +fn vert(@builtin(vertex_index) index: u32) -> @builtin(position) vec4f { + var p = vec2f(0.0, 0.0); + + if (index == 0u) { + p = vec2f(-0.5, -0.5); + } else if (index == 1u) { + p = vec2f(0.5, -0.5); + } else { + p = vec2f(0.0, 0.5); + } + + return vec4f(p, 0.0, 1.0); +} + +@fragment +fn frag() -> @location(0) vec4f { + return vec4f(1.0, 0.0, 0.0, 1.0); +} +); static void error_callback(int error, const char* description); static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods); @@ -51,6 +72,7 @@ int main(void) glfwSetErrorCallback(error_callback); if (!glfwInit()) exit(EXIT_FAILURE); + glfwSetWGPUInstanceCreateSurfaceAddr(wgpuInstanceCreateSurface); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); @@ -103,7 +125,7 @@ int main(void) while (!success); // spin until we get the adapter success = false; - WGPULimits supported_limits; + WGPULimits supported_limits = {0}; wgpuAdapterGetLimits(adapter, &supported_limits); WGPULimits required_limits = get_required_limits(&supported_limits); @@ -177,7 +199,73 @@ int main(void) wgpuAdapterRelease(adapter); wgpuInstanceRelease(instance); + + WGPUShaderSourceWGSL shader_source = {0}; + shader_source.chain.next = NULL; + shader_source.chain.sType = WGPUSType_ShaderSourceWGSL; + shader_source.code = (WGPUStringView) { CODE, strlen(CODE) }; + WGPUShaderModuleDescriptor module_descriptor = {0}; + module_descriptor.nextInChain = &shader_source.chain; + module_descriptor.label = (WGPUStringView) { NULL, SIZE_MAX }; + + WGPUShaderModule module = wgpuDeviceCreateShaderModule(device, &module_descriptor); + + WGPURenderPipelineDescriptor pipeline_descriptor = {0}; + pipeline_descriptor.nextInChain = NULL; + pipeline_descriptor.label = (WGPUStringView) { NULL, SIZE_MAX }; + pipeline_descriptor.layout = NULL; + + pipeline_descriptor.vertex.nextInChain = NULL; + pipeline_descriptor.vertex.entryPoint = (WGPUStringView) { "vert", 4 }; + pipeline_descriptor.vertex.module = module; + pipeline_descriptor.vertex.bufferCount = 0; + pipeline_descriptor.vertex.buffers = NULL; + pipeline_descriptor.vertex.constantCount = 0; + pipeline_descriptor.vertex.constants = NULL; + + pipeline_descriptor.primitive.nextInChain = NULL; + pipeline_descriptor.primitive.topology = WGPUPrimitiveTopology_TriangleList; + pipeline_descriptor.primitive.stripIndexFormat = WGPUIndexFormat_Undefined; + pipeline_descriptor.primitive.frontFace = WGPUFrontFace_CCW; + pipeline_descriptor.primitive.cullMode = WGPUCullMode_None; + + WGPUBlendState blend_state = {0}; + blend_state.color.srcFactor = WGPUBlendFactor_SrcAlpha; + blend_state.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; + blend_state.color.operation = WGPUBlendOperation_Add; + + blend_state.alpha.srcFactor = WGPUBlendFactor_Zero; + blend_state.alpha.dstFactor = WGPUBlendFactor_One; + blend_state.alpha.operation = WGPUBlendOperation_Add; + + WGPUColorTargetState color_target_state = {0}; + color_target_state.nextInChain = NULL; + color_target_state.format = format; + color_target_state.blend = &blend_state; + color_target_state.writeMask = WGPUColorWriteMask_All; + + WGPUFragmentState fragment_state = {0}; + fragment_state.nextInChain = NULL; + fragment_state.module = module; + fragment_state.entryPoint = (WGPUStringView) { "frag", 4 }; + fragment_state.constantCount = 0; + fragment_state.constants = NULL; + fragment_state.targetCount = 1; + fragment_state.targets = &color_target_state; + + pipeline_descriptor.fragment = &fragment_state; + + pipeline_descriptor.multisample.count = 1; + pipeline_descriptor.multisample.mask = ~0u; + pipeline_descriptor.multisample.alphaToCoverageEnabled = false; + + pipeline_descriptor.depthStencil = NULL; + + WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(device, &pipeline_descriptor); + + wgpuShaderModuleRelease(module); + WGPUSurfaceTexture surface_texture = {0}; WGPUTextureView target_view = {0}; while (!glfwWindowShouldClose(window)) @@ -207,6 +295,43 @@ int main(void) if (target_view == NULL) continue; + WGPURenderPassDescriptor render_pass_descriptor = {0}; + render_pass_descriptor.nextInChain = NULL; + + WGPURenderPassColorAttachment color_attachment = {0}; + color_attachment.view = target_view; + color_attachment.resolveTarget = NULL; + color_attachment.loadOp = WGPULoadOp_Clear; + color_attachment.storeOp = WGPUStoreOp_Store; + color_attachment.clearValue = (WGPUColor) { 0.0f, 0.0f, 0.0f, 1.0f }; + render_pass_descriptor.colorAttachmentCount = 1; + render_pass_descriptor.colorAttachments = &color_attachment; + + render_pass_descriptor.depthStencilAttachment = NULL; + render_pass_descriptor.timestampWrites = NULL; + + WGPUCommandEncoderDescriptor encoder_descriptor = {0}; + encoder_descriptor.nextInChain = NULL; + encoder_descriptor.label = (WGPUStringView) { NULL, SIZE_MAX }; + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, &encoder_descriptor); + + WGPURenderPassEncoder render_pass = wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_descriptor); + + wgpuRenderPassEncoderSetPipeline(render_pass, pipeline); + wgpuRenderPassEncoderDraw(render_pass, 3, 1, 0, 0); + + wgpuRenderPassEncoderEnd(render_pass); + wgpuRenderPassEncoderRelease(render_pass); + + WGPUCommandBufferDescriptor command_buffer_descriptor = {0}; + command_buffer_descriptor.nextInChain = NULL; + command_buffer_descriptor.label = (WGPUStringView) { NULL, SIZE_MAX }; + + WGPUCommandBuffer command_buffer = wgpuCommandEncoderFinish(encoder, &command_buffer_descriptor); + + wgpuQueueSubmit(queue, 1, &command_buffer); + wgpuCommandBufferRelease(command_buffer); + wgpuTextureViewRelease(target_view); target_view = NULL; wgpuSurfacePresent(surface); From 95e1dcce2ef7c7f407d2e952ad7749b2559af41f Mon Sep 17 00:00:00 2001 From: Sebastian Emanuel Dawid Date: Wed, 1 Oct 2025 09:06:03 +0200 Subject: [PATCH 14/14] Add metal layer creation that was previously missing. --- examples/CMakeLists.txt | 2 +- src/cocoa_window.m | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 047494824..b40fa5bf4 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -26,7 +26,6 @@ set(TINYCTHREAD "${GLFW_SOURCE_DIR}/deps/tinycthread.h" "${GLFW_SOURCE_DIR}/deps/tinycthread.c") include(FetchContent) - FetchContent_Declare( webgpu GIT_REPOSITORY https://github.com/eliemichel/WebGPU-distribution.git @@ -36,6 +35,7 @@ FetchContent_Declare( EXCLUDE_FROM_ALL ) FetchContent_MakeAvailable(webgpu) +message("${CMAKE_SYSTEM_PROCESSOR}") add_executable(boing WIN32 MACOSX_BUNDLE boing.c ${ICON} ${GLAD_GL}) add_executable(gears WIN32 MACOSX_BUNDLE gears.c ${ICON} ${GLAD_GL}) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 8bfe71675..a35dc7bb1 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -2028,6 +2028,17 @@ typedef struct WGPUSurfaceSourceMetalLayer WGPUSurface _glfwCreateWindowWGPUSurfaceCocoa(WGPUInstance instance, _GLFWwindow* window) { + window->ns.layer = [CAMetalLayer layer]; + if (!window->ns.layer) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create layer for view"); + return NULL; + } + + if (window->ns.scaleFramebuffer) + [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; + [window->ns.view setLayer:window->ns.layer]; [window->ns.view setWantsLayer:YES];