diff options
| author | Andre Weissflog <floooh@gmail.com> | 2025-09-08 17:24:46 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-08 17:24:46 +0200 |
| commit | 752be5bb8e775346e58595550f9d2faa97fc3acb (patch) | |
| tree | 6aab747036bd7a0cdc47d18fd83dc8512ebdd1e8 | |
| parent | 1294c11a9f21c530fa43302689117ca3f95955d7 (diff) | |
| parent | 47e95718ae1a397673d37a39746c792546332e5a (diff) | |
Merge pull request #1326 from floooh/macos-webgpu-experiment
sokol_app.h: add native WebGPU backends (Linux, macOS, Windows)
| -rw-r--r-- | CHANGELOG.md | 25 | ||||
| -rw-r--r-- | sokol_app.h | 742 | ||||
| -rw-r--r-- | sokol_gfx.h | 64 |
3 files changed, 588 insertions, 243 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c19bf56..2474b037 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ ## Updates +### 08-Sep-2025 + +- sokol_app.h: added WebGPU support to the native desktop backends (Linux, + macOS, Windows), e.g. on those platforms you can now compile sokol_app.h with + `SOKOL_WGPU` and it will setup and manage a WebGPU swapchain surface, + depth-stencil-buffer and optional MSAA surface for you. You'll need to provide a + `<webgpu/webgpu.h> C header and link with a native WebGPU implementation (which + is currently only tested with Google's Dawn library). The WebGPU setup is + currently fairly hardwired, most notably that the device limits and features are + hardwired for what's required by sokol_gfx.h, but of course it's still possible + to use sokol_app.h without sokol_gfx.h and instead render directly via WebGPU + code. All in all you should currently still see the native WebGPU backend in + sokol_app.h as experimental, in some situations it's not as robust as the native + 3D API backends (such as window resizing). For me it's currently mainly useful + for easier debugging (unlike in the browser it's possible to debug-step into the + WebGPU implementation) and for benchmarking the sokol_gfx.h WebGPU backend + against the system-native 3D API backends. A nice side effect of this work is also + that the 3D API specific code has been better separated from the window system + specific code, and it's also a nice preparation for eventually dropping MTKView + from the sokol_app.h Metal backend. + + More details in the PR: https://github.com/floooh/sokol/pull/1326 + + Also see this PR for what has changed in the sokol-samples: https://github.com/floooh/sokol-samples/pull/182 + ### 01-Sep-2025 - sokol_app.h: it's now possible to define custom mouse cursors via two new functions: diff --git a/sokol_app.h b/sokol_app.h index 87e44888..16ea4eeb 100644 --- a/sokol_app.h +++ b/sokol_app.h @@ -65,18 +65,30 @@ Link with the following system libraries: - - on macOS with Metal: Cocoa, QuartzCore, Metal, MetalKit - - on macOS with GL: Cocoa, QuartzCore, OpenGL - - on iOS with Metal: Foundation, UIKit, Metal, MetalKit - - on iOS with GL: Foundation, UIKit, OpenGLES, GLKit - - on Linux with EGL: X11, Xi, Xcursor, EGL, GL (or GLESv2), dl, pthread, m(?) - - on Linux with GLX: X11, Xi, Xcursor, GL, dl, pthread, m(?) + - on macOS: + - all backends: Foundation, Cocoa, QuartzCore + - with SOKOL_METAL: Metal, MetalKit + - with SOKOL_GLCORE: OpenGL + - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) + - on iOS: + - all backends: Foundation, UIKit + - with SOKOL_METAL: Metal, MetalKit + - with SOKOL_GLES3: OpenGLES, GLKit + - on Linux: + - all backends: X11, Xi, Xcursor, dl, pthread, m + - with SOKOL_GLCORE: GL + - with SOKOL_GLES3: GLESv2 + - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) + - with EGL: EGL - on Android: GLESv3, EGL, log, android - - on Windows with the MSVC or Clang toolchains: no action needed, libs are defined in-source via pragma-comment-lib - - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined - - link with the following libs: -lkernel32 -luser32 -lshell32 - - additionally with the GL backend: -lgdi32 - - additionally with the D3D11 backend: -ld3d11 -ldxgi + - on Windows: + - with MSVC or Clang: library dependencies are defined via `#pragma comment` + - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) + - with MINGW/MSYS2 gcc: + - compile with '-mwin32' so that _WIN32 is defined + - link with the following libs: -lkernel32 -luser32 -lshell32 + - additionally with the GL backend: -lgdi32 + - additionally with the D3D11 backend: -ld3d11 -ldxgi On Linux, you also need to use the -pthread compiler and linker option, otherwise weird things will happen, see here for details: https://github.com/floooh/sokol/issues/376 @@ -86,7 +98,7 @@ On Emscripten: - for WebGL2: add the linker option `-s USE_WEBGL2=1` - for WebGPU: compile and link with `--use-port=emdawnwebgpu` - (for more exotic situations, read: https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/pkg/README.md) + (for more exotic situations read: https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/pkg/README.md) FEATURE OVERVIEW ================ @@ -94,11 +106,12 @@ implements the 'application-wrapper' parts of a 3D application: - a common application entry function - - creates a window and 3D-API context/device with a 'default framebuffer' + - creates a window and 3D-API context/device with a swapchain + surface, depth-stencil-buffer surface and optionally MSAA surface - makes the rendered frame visible - provides keyboard-, mouse- and low-level touch-events - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android - - 3D-APIs: Metal, D3D11, GL4.1, GL4.3, GLES3, WebGL, WebGL2, NOAPI + - 3D-APIs: Metal, D3D11, GL4.1, GL4.3, GLES3, WebGL2, WebGPU, NOAPI FEATURE/PLATFORM MATRIX ======================= @@ -108,6 +121,7 @@ gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES metal | --- | YES | --- | YES | --- | --- d3d11 | YES | --- | --- | --- | --- | --- + webgpu | YES(4) | YES(4)| YES(4)| NO | NO | YES noapi | YES | TODO | TODO | --- | TODO | --- KEY_DOWN | YES | YES | YES | SOME | TODO | YES KEY_UP | YES | YES | YES | SOME | TODO | YES @@ -148,6 +162,8 @@ (1) macOS has no regular window icons, instead the dock icon is changed (2) supported with EGL only (not GLX) (3) fullscreen in the browser not supported on iphones + (4) WebGPU on native desktop platforms should be considered experimental + and mainly useful for debugging and benchmarking STEP BY STEP ============ @@ -1760,6 +1776,9 @@ typedef struct sapp_allocator { _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCREATE, "NativeActivity onCreate") \ _SAPP_LOGITEM_XMACRO(ANDROID_CREATE_THREAD_PIPE_FAILED, "failed to create thread pipe") \ _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, "NativeActivity successfully created") \ + _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_LOST, "wgpu: device lost") \ + _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_LOG, "wgpu: device log") \ + _SAPP_LOGITEM_XMACRO(WGPU_DEVICE_UNCAPTURED_ERROR, "wgpu: uncaptured error") \ _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED, "wgpu: failed to create surface for swapchain") \ _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_SURFACE_GET_CAPABILITIES_FAILED, "wgpu: wgpuSurfaceGetCapabilities failed") \ _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED, "wgpu: failed to create depth-stencil texture for swapchain") \ @@ -2120,8 +2139,8 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE /* MacOS */ #define _SAPP_MACOS (1) - #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE) - #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE") + #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE) && !defined(SOKOL_WGPU) + #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL, SOKOL_GLCORE or SOKOL_WGPU") #endif #else /* iOS or iOS Simulator */ @@ -2139,8 +2158,8 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #elif defined(_WIN32) /* Windows (D3D11 or GL) */ #define _SAPP_WIN32 (1) - #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE) && !defined(SOKOL_NOAPI) - #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11, SOKOL_GLCORE or SOKOL_NOAPI") + #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE) && !defined(SOKOL_WGPU) && !defined(SOKOL_NOAPI) + #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11, SOKOL_GLCORE, SOKOL_WGPU or SOKOL_NOAPI") #endif #elif defined(__ANDROID__) /* Android */ @@ -2154,17 +2173,21 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #elif defined(__linux__) || defined(__unix__) /* Linux */ #define _SAPP_LINUX (1) + #if !defined(SOKOL_GLCORE) && !defined(SOKOL_GLES3) && !defined(SOKOL_WGPU) + #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE, SOKOL_GLES3 or SOKOL_WGPU") + #endif #if defined(SOKOL_GLCORE) - #if !defined(SOKOL_FORCE_EGL) + #if defined(SOKOL_FORCE_EGL) + #define _SAPP_EGL (1) + #else #define _SAPP_GLX (1) #endif #define GL_GLEXT_PROTOTYPES #include <GL/gl.h> #elif defined(SOKOL_GLES3) + #define _SAPP_EGL (1) #include <GLES3/gl3.h> #include <GLES3/gl3ext.h> - #else - #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE, SOKOL_GLES3") #endif #else #error "sokol_app.h: Unknown platform" @@ -2203,21 +2226,28 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #if defined(SOKOL_WGPU) #include <webgpu/webgpu.h> + #if !defined(__EMSCRIPTEN__) + #define _SAPP_WGPU_HAS_WAIT (1) + #endif #endif #if defined(_SAPP_APPLE) + #ifndef GL_SILENCE_DEPRECATION + #define GL_SILENCE_DEPRECATION + #endif #if defined(SOKOL_METAL) #import <Metal/Metal.h> #import <MetalKit/MetalKit.h> #endif #if defined(_SAPP_MACOS) + #import <Cocoa/Cocoa.h> #if defined(_SAPP_ANY_GL) - #ifndef GL_SILENCE_DEPRECATION - #define GL_SILENCE_DEPRECATION - #endif - #include <Cocoa/Cocoa.h> #include <OpenGL/gl3.h> #endif + #if defined(SOKOL_WGPU) + #import <QuartzCore/CAMetalLayer.h> + #import <QuartzCore/CADisplayLink.h> + #endif #elif defined(_SAPP_IOS) #import <UIKit/UIKit.h> #if defined(_SAPP_ANY_GL) @@ -2319,7 +2349,7 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #include <X11/Xcursor/Xcursor.h> #include <X11/cursorfont.h> /* XC_* font cursors */ #include <X11/Xmd.h> /* CARD32 */ - #if !defined(_SAPP_GLX) + #if defined(_SAPP_EGL) #include <EGL/egl.h> #endif #include <dlfcn.h> /* dlopen, dlsym, dlclose */ @@ -2576,7 +2606,7 @@ typedef struct { WGPUTexture depth_stencil_tex; WGPUTextureView depth_stencil_view; WGPUTextureView swapchain_view; - bool async_init_done; + bool init_done; } _sapp_wgpu_t; #endif @@ -2594,6 +2624,10 @@ typedef struct { @interface _sapp_macos_view : NSOpenGLView - (void)timerFired:(id)sender; @end +#elif defined(SOKOL_WGPU) + @interface _sapp_macos_view : NSView + - (void)displayLinkFired:(id)sender; + @end #endif // SOKOL_GLCORE typedef struct { @@ -2610,6 +2644,12 @@ typedef struct { #if defined(SOKOL_METAL) id<MTLDevice> mtl_device; #endif + #if defined(SOKOL_WGPU) + struct { + CAMetalLayer* mtl_layer; + CADisplayLink* display_link; + } wgpu; + #endif } _sapp_macos_t; #endif // _SAPP_MACOS @@ -2988,16 +3028,15 @@ typedef struct { bool ARB_create_context; bool ARB_create_context_profile; } _sapp_glx_t; +#endif // _SAPP_GLX -#else - +#if defined(_SAPP_EGL) typedef struct { EGLDisplay display; EGLContext context; EGLSurface surface; } _sapp_egl_t; - -#endif // _SAPP_GLX +#endif // _SAPP_EGL #endif // _SAPP_LINUX #if defined(_SAPP_ANY_GL) @@ -3079,7 +3118,7 @@ typedef struct { _sapp_x11_t x11; #if defined(_SAPP_GLX) _sapp_glx_t glx; - #else + #elif defined(_SAPP_EGL) _sapp_egl_t egl; #endif #endif @@ -3113,6 +3152,10 @@ static const char* _sapp_log_messages[] = { #define _SAPP_ERROR(code) _sapp_log(SAPP_LOGITEM_ ##code, 1, 0, __LINE__) #define _SAPP_WARN(code) _sapp_log(SAPP_LOGITEM_ ##code, 2, 0, __LINE__) #define _SAPP_INFO(code) _sapp_log(SAPP_LOGITEM_ ##code, 3, 0, __LINE__) +#define _SAPP_PANIC_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 0, msg, __LINE__) +#define _SAPP_ERROR_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 1, msg, __LINE__) +#define _SAPP_WARN_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 2, msg, __LINE__) +#define _SAPP_INFO_MSG(code, msg) _sapp_log(SAPP_LOGITEM_ ##code, 3, msg, __LINE__) static void _sapp_log(sapp_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { if (_sapp.desc.logger.func) { @@ -3243,8 +3286,8 @@ _SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(int index) { return &_sapp.drop.buffer[offset]; } -/* Copy a string into a fixed size buffer with guaranteed zero- - termination. +/* Copy a string (either zero-terminated or with explicit length) + into a fixed size buffer with guaranteed zero-termination. Return false if the string didn't fit into the buffer and had to be clamped. @@ -3252,18 +3295,24 @@ _SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(int index) { is clamped, because the last zero-byte might be written into the middle of a multi-byte sequence. */ -_SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) { - SOKOL_ASSERT(src && dst && (max_len > 0)); - char* const end = &(dst[max_len-1]); +_SOKOL_PRIVATE bool _sapp_strcpy_range(const char* src, size_t src_len, char* dst, size_t dst_buf_len) { + SOKOL_ASSERT(src && dst && (dst_buf_len > 0)); + if (0 == src_len) { + src_len = dst_buf_len; + } + char* const end = &(dst[dst_buf_len-1]); char c = 0; - for (int i = 0; i < max_len; i++) { + for (size_t i = 0; i < dst_buf_len; i++) { c = *src; + if (i >= src_len) { + c = 0; + } if (c != 0) { src++; } *dst++ = c; } - /* truncated? */ + // truncated? if (c != 0) { *end = 0; return false; @@ -3272,6 +3321,10 @@ _SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) { } } +_SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, size_t dst_buf_len) { + return _sapp_strcpy_range(src, 0, dst, dst_buf_len); +} + _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) { SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); sapp_desc res = *desc; @@ -3590,6 +3643,45 @@ _SOKOL_PRIVATE WGPUStringView _sapp_wgpu_stringview(const char* str) { return res; } +_SOKOL_PRIVATE WGPUCallbackMode _sapp_wgpu_callbackmode(void) { + #if defined(_SAPP_WGPU_HAS_WAIT) + return WGPUCallbackMode_WaitAnyOnly; + #else + return WGPUCallbackMode_AllowProcessEvents; + #endif +} + +_SOKOL_PRIVATE void _sapp_wgpu_await(WGPUFuture future) { + #if defined(_SAPP_WGPU_HAS_WAIT) + SOKOL_ASSERT(_sapp.wgpu.instance); + WGPUFutureWaitInfo wait_info; + _sapp_clear(&wait_info, sizeof(wait_info)); + wait_info.future = future; + WGPUWaitStatus res = wgpuInstanceWaitAny(_sapp.wgpu.instance, 1, &wait_info, UINT64_MAX); + SOKOL_ASSERT(res == WGPUWaitStatus_Success); _SOKOL_UNUSED(res); + #else + // this code path should never be called + _SOKOL_UNUSED(future); + SOKOL_ASSERT(false); + #endif +} + +_SOKOL_PRIVATE WGPUTextureFormat _sapp_wgpu_pick_render_format(size_t count, const WGPUTextureFormat* formats) { + // NOTE: only accept non-SRGB formats until sokol_app.h gets proper SRGB support + SOKOL_ASSERT((count > 0) && formats); + for (size_t i = 0; i < count; i++) { + const WGPUTextureFormat fmt = formats[i]; + switch (fmt) { + case WGPUTextureFormat_RGBA8Unorm: + case WGPUTextureFormat_BGRA8Unorm: + return fmt; + default: break; + } + } + // FIXME: fallback might still return an SRGB format + return formats[0]; +} + _SOKOL_PRIVATE void _sapp_wgpu_create_swapchain(bool called_from_resize) { SOKOL_ASSERT(_sapp.wgpu.instance); SOKOL_ASSERT(_sapp.wgpu.device); @@ -3597,7 +3689,6 @@ _SOKOL_PRIVATE void _sapp_wgpu_create_swapchain(bool called_from_resize) { SOKOL_ASSERT(0 == _sapp.wgpu.msaa_view); SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_tex); SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_view); - SOKOL_ASSERT(0 == _sapp.wgpu.swapchain_view); if (!called_from_resize) { SOKOL_ASSERT(0 == _sapp.wgpu.surface); @@ -3609,8 +3700,28 @@ _SOKOL_PRIVATE void _sapp_wgpu_create_swapchain(bool called_from_resize) { html_canvas_desc.chain.sType = WGPUSType_EmscriptenSurfaceSourceCanvasHTMLSelector; html_canvas_desc.selector = _sapp_wgpu_stringview(_sapp.html5_canvas_selector); surf_desc.nextInChain = &html_canvas_desc.chain; + #elif defined(_SAPP_MACOS) + WGPUSurfaceSourceMetalLayer from_metal_layer; + _sapp_clear(&from_metal_layer, sizeof(from_metal_layer)); + from_metal_layer.chain.sType = WGPUSType_SurfaceSourceMetalLayer; + from_metal_layer.layer = _sapp.macos.view.layer; + surf_desc.nextInChain = &from_metal_layer.chain; + #elif defined(_SAPP_WIN32) + WGPUSurfaceSourceWindowsHWND from_hwnd; + _sapp_clear(&from_hwnd, sizeof(from_hwnd)); + from_hwnd.chain.sType = WGPUSType_SurfaceSourceWindowsHWND; + from_hwnd.hinstance = GetModuleHandleW(NULL); + from_hwnd.hwnd = _sapp.win32.hwnd; + surf_desc.nextInChain = &from_hwnd.chain; + #elif defined(_SAPP_LINUX) + WGPUSurfaceSourceXlibWindow from_xlib; + _sapp_clear(&from_xlib, sizeof(from_xlib)); + from_xlib.chain.sType = WGPUSType_SurfaceSourceXlibWindow; + from_xlib.display = _sapp.x11.display; + from_xlib.window = _sapp.x11.window; + surf_desc.nextInChain = &from_xlib.chain; #else - #error "Unsupported platform for SOKOL_WGPU" + #error "sokol_app.h: unsupported WebGPU platform" #endif _sapp.wgpu.surface = wgpuInstanceCreateSurface(_sapp.wgpu.instance, &surf_desc); if (0 == _sapp.wgpu.surface) { @@ -3622,7 +3733,7 @@ _SOKOL_PRIVATE void _sapp_wgpu_create_swapchain(bool called_from_resize) { if (caps_status != WGPUStatus_Success) { _SAPP_PANIC(WGPU_SWAPCHAIN_SURFACE_GET_CAPABILITIES_FAILED); } - _sapp.wgpu.render_format = surf_caps.formats[0]; + _sapp.wgpu.render_format = _sapp_wgpu_pick_render_format(surf_caps.formatCount, surf_caps.formats); } SOKOL_ASSERT(_sapp.wgpu.surface); @@ -3736,18 +3847,61 @@ _SOKOL_PRIVATE WGPUTextureView _sapp_wgpu_swapchain_next(void) { return wgpuTextureCreateView(surf_tex.texture, 0); } -_SOKOL_PRIVATE void _sapp_wgpu_size_changed(void) { +_SOKOL_PRIVATE void _sapp_wgpu_swapchain_size_changed(void) { if (_sapp.wgpu.surface) { _sapp_wgpu_discard_swapchain(true); _sapp_wgpu_create_swapchain(true); } } +_SOKOL_PRIVATE void _sapp_wgpu_device_lost_cb(const WGPUDevice* dev, WGPUDeviceLostReason reason, WGPUStringView msg, void* ud1, void* ud2) { + _SOKOL_UNUSED(dev); _SOKOL_UNUSED(reason); _SOKOL_UNUSED(ud1); _SOKOL_UNUSED(ud2); + // NOTE: on wgpuInstanceRelease(), the device lost callback is always called with + // WGPUDeviceLostReason_CallbackCancelled (even though no device should exist at that point) + if (reason != WGPUDeviceLostReason_CallbackCancelled) { + SOKOL_ASSERT(msg.data && (msg.length > 0)); + char buf[1024]; + _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf)); + _SAPP_ERROR_MSG(WGPU_DEVICE_LOST, buf); + } +} + +// NOTE: emdawnwebgpu doesn't seem to have a device logging callback +#if !defined(_SAPP_EMSCRIPTEN) +_SOKOL_PRIVATE void _sapp_wgpu_device_logging_cb(WGPULoggingType log_type, WGPUStringView msg, void* ud1, void* ud2) { + _SOKOL_UNUSED(log_type); _SOKOL_UNUSED(ud1); _SOKOL_UNUSED(ud2); + SOKOL_ASSERT(msg.data && (msg.length > 0)); + char buf[1024]; + _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf)); + switch (log_type) { + case WGPULoggingType_Warning: + _SAPP_WARN_MSG(WGPU_DEVICE_LOG, buf); + break; + case WGPULoggingType_Error: + _SAPP_ERROR_MSG(WGPU_DEVICE_LOG, buf); + break; + default: + _SAPP_INFO_MSG(WGPU_DEVICE_LOG, buf); + break; + } +} +#endif + +_SOKOL_PRIVATE void _sapp_wgpu_uncaptured_error_cb(const WGPUDevice* dev, WGPUErrorType err_type, WGPUStringView msg, void* ud1, void* ud2) { + _SOKOL_UNUSED(dev); _SOKOL_UNUSED(ud1); _SOKOL_UNUSED(ud2); + if (err_type != WGPUErrorType_NoError) { + SOKOL_ASSERT(msg.data && (msg.length > 0)); + char buf[1024]; + _sapp_strcpy_range(msg.data, msg.length, buf, sizeof(buf)); + _SAPP_ERROR_MSG(WGPU_DEVICE_UNCAPTURED_ERROR, buf); + } +} + _SOKOL_PRIVATE void _sapp_wgpu_request_device_cb(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView msg, void* userdata1, void* userdata2) { _SOKOL_UNUSED(msg); _SOKOL_UNUSED(userdata1); _SOKOL_UNUSED(userdata2); - SOKOL_ASSERT(!_sapp.wgpu.async_init_done); + SOKOL_ASSERT(!_sapp.wgpu.init_done); if (status != WGPURequestDeviceStatus_Success) { if (status == WGPURequestDeviceStatus_Error) { _SAPP_PANIC(WGPU_REQUEST_DEVICE_STATUS_ERROR); @@ -3757,42 +3911,37 @@ _SOKOL_PRIVATE void _sapp_wgpu_request_device_cb(WGPURequestDeviceStatus status, } SOKOL_ASSERT(device); _sapp.wgpu.device = device; + #if !defined(_SAPP_EMSCRIPTEN) + WGPULoggingCallbackInfo cb_info; + _sapp_clear(&cb_info, sizeof(cb_info)); + cb_info.callback = _sapp_wgpu_device_logging_cb; + wgpuDeviceSetLoggingCallback(_sapp.wgpu.device, cb_info); + #endif _sapp_wgpu_create_swapchain(false); - _sapp.wgpu.async_init_done = true; + _sapp.wgpu.init_done = true; } -_SOKOL_PRIVATE void _sapp_wgpu_request_adapter_cb(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView msg, void* userdata1, void* userdata2) { - _SOKOL_UNUSED(msg); - _SOKOL_UNUSED(userdata1); - _SOKOL_UNUSED(userdata2); - if (status != WGPURequestAdapterStatus_Success) { - switch (status) { - case WGPURequestAdapterStatus_Unavailable: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE); break; - case WGPURequestAdapterStatus_Error: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_ERROR); break; - default: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN); break; - } - } - SOKOL_ASSERT(adapter); - _sapp.wgpu.adapter = adapter; +_SOKOL_PRIVATE void _sapp_wgpu_create_device_and_swapchain(void) { + SOKOL_ASSERT(_sapp.wgpu.adapter); size_t cur_feature_index = 1; #define _SAPP_WGPU_MAX_REQUESTED_FEATURES (8) WGPUFeatureName requiredFeatures[_SAPP_WGPU_MAX_REQUESTED_FEATURES] = { WGPUFeatureName_Depth32FloatStencil8, }; // check for optional features we're interested in - if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionBC)) { + if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_TextureCompressionBC)) { SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionBC; } - if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionETC2)) { + if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_TextureCompressionETC2)) { SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionETC2; } - if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionASTC)) { + if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_TextureCompressionASTC)) { SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionASTC; } - if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_Float32Filterable)) { + if (wgpuAdapterHasFeature(_sapp.wgpu.adapter, WGPUFeatureName_Float32Filterable)) { SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); requiredFeatures[cur_feature_index++] = WGPUFeatureName_Float32Filterable; } @@ -3800,38 +3949,109 @@ _SOKOL_PRIVATE void _sapp_wgpu_request_adapter_cb(WGPURequestAdapterStatus statu WGPURequestDeviceCallbackInfo cb_info; _sapp_clear(&cb_info, sizeof(cb_info)); - cb_info.mode = WGPUCallbackMode_AllowProcessEvents; + cb_info.mode = _sapp_wgpu_callbackmode(); cb_info.callback = _sapp_wgpu_request_device_cb; WGPUDeviceDescriptor dev_desc; _sapp_clear(&dev_desc, sizeof(dev_desc)); dev_desc.requiredFeatureCount = cur_feature_index; - dev_desc.requiredFeatures = requiredFeatures, - wgpuAdapterRequestDevice(adapter, &dev_desc, cb_info); + dev_desc.requiredFeatures = requiredFeatures; + dev_desc.deviceLostCallbackInfo.mode = WGPUCallbackMode_AllowProcessEvents; + dev_desc.deviceLostCallbackInfo.callback = _sapp_wgpu_device_lost_cb; + dev_desc.uncapturedErrorCallbackInfo.callback = _sapp_wgpu_uncaptured_error_cb; + WGPUFuture future = wgpuAdapterRequestDevice(_sapp.wgpu.adapter, &dev_desc, cb_info); + #if defined(_SAPP_WGPU_HAS_WAIT) + _sapp_wgpu_await(future); + #else + _SOKOL_UNUSED(future); + #endif } -_SOKOL_PRIVATE void _sapp_wgpu_init(void) { - SOKOL_ASSERT(0 == _sapp.wgpu.instance); - SOKOL_ASSERT(!_sapp.wgpu.async_init_done); - _sapp.wgpu.instance = wgpuCreateInstance(0); - if (0 == _sapp.wgpu.instance) { - _SAPP_PANIC(WGPU_CREATE_INSTANCE_FAILED); +_SOKOL_PRIVATE void _sapp_wgpu_request_adapter_cb(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView msg, void* userdata1, void* userdata2) { + _SOKOL_UNUSED(msg); + _SOKOL_UNUSED(userdata1); + _SOKOL_UNUSED(userdata2); + if (status != WGPURequestAdapterStatus_Success) { + switch (status) { + case WGPURequestAdapterStatus_Unavailable: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE); break; + case WGPURequestAdapterStatus_Error: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_ERROR); break; + default: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN); break; + } } + SOKOL_ASSERT(adapter); + _sapp.wgpu.adapter = adapter; + #if !defined(_SAPP_WGPU_HAS_WAIT) + // chain device creation + _sapp_wgpu_create_device_and_swapchain(); + #endif +} + +_SOKOL_PRIVATE void _sapp_wgpu_create_adapter(void) { + SOKOL_ASSERT(_sapp.wgpu.instance); // FIXME: power preference? WGPURequestAdapterCallbackInfo cb_info; _sapp_clear(&cb_info, sizeof(cb_info)); - cb_info.mode = WGPUCallbackMode_AllowProcessEvents; + cb_info.mode = _sapp_wgpu_callbackmode(); cb_info.callback = _sapp_wgpu_request_adapter_cb; - wgpuInstanceRequestAdapter(_sapp.wgpu.instance, 0, cb_info); + WGPUFuture future = wgpuInstanceRequestAdapter(_sapp.wgpu.instance, 0, cb_info); + #if defined(_SAPP_WGPU_HAS_WAIT) + _sapp_wgpu_await(future); + #else + _SOKOL_UNUSED(future); + #endif +} + +_SOKOL_PRIVATE void _sapp_wgpu_init(void) { + SOKOL_ASSERT(0 == _sapp.wgpu.instance); + SOKOL_ASSERT(!_sapp.wgpu.init_done); + + WGPUInstanceDescriptor desc; + _sapp_clear(&desc, sizeof(desc)); + #if defined(_SAPP_WGPU_HAS_WAIT) + WGPUInstanceFeatureName inst_features[1] = { + WGPUInstanceFeatureName_TimedWaitAny, + }; + desc.requiredFeatureCount = 1; + desc.requiredFeatures = inst_features; + #endif + _sapp.wgpu.instance = wgpuCreateInstance(&desc); + if (0 == _sapp.wgpu.instance) { + _SAPP_PANIC(WGPU_CREATE_INSTANCE_FAILED); + } + // NOTE: on Emscripten, device and swapchain creation are chained in the callacks + _sapp_wgpu_create_adapter(); + #if defined(_SAPP_WGPU_HAS_WAIT) + _sapp_wgpu_create_device_and_swapchain(); + SOKOL_ASSERT(_sapp.wgpu.init_done); + #endif +} + +_SOKOL_PRIVATE void _sapp_wgpu_discard(void) { + _sapp_wgpu_discard_swapchain(false); + if (_sapp.wgpu.device) { + wgpuDeviceRelease(_sapp.wgpu.device); + _sapp.wgpu.device = 0; + } + if (_sapp.wgpu.adapter) { + wgpuAdapterRelease(_sapp.wgpu.adapter); + _sapp.wgpu.adapter = 0; + } + if (_sapp.wgpu.instance) { + wgpuInstanceRelease(_sapp.wgpu.instance); + _sapp.wgpu.instance = 0; + } } _SOKOL_PRIVATE void _sapp_wgpu_frame(void) { wgpuInstanceProcessEvents(_sapp.wgpu.instance); - if (_sapp.wgpu.async_init_done) { + if (_sapp.wgpu.init_done) { _sapp.wgpu.swapchain_view = _sapp_wgpu_swapchain_next(); _sapp_frame(); wgpuTextureViewRelease(_sapp.wgpu.swapchain_view); _sapp.wgpu.swapchain_view = 0; + #if !defined(_SAPP_EMSCRIPTEN) + wgpuSurfacePresent(_sapp.wgpu.surface); + #endif } } #endif // SOKOL_WGPU @@ -3860,14 +4080,21 @@ _SOKOL_PRIVATE void _sapp_wgpu_frame(void) { // >>macos #if defined(_SAPP_MACOS) -#if defined(SOKOL_METAL) -_SOKOL_PRIVATE void _sapp_macos_mtl_init(void) { +NSInteger _sapp_macos_max_fps(void) { NSInteger max_fps = 60; #if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) if (@available(macOS 12.0, *)) { max_fps = [NSScreen.mainScreen maximumFramesPerSecond]; } #endif + return max_fps; +} + +#if defined(SOKOL_METAL) +_SOKOL_PRIVATE void _sapp_macos_mtl_init(void) { + NSInteger max_fps = _sapp_macos_max_fps(); + // NOTE: when eventually switching to CAMetalLayer, use the specialized + // CAMetalDisplayLink instead of CADisplayLink! _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice(); _sapp.macos.view = [[_sapp_macos_view alloc] init]; [_sapp.macos.view updateTrackingAreas]; @@ -3887,9 +4114,9 @@ _SOKOL_PRIVATE void _sapp_macos_mtl_discard_state(void) { _SOKOL_PRIVATE bool _sapp_macos_mtl_update_framebuffer_dimensions(NSRect view_bounds) { _sapp.framebuffer_width = _sapp_roundf_gzero(view_bounds.size.width * _sapp.dpi_scale); _sapp.framebuffer_height = _sapp_roundf_gzero(view_bounds.size.height * _sapp.dpi_scale); - const CGSize fb_size = _sapp.macos.view.drawableSize; - int cur_fb_width = _sapp_roundf_gzero(fb_size.width); - int cur_fb_height = _sapp_roundf_gzero(fb_size.height); + const CGSize cur_fb_size = _sapp.macos.view.drawableSize; + int cur_fb_width = _sapp_roundf_gzero(cur_fb_size.width); + int cur_fb_height = _sapp_roundf_gzero(cur_fb_size.height); bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height); if (dim_changed) { const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; @@ -3897,22 +4124,6 @@ _SOKOL_PRIVATE bool _sapp_macos_mtl_update_framebuffer_dimensions(NSRect view_bo } return dim_changed; } - -_SOKOL_PRIVATE void _sapp_macos_mtl_on_window_will_start_live_resize(void) { - // Work around the MTKView resizing glitch by "anchoring" the layer to the window corner opposite - // to the currently manipulated corner (or edge). This prevents the content stretching back and - // forth during resizing. This is a workaround for this issue: https://github.com/floooh/sokol/issues/700 - // Can be removed if/when migrating to CAMetalLayer: https://github.com/floooh/sokol/issues/727 - bool resizing_from_left = _sapp.mouse.x < _sapp.window_width/2; - bool resizing_from_top = _sapp.mouse.y < _sapp.window_height/2; - NSViewLayerContentsPlacement placement; - if (resizing_from_left) { - placement = resizing_from_top ? NSViewLayerContentsPlacementBottomRight : NSViewLayerContentsPlacementTopRight; - } else { - placement = resizing_from_top ? NSViewLayerContentsPlacementBottomLeft : NSViewLayerContentsPlacementTopLeft; - } - _sapp.macos.view.layerContentsPlacement = placement; -} #endif #if defined(SOKOL_GLCORE) @@ -3979,6 +4190,48 @@ _SOKOL_PRIVATE bool _sapp_macos_gl_update_framebuffer_dimensions(NSRect view_bou } #endif +#if defined(SOKOL_WGPU) +_SOKOL_PRIVATE void _sapp_macos_wgpu_init(void) { + NSInteger max_fps = _sapp_macos_max_fps(); + _sapp.macos.wgpu.mtl_layer = [CAMetalLayer layer]; + _sapp.macos.wgpu.mtl_layer.magnificationFilter = kCAFilterNearest; + _sapp.macos.wgpu.mtl_layer.opaque = true; + // NOTE: might experiment with this, valid values are 2 or 3 (default: 3), I don't see any difference tbh + // _sapp.macos.wgpu.mtl_layer.maximumDrawableCount = 2; + _sapp.macos.view = [[_sapp_macos_view alloc] init]; + [_sapp.macos.view updateTrackingAreas]; + _sapp.macos.view.wantsLayer = YES; + _sapp.macos.view.layer = _sapp.macos.wgpu.mtl_layer; + _sapp.macos.wgpu.display_link = [_sapp.macos.view displayLinkWithTarget:_sapp.macos.view selector:@selector(displayLinkFired:)]; + float preferred_fps = max_fps / _sapp.swap_interval; + CAFrameRateRange frame_rate_range = { preferred_fps, preferred_fps, preferred_fps }; + _sapp.macos.wgpu.display_link.preferredFrameRateRange = frame_rate_range; + [_sapp.macos.wgpu.display_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + _sapp_wgpu_init(); +} + +_SOKOL_PRIVATE void _sapp_macos_wgpu_discard_state(void) { + _SAPP_OBJC_RELEASE(_sapp.macos.wgpu.display_link); + _SAPP_OBJC_RELEASE(_sapp.macos.wgpu.mtl_layer); + _sapp_wgpu_discard(); +} + +_SOKOL_PRIVATE bool _sapp_macos_wgpu_update_framebuffer_dimensions(NSRect view_bounds) { + _sapp.framebuffer_width = _sapp_roundf_gzero(view_bounds.size.width * _sapp.dpi_scale); + _sapp.framebuffer_height = _sapp_roundf_gzero(view_bounds.size.height * _sapp.dpi_scale); + const CGSize cur_fb_size = _sapp.macos.wgpu.mtl_layer.drawableSize; + int cur_fb_width = _sapp_roundf_gzero(cur_fb_size.width); + int cur_fb_height = _sapp_roundf_gzero(cur_fb_size.height); + bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height); + if (dim_changed) { + const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; + _sapp.macos.wgpu.mtl_layer.drawableSize = drawable_size; + _sapp_wgpu_swapchain_size_changed(); + } + return dim_changed; +} +#endif + _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { _sapp.keycodes[0x1D] = SAPP_KEYCODE_0; _sapp.keycodes[0x12] = SAPP_KEYCODE_1; @@ -4108,6 +4361,8 @@ _SOKOL_PRIVATE void _sapp_macos_discard_state(void) { _sapp_macos_mtl_discard_state(); #elif defined(SOKOL_GLCORE) _sapp_macos_gl_discard_state(); + #elif defined(SOKOL_WGPU) + _sapp_macos_wgpu_discard_state(); #endif _SAPP_OBJC_RELEASE(_sapp.macos.window); } @@ -4244,6 +4499,8 @@ _SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { bool dim_changed = _sapp_macos_mtl_update_framebuffer_dimensions(bounds); #elif defined(SOKOL_GLCORE) bool dim_changed = _sapp_macos_gl_update_framebuffer_dimensions(bounds); + #elif defined(SOKOL_WGPU) + bool dim_changed = _sapp_macos_wgpu_update_framebuffer_dimensions(bounds); #endif if (dim_changed && !_sapp.first_frame) { _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); @@ -4279,7 +4536,7 @@ _SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(void) { if (!str) { return _sapp.clipboard.buffer; } - _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size); } return _sapp.clipboard.buffer; } @@ -4444,13 +4701,6 @@ _SOKOL_PRIVATE void _sapp_macos_set_icon(const sapp_icon_desc* icon_desc, int nu CGImageRelease(cg_img); } -_SOKOL_PRIVATE void _sapp_macos_frame(void) { - _sapp_frame(); - if (_sapp.quit_requested || _sapp.quit_ordered) { - [_sapp.macos.window performClose:nil]; - } -} - @implementation _sapp_macos_app_delegate - (void)applicationDidFinishLaunching:(NSNotification*)aNotification { _SOKOL_UNUSED(aNotification); @@ -4487,6 +4737,8 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { _sapp_macos_mtl_init(); #elif defined(SOKOL_GLCORE) _sapp_macos_gl_init(window_rect); + #elif defined(SOKOL_WGPU) + _sapp_macos_wgpu_init(); #endif _sapp.macos.window.contentView = _sapp.macos.view; [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; @@ -4552,8 +4804,20 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } - (void)windowWillStartLiveResize:(NSNotification *)notification { - #if defined(SOKOL_METAL) - _sapp_macos_mtl_on_window_will_start_live_resize(); + #if defined(SOKOL_METAL) || defined(SOKOL_WGPU) + // Work around the MTKView/CAMetalLayer resizing glitch by "anchoring" the layer to the window corner opposite + // to the currently manipulated corner (or edge). This prevents the content stretching back and + // forth during resizing. This is a workaround for this issue: https://github.com/floooh/sokol/issues/700 + // Can be removed if/when migrating to CAMetalLayer: https://github.com/floooh/sokol/issues/727 + bool resizing_from_left = _sapp.mouse.x < _sapp.window_width/2; + bool resizing_from_top = _sapp.mouse.y < _sapp.window_height/2; + NSViewLayerContentsPlacement placement; + if (resizing_from_left) { + placement = resizing_from_top ? NSViewLayerContentsPlacementBottomRight : NSViewLayerContentsPlacementTopRight; + } else { + placement = resizing_from_top ? NSViewLayerContentsPlacementBottomLeft : NSViewLayerContentsPlacementTopLeft; + } + _sapp.macos.view.layerContentsPlacement = placement; #endif } @@ -4629,7 +4893,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { bool drop_failed = false; for (int i = 0; i < _sapp.drop.num_files; i++) { NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[(NSUInteger)i] stringForType:NSPasteboardTypeFileURL]]; - if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { + if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), (size_t)_sapp.drop.max_path_length)) { _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); drop_failed = true; break; @@ -4668,18 +4932,38 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } #endif +#if defined(SOKOL_WGPU) +- (void)displayLinkFired:(id)sender { + _SOKOL_UNUSED(sender); + _sapp_timing_measure(&_sapp.timing); + @autoreleasepool { + _sapp_wgpu_frame(); + } + if (_sapp.quit_requested || _sapp.quit_ordered) { + [_sapp.macos.window performClose:nil]; + } +} +#endif + - (void)drawRect:(NSRect)rect { _SOKOL_UNUSED(rect); + #if defined(SOKOL_WGPU) + // should never be called + return; + #endif #if defined(_SAPP_ANY_GL) glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); #endif _sapp_timing_measure(&_sapp.timing); @autoreleasepool { - _sapp_macos_frame(); + _sapp_frame(); } #if defined(_SAPP_ANY_GL) [[_sapp.macos.view openGLContext] flushBuffer]; #endif + if (_sapp.quit_requested || _sapp.quit_ordered) { + [_sapp.macos.window performClose:nil]; + } } - (BOOL)isOpaque { @@ -4878,6 +5162,7 @@ static void _sapp_gl_make_current(void) { event.isARepeat, _sapp_macos_mods(event)); } + - (void)flagsChanged:(NSEvent*)event { const uint32_t old_f = _sapp.macos.flags_changed_store; const uint32_t new_f = (uint32_t)event.modifierFlags; @@ -5289,7 +5574,7 @@ typedef void (*_sapp_html5_fetch_callback) (const sapp_html5_fetch_response*); EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) { if (_sapp.clipboard.enabled) { - _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + _sapp_strcpy(str, _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size); if (_sapp_events_enabled()) { _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); _sapp_call_event(&_sapp.event); @@ -5328,7 +5613,7 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) { if ((i < 0) || (i >= _sapp.drop.num_files)) { return; } - if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { + if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), (size_t)_sapp.drop.max_path_length)) { _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); _sapp.drop.num_files = 0; } @@ -5880,7 +6165,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenU emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); #if defined(SOKOL_WGPU) // on WebGPU: recreate size-dependent rendering surfaces - _sapp_wgpu_size_changed(); + _sapp_wgpu_swapchain_size_changed(); #endif if (_sapp_events_enabled()) { _sapp_init_event(SAPP_EVENTTYPE_RESIZED); @@ -6374,7 +6659,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame_animation_loop(double time, void* userDa if (_sapp.quit_ordered) { _sapp_emsc_unregister_eventhandlers(); #if defined(SOKOL_WGPU) - _sapp_wgpu_discard_swapchain(false); + _sapp_wgpu_discard(); #endif _sapp_call_cleanup(); _sapp_discard_state(); @@ -7888,6 +8173,26 @@ _SOKOL_PRIVATE void _sapp_win32_timing_measure(void) { #endif } +_SOKOL_PRIVATE void _sapp_win32_frame(bool from_winproc) { + #if defined(SOKOL_WGPU) + _sapp_wgpu_frame(); + #else + _sapp_frame(); + #endif + #if defined(SOKOL_D3D11) + bool do_not_wait = from_winproc; + _sapp_d3d11_present(do_not_wait); + #endif + #if defined(SOKOL_GLCORE) + _sapp_wgl_swap_buffers(); + #endif + if (!from_winproc) { + if (IsIconic(_sapp.win32.hwnd)) { + Sleep((DWORD)(16 * _sapp.swap_interval)); + } + } +} + _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (!_sapp.win32.in_create_window) { switch (uMsg) { @@ -8079,14 +8384,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM break; case WM_TIMER: _sapp_win32_timing_measure(); - _sapp_frame(); - #if defined(SOKOL_D3D11) - // present with DXGI_PRESENT_DO_NOT_WAIT - _sapp_d3d11_present(true); - #endif - #if defined(SOKOL_GLCORE) - _sapp_wgl_swap_buffers(); - #endif + _sapp_win32_frame(true); /* NOTE: resizing the swap-chain during resize leads to a substantial memory spike (hundreds of megabytes for a few seconds). @@ -8492,11 +8790,12 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { #if defined(SOKOL_D3D11) _sapp_d3d11_create_device_and_swapchain(); _sapp_d3d11_create_default_render_target(); - #endif - #if defined(SOKOL_GLCORE) + #elif defined(SOKOL_GLCORE) _sapp_wgl_init(); _sapp_wgl_load_extensions(); _sapp_wgl_create_context(); + #elif defined(SOKOL_WGPU) + _sapp_wgpu_init(); #endif _sapp.valid = true; @@ -8513,20 +8812,13 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { DispatchMessageW(&msg); } } - _sapp_frame(); - #if defined(SOKOL_D3D11) - _sapp_d3d11_present(false); - if (IsIconic(_sapp.win32.hwnd)) { - Sleep((DWORD)(16 * _sapp.swap_interval)); - } - #endif - #if defined(SOKOL_GLCORE) - _sapp_wgl_swap_buffers(); - #endif - /* check for window resized, this cannot happen in WM_SIZE as it explodes memory usage */ + _sapp_win32_frame(false); + // check for window resized, this cannot happen in WM_SIZE as it explodes memory usage if (_sapp_win32_update_dimensions()) { #if defined(SOKOL_D3D11) _sapp_d3d11_resize_default_render_target(); + #elif defined(SOKOL_WGPU) + _sapp_wgpu_swapchain_size_changed(); #endif _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); } @@ -8550,6 +8842,8 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { #elif defined(SOKOL_GLCORE) _sapp_wgl_destroy_context(); _sapp_wgl_shutdown(); + #elif defined(SOKOL_WGPU) + _sapp_wgpu_discard(); #endif _sapp_win32_destroy_window(); _sapp_win32_destroy_icons(); @@ -9296,7 +9590,7 @@ void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size activity->callbacks->onInputQueueCreated = _sapp_android_on_input_queue_created; activity->callbacks->onInputQueueDestroyed = _sapp_android_on_input_queue_destroyed; /* activity->callbacks->onContentRectChanged = _sapp_android_on_content_rect_changed; */ - activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; + /* activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; */ activity->callbacks->onLowMemory = _sapp_android_on_low_memory; _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS); @@ -10843,7 +11137,7 @@ _SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) { } } -#endif /* _SAPP_GLX */ +#endif // _SAPP_GLX _SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) { XEvent event; @@ -10878,13 +11172,37 @@ _SOKOL_PRIVATE bool _sapp_x11_wait_for_event(int event_type, double timeout_sec, return true; } -_SOKOL_PRIVATE void _sapp_x11_query_window_size(void) { +_SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_x11_update_dimensions(int x11_window_width, int x11_window_height) { + // NOTE: do *NOT* use _sapp.dpi_scale for the window scale + const float window_scale = _sapp.x11.dpi / 96.0f; + _sapp.window_width = _sapp_roundf_gzero(x11_window_width / window_scale); + _sapp.window_height = _sapp_roundf_gzero(x11_window_height / window_scale); + int cur_fb_width = _sapp.framebuffer_width; + int cur_fb_height = _sapp.framebuffer_height; + _sapp.framebuffer_width = _sapp_roundf_gzero(_sapp.window_width * _sapp.dpi_scale); + _sapp.framebuffer_height = _sapp_roundf_gzero(_sapp.window_height * _sapp.dpi_scale); + bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height); + if (dim_changed) { + #if defined(SOKOL_WGPU) + _sapp_wgpu_swapchain_size_changed(); + #endif + if (!_sapp.first_frame) { + _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED); + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_update_dimensions_from_window_size(void) { XWindowAttributes attribs; XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &attribs); - _sapp.window_width = attribs.width; - _sapp.window_height = attribs.height; - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; + _sapp_x11_update_dimensions(attribs.width, attribs.height); } _SOKOL_PRIVATE void _sapp_x11_set_fullscreen(bool enable) { @@ -10999,7 +11317,7 @@ _SOKOL_PRIVATE void _sapp_x11_destroy_custom_mouse_cursor(sapp_mouse_cursor curs _SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(void) { _sapp.fullscreen = !_sapp.fullscreen; _sapp_x11_set_fullscreen(_sapp.fullscreen); - _sapp_x11_query_window_size(); + _sapp_x11_update_dimensions_from_window_size(); } _SOKOL_PRIVATE void _sapp_x11_update_cursor(sapp_mouse_cursor cursor, bool shown) { @@ -11121,7 +11439,7 @@ _SOKOL_PRIVATE const char* _sapp_x11_get_clipboard_string(void) { XFree(data); return NULL; } - _sapp_strcpy(data, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + _sapp_strcpy(data, _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size); XFree(data); return _sapp.clipboard.buffer; } @@ -11177,7 +11495,12 @@ _SOKOL_PRIVATE void _sapp_x11_set_icon(const sapp_icon_desc* icon_desc, int num_ XFlush(_sapp.x11.display); } -_SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { +_SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual_or_null, int depth) { + Visual* visual = visual_or_null; + if (0 == visual_or_null) { + visual = DefaultVisual(_sapp.x11.display, _sapp.x11.screen); + depth = DefaultDepth(_sapp.x11.display, _sapp.x11.screen); + } _sapp.x11.colormap = XCreateColormap(_sapp.x11.display, _sapp.x11.root, visual, AllocNone); XSetWindowAttributes wa; _sapp_clear(&wa, sizeof(wa)); @@ -11191,20 +11514,22 @@ _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { int display_width = DisplayWidth(_sapp.x11.display, _sapp.x11.screen); int display_height = DisplayHeight(_sapp.x11.display, _sapp.x11.screen); - int window_width = (int)(_sapp.window_width * _sapp.dpi_scale); - int window_height = (int)(_sapp.window_height * _sapp.dpi_scale); - if (0 == window_width) { - window_width = (display_width * 4) / 5; + // NOTE: do *NOT* use _sapp.dpi_scale for the size multiplicator! + const float window_scale = _sapp.x11.dpi / 96.0f; + int x11_window_width = _sapp_roundf_gzero(_sapp.window_width * window_scale); + int x11_window_height = _sapp_roundf_gzero(_sapp.window_height * window_scale); + if (0 == _sapp.window_width) { + x11_window_width = (display_width * 4) / 5; } - if (0 == window_height) { - window_height = (display_height * 4) / 5; + if (0 == _sapp.window_height) { + x11_window_height = (display_height * 4) / 5; } _sapp_x11_grab_error_handler(); _sapp.x11.window = XCreateWindow(_sapp.x11.display, _sapp.x11.root, 0, 0, - (uint32_t)window_width, - (uint32_t)window_height, + (uint32_t)x11_window_width, + (uint32_t)x11_window_height, 0, /* border width */ depth, /* color depth */ InputOutput, @@ -11233,7 +11558,7 @@ _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { XChangeProperty(_sapp.x11.display, _sapp.x11.window, _sapp.x11.xdnd.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); } _sapp_x11_update_window_title(); - _sapp_x11_query_window_size(); + _sapp_x11_update_dimensions_from_window_size(); } _SOKOL_PRIVATE void _sapp_x11_destroy_window(void) { @@ -11359,13 +11684,6 @@ _SOKOL_PRIVATE uint32_t _sapp_x11_mods(uint32_t x11_mods) { return mods; } -_SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp_call_event(&_sapp.event); - } -} - _SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) { switch (event->xbutton.button) { case Button1: return SAPP_MOUSEBUTTON_LEFT; @@ -11377,8 +11695,8 @@ _SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) _SOKOL_PRIVATE void _sapp_x11_mouse_update(int x, int y, bool clear_dxdy) { if (!_sapp.mouse.locked) { - const float new_x = (float) x; - const float new_y = (float) y; + const float new_x = (float)x; + const float new_y = (float)y; if (clear_dxdy) { _sapp.mouse.dx = 0.0f; _sapp.mouse.dy = 0.0f; @@ -11695,16 +12013,6 @@ _SOKOL_PRIVATE void _sapp_x11_on_motionnotify(XEvent* event) { } } -_SOKOL_PRIVATE void _sapp_x11_on_configurenotify(XEvent* event) { - if ((event->xconfigure.width != _sapp.window_width) || (event->xconfigure.height != _sapp.window_height)) { - _sapp.window_width = event->xconfigure.width; - _sapp.window_height = event->xconfigure.height; - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; - _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED); - } -} - _SOKOL_PRIVATE void _sapp_x11_on_propertynotify(XEvent* event) { if (event->xproperty.state == PropertyNewValue) { if (event->xproperty.atom == _sapp.x11.WM_STATE) { @@ -11922,9 +12230,6 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { case MotionNotify: _sapp_x11_on_motionnotify(event); break; - case ConfigureNotify: - _sapp_x11_on_configurenotify(event); - break; case PropertyNotify: _sapp_x11_on_propertynotify(event); break; @@ -11943,18 +12248,18 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { } } -#if !defined(_SAPP_GLX) +#if defined(_SAPP_EGL) _SOKOL_PRIVATE void _sapp_egl_init(void) { -#if defined(SOKOL_GLCORE) - if (!eglBindAPI(EGL_OPENGL_API)) { - _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_API_FAILED); - } -#else - if (!eglBindAPI(EGL_OPENGL_ES_API)) { - _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_ES_API_FAILED); - } -#endif + #if defined(SOKOL_GLCORE) + if (!eglBindAPI(EGL_OPENGL_API)) { + _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_API_FAILED); + } + #else + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_ES_API_FAILED); + } + #endif _sapp.egl.display = eglGetDisplay((EGLNativeDisplayType)_sapp.x11.display); if (EGL_NO_DISPLAY == _sapp.egl.display) { @@ -12073,7 +12378,21 @@ _SOKOL_PRIVATE void _sapp_egl_destroy(void) { } } -#endif /* _SAPP_GLX */ +#endif // _SAPP_EGL + +_SOKOL_PRIVATE void _sapp_linux_frame(void) { + _sapp_x11_update_dimensions_from_window_size(); + #if defined(SOKOL_WGPU) + _sapp_wgpu_frame(); + #else + _sapp_frame(); + #if defined(_SAPP_GLX) + _sapp_glx_swap_buffers(); + #elif defined(_SAPP_EGL) + eglSwapBuffers(_sapp.egl.display, _sapp.egl.surface); + #endif + #endif +} _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { /* The following lines are here to trigger a linker error instead of an @@ -12096,22 +12415,26 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { _sapp.x11.screen = DefaultScreen(_sapp.x11.display); _sapp.x11.root = DefaultRootWindow(_sapp.x11.display); _sapp_x11_query_system_dpi(); + // NOTE: on Linux system-window-size to frame-buffer-size mapping is always 1:1 _sapp.dpi_scale = _sapp.x11.dpi / 96.0f; _sapp_x11_init_extensions(); _sapp_x11_create_standard_cursors(); XkbSetDetectableAutoRepeat(_sapp.x11.display, true, NULL); _sapp_x11_init_keytable(); -#if defined(_SAPP_GLX) - _sapp_glx_init(); - Visual* visual = 0; - int depth = 0; - _sapp_glx_choose_visual(&visual, &depth); - _sapp_x11_create_window(visual, depth); - _sapp_glx_create_context(); - _sapp_glx_swapinterval(_sapp.swap_interval); -#else - _sapp_egl_init(); -#endif + #if defined(_SAPP_GLX) + _sapp_glx_init(); + Visual* visual = 0; + int depth = 0; + _sapp_glx_choose_visual(&visual, &depth); + _sapp_x11_create_window(visual, depth); + _sapp_glx_create_context(); + _sapp_glx_swapinterval(_sapp.swap_interval); + #elif defined(_SAPP_EGL) + _sapp_egl_init(); + #elif defined(SOKOL_WGPU) + _sapp_x11_create_window(0, 0); + _sapp_wgpu_init(); + #endif sapp_set_icon(&desc->icon); _sapp.valid = true; _sapp_x11_show_window(); @@ -12128,16 +12451,11 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { XNextEvent(_sapp.x11.display, &event); _sapp_x11_process_event(&event); } - _sapp_frame(); -#if defined(_SAPP_GLX) - _sapp_glx_swap_buffers(); -#else - eglSwapBuffers(_sapp.egl.display, _sapp.egl.surface); -#endif + _sapp_linux_frame(); XFlush(_sapp.x11.display); - /* handle quit-requested, either from window or from sapp_request_quit() */ + // handle quit-requested, either from window or from sapp_request_quit() if (_sapp.quit_requested && !_sapp.quit_ordered) { - /* give user code a chance to intervene */ + // give user code a chance to intervene _sapp_x11_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); /* if user code hasn't intervened, quit the app */ if (_sapp.quit_requested) { @@ -12146,11 +12464,13 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { } } _sapp_call_cleanup(); -#if defined(_SAPP_GLX) - _sapp_glx_destroy_context(); -#else - _sapp_egl_destroy(); -#endif + #if defined(_SAPP_GLX) + _sapp_glx_destroy_context(); + #elif defined(_SAPP_EGL) + _sapp_egl_destroy(); + #elif defined(SOKOL_WGPU) + _sapp_wgpu_discard(); + #endif _sapp_x11_destroy_window(); _sapp_x11_destroy_standard_cursors(); XCloseDisplay(_sapp.x11.display); @@ -12280,7 +12600,7 @@ SOKOL_API_IMPL const void* sapp_egl_get_display(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_ANDROID) return _sapp.android.display; - #elif defined(_SAPP_LINUX) && !defined(_SAPP_GLX) + #elif defined(_SAPP_LINUX) && defined(_SAPP_EGL) return _sapp.egl.display; #else return 0; @@ -12291,7 +12611,7 @@ SOKOL_API_IMPL const void* sapp_egl_get_context(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_ANDROID) return _sapp.android.context; - #elif defined(_SAPP_LINUX) && !defined(_SAPP_GLX) + #elif defined(_SAPP_LINUX) && defined(_SAPP_EGL) return _sapp.egl.context; #else return 0; @@ -12470,7 +12790,7 @@ SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) { #else /* not implemented */ #endif - _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + _sapp_strcpy(str, _sapp.clipboard.buffer, (size_t)_sapp.clipboard.buf_size); } SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) { diff --git a/sokol_gfx.h b/sokol_gfx.h index 48b1dc6e..b73ae516 100644 --- a/sokol_gfx.h +++ b/sokol_gfx.h @@ -15852,16 +15852,16 @@ _SOKOL_PRIVATE WGPUOptionalBool _sg_wgpu_optional_bool(bool b) { _SOKOL_PRIVATE WGPUBufferUsage _sg_wgpu_buffer_usage(const sg_buffer_usage* usg) { int res = 0; if (usg->vertex_buffer) { - res |= WGPUBufferUsage_Vertex; + res |= (int)WGPUBufferUsage_Vertex; } if (usg->index_buffer) { - res |= WGPUBufferUsage_Index; + res |= (int)WGPUBufferUsage_Index; } if (usg->storage_buffer) { - res |= WGPUBufferUsage_Storage; + res |= (int)WGPUBufferUsage_Storage; } if (!usg->immutable) { - res |= WGPUBufferUsage_CopyDst; + res |= (int)WGPUBufferUsage_CopyDst; } return (WGPUBufferUsage)res; } @@ -16211,20 +16211,19 @@ _SOKOL_PRIVATE WGPUBlendFactor _sg_wgpu_blendfactor(sg_blend_factor f) { } } -_SOKOL_PRIVATE WGPUColorWriteMask _sg_wgpu_colorwritemask(uint8_t m) { - // FIXME: change to WGPUColorWriteMask once Emscripten and Dawn webgpu.h agree +_SOKOL_PRIVATE WGPUColorWriteMask _sg_wgpu_colorwritemask(sg_color_mask m) { int res = 0; if (0 != (m & SG_COLORMASK_R)) { - res |= WGPUColorWriteMask_Red; + res |= (int)WGPUColorWriteMask_Red; } if (0 != (m & SG_COLORMASK_G)) { - res |= WGPUColorWriteMask_Green; + res |= (int)WGPUColorWriteMask_Green; } if (0 != (m & SG_COLORMASK_B)) { - res |= WGPUColorWriteMask_Blue; + res |= (int)WGPUColorWriteMask_Blue; } if (0 != (m & SG_COLORMASK_A)) { - res |= WGPUColorWriteMask_Alpha; + res |= (int)WGPUColorWriteMask_Alpha; } return (WGPUColorWriteMask)res; } @@ -16488,8 +16487,11 @@ _SOKOL_PRIVATE uint64_t _sg_wgpu_hash(const void* key, int len, uint64_t seed) { } switch(len) { case 3: h2 ^= (uint32_t)(((unsigned char*)data)[2] << 16); + // fall through case 2: h2 ^= (uint32_t)(((unsigned char*)data)[1] << 8); + // fall through case 1: h2 ^= ((unsigned char*)data)[0]; + // fall through h2 *= m; }; h1 ^= h2 >> 18; h1 *= m; @@ -16733,7 +16735,7 @@ _SOKOL_PRIVATE void _sg_wgpu_bindings_cache_clear(void) { } _SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_vb_dirty(size_t index, const _sg_buffer_t* vb, uint64_t offset) { - SOKOL_ASSERT((index >= 0) && (index < SG_MAX_VERTEXBUFFER_BINDSLOTS)); + SOKOL_ASSERT(index < SG_MAX_VERTEXBUFFER_BINDSLOTS); if (vb) { return (_sg.wgpu.bindings_cache.vbs[index].buffer.id != vb->slot.id) || (_sg.wgpu.bindings_cache.vbs[index].offset != offset); @@ -16743,7 +16745,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_vb_dirty(size_t index, const _sg_buf } _SOKOL_PRIVATE void _sg_wgpu_bindings_cache_vb_update(size_t index, const _sg_buffer_t* vb, uint64_t offset) { - SOKOL_ASSERT((index >= 0) && (index < SG_MAX_VERTEXBUFFER_BINDSLOTS)); + SOKOL_ASSERT(index < SG_MAX_VERTEXBUFFER_BINDSLOTS); if (vb) { _sg.wgpu.bindings_cache.vbs[index].buffer.id = vb->slot.id; _sg.wgpu.bindings_cache.vbs[index].offset = offset; @@ -16788,7 +16790,7 @@ _SOKOL_PRIVATE void _sg_wgpu_bindings_cache_bg_update(const _sg_wgpu_bindgroup_t } } -_SOKOL_PRIVATE void _sg_wgpu_set_bindgroup(size_t bg_idx, _sg_wgpu_bindgroup_t* bg) { +_SOKOL_PRIVATE void _sg_wgpu_set_bindgroup(uint32_t bg_idx, _sg_wgpu_bindgroup_t* bg) { if (_sg_wgpu_bindings_cache_bg_dirty(bg)) { _sg_wgpu_bindings_cache_bg_update(bg); _sg_stats_add(wgpu.bindings.num_set_bindgroup, 1); @@ -16939,24 +16941,20 @@ _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { _sg.wgpu.empty_bind_group = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); SOKOL_ASSERT(_sg.wgpu.empty_bind_group); wgpuBindGroupLayoutRelease(empty_bgl); - - // create initial per-frame command encoder - WGPUCommandEncoderDescriptor cmd_enc_desc; - _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); - _sg.wgpu.cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); - SOKOL_ASSERT(_sg.wgpu.cmd_enc); } _SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { SOKOL_ASSERT(_sg.wgpu.valid); - SOKOL_ASSERT(_sg.wgpu.cmd_enc); _sg.wgpu.valid = false; _sg_wgpu_discard_all_bindgroups(); _sg_wgpu_bindgroups_cache_discard(); _sg_wgpu_bindgroups_pool_discard(); _sg_wgpu_uniform_buffer_discard(); wgpuBindGroupRelease(_sg.wgpu.empty_bind_group); _sg.wgpu.empty_bind_group = 0; - wgpuCommandEncoderRelease(_sg.wgpu.cmd_enc); _sg.wgpu.cmd_enc = 0; + // the command encoder is usually released in sg_commit() + if (_sg.wgpu.cmd_enc) { + wgpuCommandEncoderRelease(_sg.wgpu.cmd_enc); _sg.wgpu.cmd_enc = 0; + } wgpuQueueRelease(_sg.wgpu.queue); _sg.wgpu.queue = 0; } @@ -17020,11 +17018,11 @@ _SOKOL_PRIVATE void _sg_wgpu_copy_buffer_data(const _sg_buffer_t* buf, uint64_t const uint64_t extra_src_offset = clamped_size; const uint64_t extra_dst_offset = offset + clamped_size; uint8_t extra_data[4] = { 0 }; - uint8_t* extra_src_ptr = ((uint8_t*)data->ptr) + extra_src_offset; + const uint8_t* extra_src_ptr = ((uint8_t*)data->ptr) + extra_src_offset; for (size_t i = 0; i < extra_size; i++) { extra_data[i] = extra_src_ptr[i]; } - wgpuQueueWriteBuffer(_sg.wgpu.queue, buf->wgpu.buf, extra_dst_offset, extra_src_ptr, 4); + wgpuQueueWriteBuffer(_sg.wgpu.queue, buf->wgpu.buf, extra_dst_offset, extra_data, 4); } } @@ -17308,8 +17306,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const bg_entry->binding = bgl_entry->binding; bg_entry->buffer = _sg.wgpu.uniform.buf; bg_entry->size = _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE; - dynoffset_map[i].sokol_slot = i; - dynoffset_map[i].wgpu_slot = bgl_entry->binding; + dynoffset_map[i].sokol_slot = (uint8_t)i; + dynoffset_map[i].wgpu_slot = (uint8_t)bgl_entry->binding; bgl_index += 1; } bgl_desc.entryCount = bgl_index; @@ -17326,7 +17324,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const // dynamic offsets of the WebGPU setBindGroup call must be in // 'binding order', not 'bindgroup entry order' qsort(dynoffset_map, bgl_index, sizeof(_sg_wgpu_dynoffset_mapping_t), _sg_wgpu_dynoffset_cmp); - shd->wgpu.ub_num_dynoffsets = bgl_index; + shd->wgpu.ub_num_dynoffsets = (uint8_t)bgl_index; for (uint8_t i = 0; i < bgl_index; i++) { const uint8_t sokol_slot = dynoffset_map[i].sokol_slot; shd->wgpu.ub_dynoffsets[sokol_slot] = i; @@ -17710,10 +17708,17 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_render_pass(const sg_pass* pass, const _sg_at _SOKOL_PRIVATE void _sg_wgpu_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) { SOKOL_ASSERT(pass && atts); SOKOL_ASSERT(_sg.wgpu.dev); - SOKOL_ASSERT(_sg.wgpu.cmd_enc); SOKOL_ASSERT(0 == _sg.wgpu.rpass_enc); SOKOL_ASSERT(0 == _sg.wgpu.cpass_enc); + // first pass in the frame? create command encoder + if (0 == _sg.wgpu.cmd_enc) { + WGPUCommandEncoderDescriptor cmd_enc_desc; + _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); + _sg.wgpu.cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); + } + _sg_wgpu_bindings_cache_clear(); if (pass->compute) { _sg_wgpu_begin_compute_pass(pass); @@ -17750,11 +17755,6 @@ _SOKOL_PRIVATE void _sg_wgpu_commit(void) { wgpuQueueSubmit(_sg.wgpu.queue, 1, &wgpu_cmd_buf); wgpuCommandBufferRelease(wgpu_cmd_buf); - - // create a new render-command-encoder for next frame - WGPUCommandEncoderDescriptor cmd_enc_desc; - _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); - _sg.wgpu.cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); } _SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { |