diff --git a/cmd/tools/vshader.v b/cmd/tools/vshader.v index cbd7bfead7..1d6083cd36 100644 --- a/cmd/tools/vshader.v +++ b/cmd/tools/vshader.v @@ -18,17 +18,16 @@ import io.util import flag import net.http -const shdc_full_hash = '6b84ea387a323db9e8e17f5abed2b254a6e409fe' -const tool_version = '0.0.3' +const shdc_full_hash = '0d91b038780614a867f2c8eecd7d935d76bcaae3' +const tool_version = '0.0.4' const tool_description = "Compile shaders in sokol's annotated GLSL format to C headers for use with sokol based apps" const tool_name = os.file_name(os.executable()) const cache_dir = os.join_path(os.cache_dir(), 'v', tool_name) const runtime_os = os.user_os() - const supported_hosts = ['linux', 'macos', 'windows'] const supported_slangs = [ - 'glsl330', // desktop OpenGL backend (SOKOL_GLCORE33) - 'glsl100', // OpenGLES2 and WebGL (SOKOL_GLES3) + 'glsl430', // default desktop OpenGL backend (SOKOL_GLCORE) + 'glsl410', // default macOS desktop OpenGL 'glsl300es', // OpenGLES3 and WebGL2 (SOKOL_GLES3) 'hlsl4', // Direct3D11 with HLSL4 (SOKOL_D3D11) 'hlsl5', // Direct3D11 with HLSL5 (SOKOL_D3D11) @@ -36,10 +35,10 @@ const supported_slangs = [ 'metal_ios', // Metal on iOS devices (SOKOL_METAL) 'metal_sim', // Metal on iOS simulator (SOKOL_METAL) 'wgsl', // WebGPU (SOKOL_WGPU) + 'reflection', ] const default_slangs = [ - 'glsl330', - 'glsl100', + 'glsl410', 'glsl300es', // 'hlsl4', and hlsl5 can't be used at the same time 'hlsl5', diff --git a/thirdparty/sokol/sokol_app.h b/thirdparty/sokol/sokol_app.h index cb3896f520..ade23dbad4 100644 --- a/thirdparty/sokol/sokol_app.h +++ b/thirdparty/sokol/sokol_app.h @@ -25,11 +25,12 @@ the backend selected for sokol_gfx.h if both are used in the same project): - #define SOKOL_GLCORE33 + #define SOKOL_GLCORE #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL #define SOKOL_WGPU + #define SOKOL_NOAPI Optionally provide the following defines with your own implementations: @@ -54,13 +55,13 @@ On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. - On Linux, SOKOL_GLCORE33 can use either GLX or EGL. + On Linux, SOKOL_GLCORE can use either GLX or EGL. GLX is default, set SOKOL_FORCE_EGL to override. For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp Portions of the Windows and Linux GL initialization, event-, icon- etc... code - have been taken from GLFW (http://www.glfw.org/) + have been taken from GLFW (http://www.glfw.org/). iOS onscreen keyboard support 'inspired' by libgdx. @@ -94,7 +95,7 @@ - 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, GL3.2, GLES3, WebGL, WebGL2 + - 3D-APIs: Metal, D3D11, GL4.1, GL4.3, GLES3, WebGL, WebGL2, NOAPI FEATURE/PLATFORM MATRIX ======================= @@ -104,6 +105,7 @@ gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES metal | --- | YES | --- | YES | --- | --- d3d11 | YES | --- | --- | --- | --- | --- + noapi | YES | TODO | TODO | --- | TODO | --- KEY_DOWN | YES | YES | YES | SOME | TODO | YES KEY_UP | YES | YES | YES | SOME | TODO | YES CHAR | YES | YES | YES | YES | TODO | YES @@ -320,10 +322,15 @@ objects and values required for rendering. If sokol_app.h is not compiled with SOKOL_WGPU, these functions return null. - const uint32_t sapp_gl_get_framebuffer(void) + uint32_t sapp_gl_get_framebuffer(void) This returns the 'default framebuffer' of the GL context. Typically this will be zero. + int sapp_gl_get_major_version(void) + int sapp_gl_get_minor_version(void) + Returns the major and minor version of the GL context + (only for SOKOL_GLCORE, all other backends return zero here, including SOKOL_GLES3) + const void* sapp_android_get_native_activity(void); On Android, get the native activity ANativeActivity pointer, otherwise a null pointer. @@ -355,7 +362,7 @@ sapp_consume_event() from inside the event handler (NOTE that this behaviour is currently only implemented for some HTML5 events, support for other platforms and event types will - be added as needed, please open a github ticket and/or provide + be added as needed, please open a GitHub ticket and/or provide a PR if needed). NOTE: Do *not* call any 3D API rendering functions in the event @@ -1554,7 +1561,7 @@ typedef struct sapp_allocator { #define _SAPP_LOG_ITEMS \ _SAPP_LOGITEM_XMACRO(OK, "Ok") \ _SAPP_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ - _SAPP_LOGITEM_XMACRO(MACOS_INVALID_NSOPENGL_PROFILE, "macos: invalid NSOpenGLProfile (valid choices are 1.0, 3.2 and 4.1)") \ + _SAPP_LOGITEM_XMACRO(MACOS_INVALID_NSOPENGL_PROFILE, "macos: invalid NSOpenGLProfile (valid choices are 1.0 and 4.1)") \ _SAPP_LOGITEM_XMACRO(WIN32_LOAD_OPENGL32_DLL_FAILED, "failed loading opengl32.dll") \ _SAPP_LOGITEM_XMACRO(WIN32_CREATE_HELPER_WINDOW_FAILED, "failed to create helper window") \ _SAPP_LOGITEM_XMACRO(WIN32_HELPER_WINDOW_GETDC_FAILED, "failed to get helper window DC") \ @@ -1567,7 +1574,7 @@ typedef struct sapp_allocator { _SAPP_LOGITEM_XMACRO(WIN32_WGL_SET_PIXELFORMAT_FAILED, "failed to set selected pixel format") \ _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED, "ARB_create_context required") \ _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED, "ARB_create_context_profile required") \ - _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_3_2_NOT_SUPPORTED, "OpenGL 3.2 not supported by GL driver (ERROR_INVALID_VERSION_ARB)") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_VERSION_NOT_SUPPORTED, "requested OpenGL version not supported by GL driver (ERROR_INVALID_VERSION_ARB)") \ _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED, "requested OpenGL profile not support by GL driver (ERROR_INVALID_PROFILE_ARB)") \ _SAPP_LOGITEM_XMACRO(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT, "CreateContextAttribsARB failed with ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB") \ _SAPP_LOGITEM_XMACRO(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER, "CreateContextAttribsARB failed for other reason") \ @@ -1708,7 +1715,7 @@ typedef struct sapp_desc { sapp_logger logger; // logging callback override (default: NO LOGGING!) // backend-specific options - int gl_major_version; // override GL major and minor version (the default GL version is 3.2) + int gl_major_version; // override GL major and minor version (the default GL version is 4.1 on macOS, 4.3 elsewhere) int gl_minor_version; bool win32_console_utf8; // if true, set the output console codepage to UTF-8 bool win32_console_create; // if true, attach stdout/stderr to a new console window @@ -1904,6 +1911,10 @@ SOKOL_APP_API_DECL const void* sapp_wgpu_get_depth_stencil_view(void); /* GL: get framebuffer object */ SOKOL_APP_API_DECL uint32_t sapp_gl_get_framebuffer(void); +/* GL: get major version (only valid for desktop GL) */ +SOKOL_APP_API_DECL int sapp_gl_get_major_version(void); +/* GL: get minor version (only valid for desktop GL) */ +SOKOL_APP_API_DECL int sapp_gl_get_minor_version(void); /* Android: get native activity handle */ SOKOL_APP_API_DECL const void* sapp_android_get_native_activity(void); @@ -1941,7 +1952,7 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #endif #include // malloc, free -#include // memset +#include // memset, strncmp #include // size_t #include // roundf @@ -1971,8 +1982,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_GLCORE33) - #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE33") + #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE) + #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE") #endif #else /* iOS or iOS Simulator */ @@ -1990,8 +2001,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_GLCORE33) - #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33") + #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") #endif #elif defined(__ANDROID__) /* Android */ @@ -2005,7 +2016,7 @@ 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_GLCORE33) + #if defined(SOKOL_GLCORE) #if !defined(SOKOL_FORCE_EGL) #define _SAPP_GLX (1) #endif @@ -2015,13 +2026,13 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #include #include #else - #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33, SOKOL_GLES3") + #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE, SOKOL_GLES3") #endif #else #error "sokol_app.h: Unknown platform" #endif -#if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) +#if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) #define _SAPP_ANY_GL (1) #endif @@ -2430,11 +2441,11 @@ MyView2* g_view; #if defined(SOKOL_METAL) @interface _sapp_macos_view : MTKView @end -#elif defined(SOKOL_GLCORE33) +#elif defined(SOKOL_GLCORE) @interface _sapp_macos_view : NSOpenGLView - (void)timerFired:(id)sender; @end -#endif // SOKOL_GLCORE33 +#endif // SOKOL_GLCORE typedef struct { uint32_t flags_changed_store; @@ -2579,7 +2590,7 @@ typedef struct { uint8_t raw_input_data[256]; } _sapp_win32_t; -#if defined(SOKOL_GLCORE33) +#if defined(SOKOL_GLCORE) #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 #define WGL_SUPPORT_OPENGL_ARB 0x2010 #define WGL_DRAW_TO_WINDOW_ARB 0x2001 @@ -2630,7 +2641,7 @@ typedef struct { PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; // special case glGetIntegerv - void (*GetIntegerv)(uint32_t pname, int32_t* data); + void (WINAPI *GetIntegerv)(uint32_t pname, int32_t* data); bool ext_swap_control; bool arb_multisample; bool arb_pixel_format; @@ -2639,7 +2650,7 @@ typedef struct { HWND msg_hwnd; HDC msg_dc; } _sapp_wgl_t; -#endif // SOKOL_GLCORE33 +#endif // SOKOL_GLCORE #endif // _SAPP_WIN32 @@ -2691,6 +2702,7 @@ typedef struct { #if defined(_SAPP_LINUX) #define _SAPP_X11_XDND_VERSION (5) +#define _SAPP_X11_MAX_X11_KEYCODES (256) #define GLX_VENDOR 1 #define GLX_RGBA_BIT 0x00000001 @@ -2784,6 +2796,9 @@ typedef struct { Atom NET_WM_STATE_FULLSCREEN; _sapp_xi_t xi; _sapp_xdnd_t xdnd; + // XLib manual says keycodes are in the range [8, 255] inclusive. + // https://tronche.com/gui/x/xlib/input/keyboard-encoding.html + bool key_repeat[_SAPP_X11_MAX_X11_KEYCODES]; } _sapp_x11_t; #if defined(_SAPP_GLX) @@ -2910,7 +2925,7 @@ typedef struct { _sapp_win32_t win32; #if defined(SOKOL_D3D11) _sapp_d3d11_t d3d11; - #elif defined(SOKOL_GLCORE33) + #elif defined(SOKOL_GLCORE) _sapp_wgl_t wgl; #endif #elif defined(_SAPP_ANDROID) @@ -3141,8 +3156,16 @@ _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) { // (or expressed differently: zero is a valid value for gl_minor_version // and can't be used to indicate 'default') if (0 == res.gl_major_version) { - res.gl_major_version = 3; - res.gl_minor_version = 2; + #if defined(_SAPP_APPLE) + res.gl_major_version = 4; + res.gl_minor_version = 1; + #else + res.gl_major_version = 4; + res.gl_minor_version = 3; + // __v_ start + res.gl_minor_version = 1; // keep backwards compatibility + // __v_ end + #endif } res.html5_canvas_name = _sapp_def(res.html5_canvas_name, "canvas"); res.clipboard_size = _sapp_def(res.clipboard_size, 8192); @@ -3709,7 +3732,7 @@ _SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { const int cur_fb_height = (int)roundf(fb_size.height); const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || (_sapp.framebuffer_height != cur_fb_height); - #elif defined(SOKOL_GLCORE33) + #elif defined(SOKOL_GLCORE) const int cur_fb_width = (int)roundf(bounds.size.width * _sapp.dpi_scale); const int cur_fb_height = (int)roundf(bounds.size.height * _sapp.dpi_scale); const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || @@ -3907,7 +3930,6 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { @implementation _sapp_macos_app_delegate - (void)applicationDidFinishLaunching:(NSNotification*)aNotification { _SOKOL_UNUSED(aNotification); - _sapp_macos_init_cursors(); if ((_sapp.window_width == 0) || (_sapp.window_height == 0)) { // use 4/5 of screen size as default size @@ -3993,7 +4015,7 @@ keyEquivalent:@"v"]; _sapp.macos.window.contentView = _sapp.macos.view; [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; _sapp.macos.view.layer.magnificationFilter = kCAFilterNearest; - #elif defined(SOKOL_GLCORE33) + #elif defined(SOKOL_GLCORE) NSOpenGLPixelFormatAttribute attrs[32]; int i = 0; attrs[i++] = NSOpenGLPFAAccelerated; @@ -4278,7 +4300,7 @@ keyEquivalent:@"v"]; @end @implementation _sapp_macos_view -#if defined(SOKOL_GLCORE33) +#if defined(SOKOL_GLCORE) - (void)timerFired:(id)sender { _SOKOL_UNUSED(sender); [self setNeedsDisplay:YES]; @@ -4378,7 +4400,7 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events(void) { // helper function to make GL context active static void _sapp_gl_make_current(void) { - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) [[_sapp.macos.view openGLContext] makeCurrentContext]; #endif } @@ -5903,16 +5925,28 @@ _SOKOL_PRIVATE void _sapp_emsc_wgpu_request_adapter_cb(WGPURequestAdapterStatus SOKOL_ASSERT(adapter); _sapp.wgpu.adapter = adapter; size_t cur_feature_index = 1; - WGPUFeatureName requiredFeatures[8] = { + #define _SAPP_WGPU_MAX_REQUESTED_FEATURES (8) + WGPUFeatureName requiredFeatures[_SAPP_WGPU_MAX_REQUESTED_FEATURES] = { WGPUFeatureName_Depth32FloatStencil8, }; // check for optional features we're interested in - // FIXME: ASTC texture compression if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionBC)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionBC; - } else if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionETC2)) { + } + if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionETC2)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionETC2; } + if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionASTC)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); + requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionASTC; + } + if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_Float32Filterable)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); + requiredFeatures[cur_feature_index++] = WGPUFeatureName_Float32Filterable; + } + #undef _SAPP_WGPU_MAX_REQUESTED_FEATURES WGPUDeviceDescriptor dev_desc; _sapp_clear(&dev_desc, sizeof(dev_desc)); @@ -6099,7 +6133,7 @@ int main(int argc, char* argv[]) { // ██████ ███████ ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ // // >>gl helpers -#if defined(SOKOL_GLCORE33) +#if defined(SOKOL_GLCORE) typedef struct { int red_bits; int green_bits; @@ -6429,6 +6463,7 @@ _SOKOL_PRIVATE void _sapp_win32_init_keytable(void) { _sapp.keycodes[0x138] = SAPP_KEYCODE_RIGHT_ALT; _sapp.keycodes[0x11D] = SAPP_KEYCODE_RIGHT_CONTROL; _sapp.keycodes[0x036] = SAPP_KEYCODE_RIGHT_SHIFT; + _sapp.keycodes[0x136] = SAPP_KEYCODE_RIGHT_SHIFT; _sapp.keycodes[0x15C] = SAPP_KEYCODE_RIGHT_SUPER; _sapp.keycodes[0x150] = SAPP_KEYCODE_DOWN; _sapp.keycodes[0x14B] = SAPP_KEYCODE_LEFT; @@ -6750,7 +6785,7 @@ _SOKOL_PRIVATE void _sapp_d3d11_present(bool do_not_wait) { #endif /* SOKOL_D3D11 */ -#if defined(SOKOL_GLCORE33) +#if defined(SOKOL_GLCORE) _SOKOL_PRIVATE void _sapp_wgl_init(void) { _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll"); if (!_sapp.wgl.opengl32) { @@ -6767,7 +6802,7 @@ _SOKOL_PRIVATE void _sapp_wgl_init(void) { SOKOL_ASSERT(_sapp.wgl.GetCurrentDC); _sapp.wgl.MakeCurrent = (PFN_wglMakeCurrent)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglMakeCurrent"); SOKOL_ASSERT(_sapp.wgl.MakeCurrent); - _sapp.wgl.GetIntegerv = (void(*)(uint32_t, int32_t*)) GetProcAddress(_sapp.wgl.opengl32, "glGetIntegerv"); + _sapp.wgl.GetIntegerv = (void(WINAPI*)(uint32_t, int32_t*)) GetProcAddress(_sapp.wgl.opengl32, "glGetIntegerv"); SOKOL_ASSERT(_sapp.wgl.GetIntegerv); _sapp.wgl.msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, @@ -7016,7 +7051,7 @@ _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { if (!_sapp.wgl.gl_ctx) { const DWORD err = GetLastError(); if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) { - _SAPP_PANIC(WIN32_WGL_OPENGL_3_2_NOT_SUPPORTED); + _SAPP_PANIC(WIN32_WGL_OPENGL_VERSION_NOT_SUPPORTED); } else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) { _SAPP_PANIC(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED); @@ -7048,7 +7083,7 @@ _SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) { /* FIXME: DwmIsCompositionEnabled? (see GLFW) */ SwapBuffers(_sapp.win32.dc); } -#endif /* SOKOL_GLCORE33 */ +#endif /* SOKOL_GLCORE */ _SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int dst_num_bytes) { SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); @@ -7465,7 +7500,10 @@ _SOKOL_PRIVATE void _sapp_win32_timing_measure(void) { // fallback if swap model isn't "flip-discard" or GetFrameStatistics failed for another reason _sapp_timing_measure(&_sapp.timing); #endif - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) + _sapp_timing_measure(&_sapp.timing); + #endif + #if defined(SOKOL_NOAPI) _sapp_timing_measure(&_sapp.timing); #endif } @@ -7667,7 +7705,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM // present with DXGI_PRESENT_DO_NOT_WAIT _sapp_d3d11_present(true); #endif - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) _sapp_wgl_swap_buffers(); #endif /* NOTE: resizing the swap-chain during resize leads to a substantial @@ -8080,7 +8118,7 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { _sapp_d3d11_create_device_and_swapchain(); _sapp_d3d11_create_default_render_target(); #endif - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) _sapp_wgl_init(); _sapp_wgl_load_extensions(); _sapp_wgl_create_context(); @@ -8108,7 +8146,7 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { Sleep((DWORD)(16 * _sapp.swap_interval)); } #endif - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) _sapp_wgl_swap_buffers(); #endif /* check for window resized, this cannot happen in WM_SIZE as it explodes memory usage */ @@ -9759,6 +9797,354 @@ _SOKOL_PRIVATE void _sapp_x11_init_extensions(void) { } } +// translate the X11 KeySyms for a key to sokol-app key code +// NOTE: this is only used as a fallback, in case the XBK method fails +// it is layout-dependent and will fail partially on most non-US layouts. +// +_SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_keysyms(const KeySym* keysyms, int width) { + if (width > 1) { + switch (keysyms[1]) { + case XK_KP_0: return SAPP_KEYCODE_KP_0; + case XK_KP_1: return SAPP_KEYCODE_KP_1; + case XK_KP_2: return SAPP_KEYCODE_KP_2; + case XK_KP_3: return SAPP_KEYCODE_KP_3; + case XK_KP_4: return SAPP_KEYCODE_KP_4; + case XK_KP_5: return SAPP_KEYCODE_KP_5; + case XK_KP_6: return SAPP_KEYCODE_KP_6; + case XK_KP_7: return SAPP_KEYCODE_KP_7; + case XK_KP_8: return SAPP_KEYCODE_KP_8; + case XK_KP_9: return SAPP_KEYCODE_KP_9; + case XK_KP_Separator: + case XK_KP_Decimal: return SAPP_KEYCODE_KP_DECIMAL; + case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL; + case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER; + default: break; + } + } + + switch (keysyms[0]) { + case XK_Escape: return SAPP_KEYCODE_ESCAPE; + case XK_Tab: return SAPP_KEYCODE_TAB; + case XK_Shift_L: return SAPP_KEYCODE_LEFT_SHIFT; + case XK_Shift_R: return SAPP_KEYCODE_RIGHT_SHIFT; + case XK_Control_L: return SAPP_KEYCODE_LEFT_CONTROL; + case XK_Control_R: return SAPP_KEYCODE_RIGHT_CONTROL; + case XK_Meta_L: + case XK_Alt_L: return SAPP_KEYCODE_LEFT_ALT; + case XK_Mode_switch: // Mapped to Alt_R on many keyboards + case XK_ISO_Level3_Shift: // AltGr on at least some machines + case XK_Meta_R: + case XK_Alt_R: return SAPP_KEYCODE_RIGHT_ALT; + case XK_Super_L: return SAPP_KEYCODE_LEFT_SUPER; + case XK_Super_R: return SAPP_KEYCODE_RIGHT_SUPER; + case XK_Menu: return SAPP_KEYCODE_MENU; + case XK_Num_Lock: return SAPP_KEYCODE_NUM_LOCK; + case XK_Caps_Lock: return SAPP_KEYCODE_CAPS_LOCK; + case XK_Print: return SAPP_KEYCODE_PRINT_SCREEN; + case XK_Scroll_Lock: return SAPP_KEYCODE_SCROLL_LOCK; + case XK_Pause: return SAPP_KEYCODE_PAUSE; + case XK_Delete: return SAPP_KEYCODE_DELETE; + case XK_BackSpace: return SAPP_KEYCODE_BACKSPACE; + case XK_Return: return SAPP_KEYCODE_ENTER; + case XK_Home: return SAPP_KEYCODE_HOME; + case XK_End: return SAPP_KEYCODE_END; + case XK_Page_Up: return SAPP_KEYCODE_PAGE_UP; + case XK_Page_Down: return SAPP_KEYCODE_PAGE_DOWN; + case XK_Insert: return SAPP_KEYCODE_INSERT; + case XK_Left: return SAPP_KEYCODE_LEFT; + case XK_Right: return SAPP_KEYCODE_RIGHT; + case XK_Down: return SAPP_KEYCODE_DOWN; + case XK_Up: return SAPP_KEYCODE_UP; + case XK_F1: return SAPP_KEYCODE_F1; + case XK_F2: return SAPP_KEYCODE_F2; + case XK_F3: return SAPP_KEYCODE_F3; + case XK_F4: return SAPP_KEYCODE_F4; + case XK_F5: return SAPP_KEYCODE_F5; + case XK_F6: return SAPP_KEYCODE_F6; + case XK_F7: return SAPP_KEYCODE_F7; + case XK_F8: return SAPP_KEYCODE_F8; + case XK_F9: return SAPP_KEYCODE_F9; + case XK_F10: return SAPP_KEYCODE_F10; + case XK_F11: return SAPP_KEYCODE_F11; + case XK_F12: return SAPP_KEYCODE_F12; + case XK_F13: return SAPP_KEYCODE_F13; + case XK_F14: return SAPP_KEYCODE_F14; + case XK_F15: return SAPP_KEYCODE_F15; + case XK_F16: return SAPP_KEYCODE_F16; + case XK_F17: return SAPP_KEYCODE_F17; + case XK_F18: return SAPP_KEYCODE_F18; + case XK_F19: return SAPP_KEYCODE_F19; + case XK_F20: return SAPP_KEYCODE_F20; + case XK_F21: return SAPP_KEYCODE_F21; + case XK_F22: return SAPP_KEYCODE_F22; + case XK_F23: return SAPP_KEYCODE_F23; + case XK_F24: return SAPP_KEYCODE_F24; + case XK_F25: return SAPP_KEYCODE_F25; + + // numeric keypad + case XK_KP_Divide: return SAPP_KEYCODE_KP_DIVIDE; + case XK_KP_Multiply: return SAPP_KEYCODE_KP_MULTIPLY; + case XK_KP_Subtract: return SAPP_KEYCODE_KP_SUBTRACT; + case XK_KP_Add: return SAPP_KEYCODE_KP_ADD; + + // these should have been detected in secondary keysym test above! + case XK_KP_Insert: return SAPP_KEYCODE_KP_0; + case XK_KP_End: return SAPP_KEYCODE_KP_1; + case XK_KP_Down: return SAPP_KEYCODE_KP_2; + case XK_KP_Page_Down: return SAPP_KEYCODE_KP_3; + case XK_KP_Left: return SAPP_KEYCODE_KP_4; + case XK_KP_Right: return SAPP_KEYCODE_KP_6; + case XK_KP_Home: return SAPP_KEYCODE_KP_7; + case XK_KP_Up: return SAPP_KEYCODE_KP_8; + case XK_KP_Page_Up: return SAPP_KEYCODE_KP_9; + case XK_KP_Delete: return SAPP_KEYCODE_KP_DECIMAL; + case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL; + case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER; + + // last resort: Check for printable keys (should not happen if the XKB + // extension is available). This will give a layout dependent mapping + // (which is wrong, and we may miss some keys, especially on non-US + // keyboards), but it's better than nothing... + case XK_a: return SAPP_KEYCODE_A; + case XK_b: return SAPP_KEYCODE_B; + case XK_c: return SAPP_KEYCODE_C; + case XK_d: return SAPP_KEYCODE_D; + case XK_e: return SAPP_KEYCODE_E; + case XK_f: return SAPP_KEYCODE_F; + case XK_g: return SAPP_KEYCODE_G; + case XK_h: return SAPP_KEYCODE_H; + case XK_i: return SAPP_KEYCODE_I; + case XK_j: return SAPP_KEYCODE_J; + case XK_k: return SAPP_KEYCODE_K; + case XK_l: return SAPP_KEYCODE_L; + case XK_m: return SAPP_KEYCODE_M; + case XK_n: return SAPP_KEYCODE_N; + case XK_o: return SAPP_KEYCODE_O; + case XK_p: return SAPP_KEYCODE_P; + case XK_q: return SAPP_KEYCODE_Q; + case XK_r: return SAPP_KEYCODE_R; + case XK_s: return SAPP_KEYCODE_S; + case XK_t: return SAPP_KEYCODE_T; + case XK_u: return SAPP_KEYCODE_U; + case XK_v: return SAPP_KEYCODE_V; + case XK_w: return SAPP_KEYCODE_W; + case XK_x: return SAPP_KEYCODE_X; + case XK_y: return SAPP_KEYCODE_Y; + case XK_z: return SAPP_KEYCODE_Z; + case XK_1: return SAPP_KEYCODE_1; + case XK_2: return SAPP_KEYCODE_2; + case XK_3: return SAPP_KEYCODE_3; + case XK_4: return SAPP_KEYCODE_4; + case XK_5: return SAPP_KEYCODE_5; + case XK_6: return SAPP_KEYCODE_6; + case XK_7: return SAPP_KEYCODE_7; + case XK_8: return SAPP_KEYCODE_8; + case XK_9: return SAPP_KEYCODE_9; + case XK_0: return SAPP_KEYCODE_0; + case XK_space: return SAPP_KEYCODE_SPACE; + case XK_minus: return SAPP_KEYCODE_MINUS; + case XK_equal: return SAPP_KEYCODE_EQUAL; + case XK_bracketleft: return SAPP_KEYCODE_LEFT_BRACKET; + case XK_bracketright: return SAPP_KEYCODE_RIGHT_BRACKET; + case XK_backslash: return SAPP_KEYCODE_BACKSLASH; + case XK_semicolon: return SAPP_KEYCODE_SEMICOLON; + case XK_apostrophe: return SAPP_KEYCODE_APOSTROPHE; + case XK_grave: return SAPP_KEYCODE_GRAVE_ACCENT; + case XK_comma: return SAPP_KEYCODE_COMMA; + case XK_period: return SAPP_KEYCODE_PERIOD; + case XK_slash: return SAPP_KEYCODE_SLASH; + case XK_less: return SAPP_KEYCODE_WORLD_1; // At least in some layouts... + default: break; + } + + // no matching translation was found + return SAPP_KEYCODE_INVALID; +} + + +// setup dynamic keycode/scancode mapping tables, this is required +// for getting layout-independent keycodes on X11. +// +// see GLFW x11_init.c/createKeyTables() +_SOKOL_PRIVATE void _sapp_x11_init_keytable(void) { + for (int i = 0; i < SAPP_MAX_KEYCODES; i++) { + _sapp.keycodes[i] = SAPP_KEYCODE_INVALID; + } + // use XKB to determine physical key locations independently of the current keyboard layout + XkbDescPtr desc = XkbGetMap(_sapp.x11.display, 0, XkbUseCoreKbd); + SOKOL_ASSERT(desc); + XkbGetNames(_sapp.x11.display, XkbKeyNamesMask | XkbKeyAliasesMask, desc); + + const int scancode_min = desc->min_key_code; + const int scancode_max = desc->max_key_code; + + const struct { sapp_keycode key; const char* name; } keymap[] = { + { SAPP_KEYCODE_GRAVE_ACCENT, "TLDE" }, + { SAPP_KEYCODE_1, "AE01" }, + { SAPP_KEYCODE_2, "AE02" }, + { SAPP_KEYCODE_3, "AE03" }, + { SAPP_KEYCODE_4, "AE04" }, + { SAPP_KEYCODE_5, "AE05" }, + { SAPP_KEYCODE_6, "AE06" }, + { SAPP_KEYCODE_7, "AE07" }, + { SAPP_KEYCODE_8, "AE08" }, + { SAPP_KEYCODE_9, "AE09" }, + { SAPP_KEYCODE_0, "AE10" }, + { SAPP_KEYCODE_MINUS, "AE11" }, + { SAPP_KEYCODE_EQUAL, "AE12" }, + { SAPP_KEYCODE_Q, "AD01" }, + { SAPP_KEYCODE_W, "AD02" }, + { SAPP_KEYCODE_E, "AD03" }, + { SAPP_KEYCODE_R, "AD04" }, + { SAPP_KEYCODE_T, "AD05" }, + { SAPP_KEYCODE_Y, "AD06" }, + { SAPP_KEYCODE_U, "AD07" }, + { SAPP_KEYCODE_I, "AD08" }, + { SAPP_KEYCODE_O, "AD09" }, + { SAPP_KEYCODE_P, "AD10" }, + { SAPP_KEYCODE_LEFT_BRACKET, "AD11" }, + { SAPP_KEYCODE_RIGHT_BRACKET, "AD12" }, + { SAPP_KEYCODE_A, "AC01" }, + { SAPP_KEYCODE_S, "AC02" }, + { SAPP_KEYCODE_D, "AC03" }, + { SAPP_KEYCODE_F, "AC04" }, + { SAPP_KEYCODE_G, "AC05" }, + { SAPP_KEYCODE_H, "AC06" }, + { SAPP_KEYCODE_J, "AC07" }, + { SAPP_KEYCODE_K, "AC08" }, + { SAPP_KEYCODE_L, "AC09" }, + { SAPP_KEYCODE_SEMICOLON, "AC10" }, + { SAPP_KEYCODE_APOSTROPHE, "AC11" }, + { SAPP_KEYCODE_Z, "AB01" }, + { SAPP_KEYCODE_X, "AB02" }, + { SAPP_KEYCODE_C, "AB03" }, + { SAPP_KEYCODE_V, "AB04" }, + { SAPP_KEYCODE_B, "AB05" }, + { SAPP_KEYCODE_N, "AB06" }, + { SAPP_KEYCODE_M, "AB07" }, + { SAPP_KEYCODE_COMMA, "AB08" }, + { SAPP_KEYCODE_PERIOD, "AB09" }, + { SAPP_KEYCODE_SLASH, "AB10" }, + { SAPP_KEYCODE_BACKSLASH, "BKSL" }, + { SAPP_KEYCODE_WORLD_1, "LSGT" }, + { SAPP_KEYCODE_SPACE, "SPCE" }, + { SAPP_KEYCODE_ESCAPE, "ESC" }, + { SAPP_KEYCODE_ENTER, "RTRN" }, + { SAPP_KEYCODE_TAB, "TAB" }, + { SAPP_KEYCODE_BACKSPACE, "BKSP" }, + { SAPP_KEYCODE_INSERT, "INS" }, + { SAPP_KEYCODE_DELETE, "DELE" }, + { SAPP_KEYCODE_RIGHT, "RGHT" }, + { SAPP_KEYCODE_LEFT, "LEFT" }, + { SAPP_KEYCODE_DOWN, "DOWN" }, + { SAPP_KEYCODE_UP, "UP" }, + { SAPP_KEYCODE_PAGE_UP, "PGUP" }, + { SAPP_KEYCODE_PAGE_DOWN, "PGDN" }, + { SAPP_KEYCODE_HOME, "HOME" }, + { SAPP_KEYCODE_END, "END" }, + { SAPP_KEYCODE_CAPS_LOCK, "CAPS" }, + { SAPP_KEYCODE_SCROLL_LOCK, "SCLK" }, + { SAPP_KEYCODE_NUM_LOCK, "NMLK" }, + { SAPP_KEYCODE_PRINT_SCREEN, "PRSC" }, + { SAPP_KEYCODE_PAUSE, "PAUS" }, + { SAPP_KEYCODE_F1, "FK01" }, + { SAPP_KEYCODE_F2, "FK02" }, + { SAPP_KEYCODE_F3, "FK03" }, + { SAPP_KEYCODE_F4, "FK04" }, + { SAPP_KEYCODE_F5, "FK05" }, + { SAPP_KEYCODE_F6, "FK06" }, + { SAPP_KEYCODE_F7, "FK07" }, + { SAPP_KEYCODE_F8, "FK08" }, + { SAPP_KEYCODE_F9, "FK09" }, + { SAPP_KEYCODE_F10, "FK10" }, + { SAPP_KEYCODE_F11, "FK11" }, + { SAPP_KEYCODE_F12, "FK12" }, + { SAPP_KEYCODE_F13, "FK13" }, + { SAPP_KEYCODE_F14, "FK14" }, + { SAPP_KEYCODE_F15, "FK15" }, + { SAPP_KEYCODE_F16, "FK16" }, + { SAPP_KEYCODE_F17, "FK17" }, + { SAPP_KEYCODE_F18, "FK18" }, + { SAPP_KEYCODE_F19, "FK19" }, + { SAPP_KEYCODE_F20, "FK20" }, + { SAPP_KEYCODE_F21, "FK21" }, + { SAPP_KEYCODE_F22, "FK22" }, + { SAPP_KEYCODE_F23, "FK23" }, + { SAPP_KEYCODE_F24, "FK24" }, + { SAPP_KEYCODE_F25, "FK25" }, + { SAPP_KEYCODE_KP_0, "KP0" }, + { SAPP_KEYCODE_KP_1, "KP1" }, + { SAPP_KEYCODE_KP_2, "KP2" }, + { SAPP_KEYCODE_KP_3, "KP3" }, + { SAPP_KEYCODE_KP_4, "KP4" }, + { SAPP_KEYCODE_KP_5, "KP5" }, + { SAPP_KEYCODE_KP_6, "KP6" }, + { SAPP_KEYCODE_KP_7, "KP7" }, + { SAPP_KEYCODE_KP_8, "KP8" }, + { SAPP_KEYCODE_KP_9, "KP9" }, + { SAPP_KEYCODE_KP_DECIMAL, "KPDL" }, + { SAPP_KEYCODE_KP_DIVIDE, "KPDV" }, + { SAPP_KEYCODE_KP_MULTIPLY, "KPMU" }, + { SAPP_KEYCODE_KP_SUBTRACT, "KPSU" }, + { SAPP_KEYCODE_KP_ADD, "KPAD" }, + { SAPP_KEYCODE_KP_ENTER, "KPEN" }, + { SAPP_KEYCODE_KP_EQUAL, "KPEQ" }, + { SAPP_KEYCODE_LEFT_SHIFT, "LFSH" }, + { SAPP_KEYCODE_LEFT_CONTROL, "LCTL" }, + { SAPP_KEYCODE_LEFT_ALT, "LALT" }, + { SAPP_KEYCODE_LEFT_SUPER, "LWIN" }, + { SAPP_KEYCODE_RIGHT_SHIFT, "RTSH" }, + { SAPP_KEYCODE_RIGHT_CONTROL, "RCTL" }, + { SAPP_KEYCODE_RIGHT_ALT, "RALT" }, + { SAPP_KEYCODE_RIGHT_ALT, "LVL3" }, + { SAPP_KEYCODE_RIGHT_ALT, "MDSW" }, + { SAPP_KEYCODE_RIGHT_SUPER, "RWIN" }, + { SAPP_KEYCODE_MENU, "MENU" } + }; + const int num_keymap_items = (int)(sizeof(keymap) / sizeof(keymap[0])); + + // find X11 keycode to sokol-app key code mapping + for (int scancode = scancode_min; scancode <= scancode_max; scancode++) { + sapp_keycode key = SAPP_KEYCODE_INVALID; + for (int i = 0; i < num_keymap_items; i++) { + if (strncmp(desc->names->keys[scancode].name, keymap[i].name, XkbKeyNameLength) == 0) { + key = keymap[i].key; + break; + } + } + + // fall back to key aliases in case the key name did not match + for (int i = 0; i < desc->names->num_key_aliases; i++) { + if (key != SAPP_KEYCODE_INVALID) { + break; + } + if (strncmp(desc->names->key_aliases[i].real, desc->names->keys[scancode].name, XkbKeyNameLength) != 0) { + continue; + } + for (int j = 0; j < num_keymap_items; j++) { + if (strncmp(desc->names->key_aliases[i].alias, keymap[i].name, XkbKeyNameLength) == 0) { + key = keymap[i].key; + break; + } + } + } + _sapp.keycodes[scancode] = key; + } + XkbFreeNames(desc, XkbKeyNamesMask, True); + XkbFreeKeyboard(desc, 0, True); + + int width = 0; + KeySym* keysyms = XGetKeyboardMapping(_sapp.x11.display, scancode_min, scancode_max - scancode_min + 1, &width); + for (int scancode = scancode_min; scancode <= scancode_max; scancode++) { + // translate untranslated key codes using the traditional X11 KeySym lookups + if (_sapp.keycodes[scancode] == SAPP_KEYCODE_INVALID) { + const size_t base = (size_t)((scancode - scancode_min) * width); + _sapp.keycodes[scancode] = _sapp_x11_translate_keysyms(&keysyms[base], width); + } + } + XFree(keysyms); +} + _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { /* from GLFW: @@ -10558,139 +10944,10 @@ _SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mod } _SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_key(int scancode) { - int dummy; - KeySym* keysyms = XGetKeyboardMapping(_sapp.x11.display, scancode, 1, &dummy); - SOKOL_ASSERT(keysyms); - KeySym keysym = keysyms[0]; - XFree(keysyms); - switch (keysym) { - case XK_Escape: return SAPP_KEYCODE_ESCAPE; - case XK_Tab: return SAPP_KEYCODE_TAB; - case XK_Shift_L: return SAPP_KEYCODE_LEFT_SHIFT; - case XK_Shift_R: return SAPP_KEYCODE_RIGHT_SHIFT; - case XK_Control_L: return SAPP_KEYCODE_LEFT_CONTROL; - case XK_Control_R: return SAPP_KEYCODE_RIGHT_CONTROL; - case XK_Meta_L: - case XK_Alt_L: return SAPP_KEYCODE_LEFT_ALT; - case XK_Mode_switch: /* Mapped to Alt_R on many keyboards */ - case XK_ISO_Level3_Shift: /* AltGr on at least some machines */ - case XK_Meta_R: - case XK_Alt_R: return SAPP_KEYCODE_RIGHT_ALT; - case XK_Super_L: return SAPP_KEYCODE_LEFT_SUPER; - case XK_Super_R: return SAPP_KEYCODE_RIGHT_SUPER; - case XK_Menu: return SAPP_KEYCODE_MENU; - case XK_Num_Lock: return SAPP_KEYCODE_NUM_LOCK; - case XK_Caps_Lock: return SAPP_KEYCODE_CAPS_LOCK; - case XK_Print: return SAPP_KEYCODE_PRINT_SCREEN; - case XK_Scroll_Lock: return SAPP_KEYCODE_SCROLL_LOCK; - case XK_Pause: return SAPP_KEYCODE_PAUSE; - case XK_Delete: return SAPP_KEYCODE_DELETE; - case XK_BackSpace: return SAPP_KEYCODE_BACKSPACE; - case XK_Return: return SAPP_KEYCODE_ENTER; - case XK_Home: return SAPP_KEYCODE_HOME; - case XK_End: return SAPP_KEYCODE_END; - case XK_Page_Up: return SAPP_KEYCODE_PAGE_UP; - case XK_Page_Down: return SAPP_KEYCODE_PAGE_DOWN; - case XK_Insert: return SAPP_KEYCODE_INSERT; - case XK_Left: return SAPP_KEYCODE_LEFT; - case XK_Right: return SAPP_KEYCODE_RIGHT; - case XK_Down: return SAPP_KEYCODE_DOWN; - case XK_Up: return SAPP_KEYCODE_UP; - case XK_F1: return SAPP_KEYCODE_F1; - case XK_F2: return SAPP_KEYCODE_F2; - case XK_F3: return SAPP_KEYCODE_F3; - case XK_F4: return SAPP_KEYCODE_F4; - case XK_F5: return SAPP_KEYCODE_F5; - case XK_F6: return SAPP_KEYCODE_F6; - case XK_F7: return SAPP_KEYCODE_F7; - case XK_F8: return SAPP_KEYCODE_F8; - case XK_F9: return SAPP_KEYCODE_F9; - case XK_F10: return SAPP_KEYCODE_F10; - case XK_F11: return SAPP_KEYCODE_F11; - case XK_F12: return SAPP_KEYCODE_F12; - case XK_F13: return SAPP_KEYCODE_F13; - case XK_F14: return SAPP_KEYCODE_F14; - case XK_F15: return SAPP_KEYCODE_F15; - case XK_F16: return SAPP_KEYCODE_F16; - case XK_F17: return SAPP_KEYCODE_F17; - case XK_F18: return SAPP_KEYCODE_F18; - case XK_F19: return SAPP_KEYCODE_F19; - case XK_F20: return SAPP_KEYCODE_F20; - case XK_F21: return SAPP_KEYCODE_F21; - case XK_F22: return SAPP_KEYCODE_F22; - case XK_F23: return SAPP_KEYCODE_F23; - case XK_F24: return SAPP_KEYCODE_F24; - case XK_F25: return SAPP_KEYCODE_F25; - - case XK_KP_Divide: return SAPP_KEYCODE_KP_DIVIDE; - case XK_KP_Multiply: return SAPP_KEYCODE_KP_MULTIPLY; - case XK_KP_Subtract: return SAPP_KEYCODE_KP_SUBTRACT; - case XK_KP_Add: return SAPP_KEYCODE_KP_ADD; - - case XK_KP_Insert: return SAPP_KEYCODE_KP_0; - case XK_KP_End: return SAPP_KEYCODE_KP_1; - case XK_KP_Down: return SAPP_KEYCODE_KP_2; - case XK_KP_Page_Down: return SAPP_KEYCODE_KP_3; - case XK_KP_Left: return SAPP_KEYCODE_KP_4; - case XK_KP_Begin: return SAPP_KEYCODE_KP_5; - case XK_KP_Right: return SAPP_KEYCODE_KP_6; - case XK_KP_Home: return SAPP_KEYCODE_KP_7; - case XK_KP_Up: return SAPP_KEYCODE_KP_8; - case XK_KP_Page_Up: return SAPP_KEYCODE_KP_9; - case XK_KP_Delete: return SAPP_KEYCODE_KP_DECIMAL; - case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL; - case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER; - - case XK_a: return SAPP_KEYCODE_A; - case XK_b: return SAPP_KEYCODE_B; - case XK_c: return SAPP_KEYCODE_C; - case XK_d: return SAPP_KEYCODE_D; - case XK_e: return SAPP_KEYCODE_E; - case XK_f: return SAPP_KEYCODE_F; - case XK_g: return SAPP_KEYCODE_G; - case XK_h: return SAPP_KEYCODE_H; - case XK_i: return SAPP_KEYCODE_I; - case XK_j: return SAPP_KEYCODE_J; - case XK_k: return SAPP_KEYCODE_K; - case XK_l: return SAPP_KEYCODE_L; - case XK_m: return SAPP_KEYCODE_M; - case XK_n: return SAPP_KEYCODE_N; - case XK_o: return SAPP_KEYCODE_O; - case XK_p: return SAPP_KEYCODE_P; - case XK_q: return SAPP_KEYCODE_Q; - case XK_r: return SAPP_KEYCODE_R; - case XK_s: return SAPP_KEYCODE_S; - case XK_t: return SAPP_KEYCODE_T; - case XK_u: return SAPP_KEYCODE_U; - case XK_v: return SAPP_KEYCODE_V; - case XK_w: return SAPP_KEYCODE_W; - case XK_x: return SAPP_KEYCODE_X; - case XK_y: return SAPP_KEYCODE_Y; - case XK_z: return SAPP_KEYCODE_Z; - case XK_1: return SAPP_KEYCODE_1; - case XK_2: return SAPP_KEYCODE_2; - case XK_3: return SAPP_KEYCODE_3; - case XK_4: return SAPP_KEYCODE_4; - case XK_5: return SAPP_KEYCODE_5; - case XK_6: return SAPP_KEYCODE_6; - case XK_7: return SAPP_KEYCODE_7; - case XK_8: return SAPP_KEYCODE_8; - case XK_9: return SAPP_KEYCODE_9; - case XK_0: return SAPP_KEYCODE_0; - case XK_space: return SAPP_KEYCODE_SPACE; - case XK_minus: return SAPP_KEYCODE_MINUS; - case XK_equal: return SAPP_KEYCODE_EQUAL; - case XK_bracketleft: return SAPP_KEYCODE_LEFT_BRACKET; - case XK_bracketright: return SAPP_KEYCODE_RIGHT_BRACKET; - case XK_backslash: return SAPP_KEYCODE_BACKSLASH; - case XK_semicolon: return SAPP_KEYCODE_SEMICOLON; - case XK_apostrophe: return SAPP_KEYCODE_APOSTROPHE; - case XK_grave: return SAPP_KEYCODE_GRAVE_ACCENT; - case XK_comma: return SAPP_KEYCODE_COMMA; - case XK_period: return SAPP_KEYCODE_PERIOD; - case XK_slash: return SAPP_KEYCODE_SLASH; - case XK_less: return SAPP_KEYCODE_WORLD_1; /* At least in some layouts... */ - default: return SAPP_KEYCODE_INVALID; + if ((scancode >= 0) && (scancode < _SAPP_X11_MAX_X11_KEYCODES)) { + return _sapp.keycodes[scancode]; + } else { + return SAPP_KEYCODE_INVALID; } } @@ -10729,6 +10986,21 @@ _SOKOL_PRIVATE int32_t _sapp_x11_keysym_to_unicode(KeySym keysym) { return -1; } +_SOKOL_PRIVATE bool _sapp_x11_keypress_repeat(int keycode) { + bool repeat = false; + if ((keycode >= 0) && (keycode < _SAPP_X11_MAX_X11_KEYCODES)) { + repeat = _sapp.x11.key_repeat[keycode]; + _sapp.x11.key_repeat[keycode] = true; + } + return repeat; +} + +_SOKOL_PRIVATE void _sapp_x11_keyrelease_repeat(int keycode) { + if ((keycode >= 0) && (keycode < _SAPP_X11_MAX_X11_KEYCODES)) { + _sapp.x11.key_repeat[keycode] = false; + } +} + _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { SOKOL_ASSERT(src); SOKOL_ASSERT(_sapp.drop.buffer); @@ -10807,293 +11079,331 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { } } -// XLib manual says keycodes are in the range [8, 255] inclusive. -// https://tronche.com/gui/x/xlib/input/keyboard-encoding.html -static bool _sapp_x11_keycodes[256]; +_SOKOL_PRIVATE void _sapp_x11_on_genericevent(XEvent* event) { + if (_sapp.mouse.locked && _sapp.x11.xi.available) { + if (event->xcookie.extension == _sapp.x11.xi.major_opcode) { + if (XGetEventData(_sapp.x11.display, &event->xcookie)) { + if (event->xcookie.evtype == XI_RawMotion) { + XIRawEvent* re = (XIRawEvent*) event->xcookie.data; + if (re->valuators.mask_len) { + const double* values = re->raw_values; + if (XIMaskIsSet(re->valuators.mask, 0)) { + _sapp.mouse.dx = (float) *values; + values++; + } + if (XIMaskIsSet(re->valuators.mask, 1)) { + _sapp.mouse.dy = (float) *values; + } + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); + } + } + XFreeEventData(_sapp.x11.display, &event->xcookie); + } + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_focusin(XEvent* event) { + // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW + if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { + _sapp_x11_app_event(SAPP_EVENTTYPE_FOCUSED); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_focusout(XEvent* event) { + // if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock + if (_sapp.mouse.locked) { + _sapp_x11_lock_mouse(false); + } + // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW + if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { + _sapp_x11_app_event(SAPP_EVENTTYPE_UNFOCUSED); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_keypress(XEvent* event) { + int keycode = (int)event->xkey.keycode; + + const sapp_keycode key = _sapp_x11_translate_key(keycode); + const bool repeat = _sapp_x11_keypress_repeat(keycode); + uint32_t mods = _sapp_x11_mods(event->xkey.state); + // X11 doesn't set modifier bit on key down, so emulate that + mods |= _sapp_x11_key_modifier_bit(key); + if (key != SAPP_KEYCODE_INVALID) { + _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_DOWN, key, repeat, mods); + } + KeySym keysym; + XLookupString(&event->xkey, NULL, 0, &keysym, NULL); + int32_t chr = _sapp_x11_keysym_to_unicode(keysym); + if (chr > 0) { + _sapp_x11_char_event((uint32_t)chr, repeat, mods); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_keyrelease(XEvent* event) { + int keycode = (int)event->xkey.keycode; + const sapp_keycode key = _sapp_x11_translate_key(keycode); + _sapp_x11_keyrelease_repeat(keycode); + if (key != SAPP_KEYCODE_INVALID) { + uint32_t mods = _sapp_x11_mods(event->xkey.state); + // X11 doesn't clear modifier bit on key up, so emulate that + mods &= ~_sapp_x11_key_modifier_bit(key); + _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_UP, key, false, mods); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_buttonpress(XEvent* event) { + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); + const sapp_mousebutton btn = _sapp_x11_translate_button(event); + uint32_t mods = _sapp_x11_mods(event->xbutton.state); + // X11 doesn't set modifier bit on button down, so emulate that + mods |= _sapp_x11_button_modifier_bit(btn); + if (btn != SAPP_MOUSEBUTTON_INVALID) { + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods); + _sapp.x11.mouse_buttons |= (1 << btn); + } + else { + // might be a scroll event + switch (event->xbutton.button) { + case 4: _sapp_x11_scroll_event(0.0f, 1.0f, mods); break; + case 5: _sapp_x11_scroll_event(0.0f, -1.0f, mods); break; + case 6: _sapp_x11_scroll_event(1.0f, 0.0f, mods); break; + case 7: _sapp_x11_scroll_event(-1.0f, 0.0f, mods); break; + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_buttonrelease(XEvent* event) { + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); + const sapp_mousebutton btn = _sapp_x11_translate_button(event); + if (btn != SAPP_MOUSEBUTTON_INVALID) { + uint32_t mods = _sapp_x11_mods(event->xbutton.state); + // X11 doesn't clear modifier bit on button up, so emulate that + mods &= ~_sapp_x11_button_modifier_bit(btn); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, btn, mods); + _sapp.x11.mouse_buttons &= ~(1 << btn); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_enternotify(XEvent* event) { + // don't send enter/leave events while mouse button held down + if (0 == _sapp.x11.mouse_buttons) { + _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_leavenotify(XEvent* event) { + if (0 == _sapp.x11.mouse_buttons) { + _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_motionnotify(XEvent* event) { + if (!_sapp.mouse.locked) { + _sapp_x11_mouse_update(event->xmotion.x, event->xmotion.y, false); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); + } +} + +_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) { + const int state = _sapp_x11_get_window_state(); + if (state != _sapp.x11.window_state) { + _sapp.x11.window_state = state; + if (state == IconicState) { + _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED); + } + else if (state == NormalState) { + _sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED); + } + } + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_selectionnotify(XEvent* event) { + if (event->xselection.property == _sapp.x11.xdnd.XdndSelection) { + char* data = 0; + uint32_t result = _sapp_x11_get_window_property(event->xselection.requestor, + event->xselection.property, + event->xselection.target, + (unsigned char**) &data); + if (_sapp.drop.enabled && result) { + if (_sapp_x11_parse_dropped_files_list(data)) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + if (_sapp_events_enabled()) { + // FIXME: Figure out how to get modifier key state here. + // The XSelection event has no 'state' item, and + // XQueryKeymap() always returns a zeroed array. + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp_call_event(&_sapp.event); + } + } + } + if (_sapp.x11.xdnd.version >= 2) { + XEvent reply; + _sapp_clear(&reply, sizeof(reply)); + reply.type = ClientMessage; + reply.xclient.window = _sapp.x11.xdnd.source; + reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)_sapp.x11.window; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = (long)_sapp.x11.xdnd.XdndActionCopy; + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_on_clientmessage(XEvent* event) { + if (XFilterEvent(event, None)) { + return; + } + if (event->xclient.message_type == _sapp.x11.WM_PROTOCOLS) { + const Atom protocol = (Atom)event->xclient.data.l[0]; + if (protocol == _sapp.x11.WM_DELETE_WINDOW) { + _sapp.quit_requested = true; + } + } else if (event->xclient.message_type == _sapp.x11.xdnd.XdndEnter) { + const bool is_list = 0 != (event->xclient.data.l[1] & 1); + _sapp.x11.xdnd.source = (Window)event->xclient.data.l[0]; + _sapp.x11.xdnd.version = event->xclient.data.l[1] >> 24; + _sapp.x11.xdnd.format = None; + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + uint32_t count = 0; + Atom* formats = 0; + if (is_list) { + count = _sapp_x11_get_window_property(_sapp.x11.xdnd.source, _sapp.x11.xdnd.XdndTypeList, XA_ATOM, (unsigned char**)&formats); + } else { + count = 3; + formats = (Atom*) event->xclient.data.l + 2; + } + for (uint32_t i = 0; i < count; i++) { + if (formats[i] == _sapp.x11.xdnd.text_uri_list) { + _sapp.x11.xdnd.format = _sapp.x11.xdnd.text_uri_list; + break; + } + } + if (is_list && formats) { + XFree(formats); + } + } else if (event->xclient.message_type == _sapp.x11.xdnd.XdndDrop) { + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + Time time = CurrentTime; + if (_sapp.x11.xdnd.format) { + if (_sapp.x11.xdnd.version >= 1) { + time = (Time)event->xclient.data.l[2]; + } + XConvertSelection(_sapp.x11.display, + _sapp.x11.xdnd.XdndSelection, + _sapp.x11.xdnd.format, + _sapp.x11.xdnd.XdndSelection, + _sapp.x11.window, + time); + } else if (_sapp.x11.xdnd.version >= 2) { + XEvent reply; + _sapp_clear(&reply, sizeof(reply)); + reply.type = ClientMessage; + reply.xclient.window = _sapp.x11.xdnd.source; + reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)_sapp.x11.window; + reply.xclient.data.l[1] = 0; // drag was rejected + reply.xclient.data.l[2] = None; + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } + } else if (event->xclient.message_type == _sapp.x11.xdnd.XdndPosition) { + // drag operation has moved over the window + // FIXME: we could track the mouse position here, but + // this isn't implemented on other platforms either so far + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + XEvent reply; + _sapp_clear(&reply, sizeof(reply)); + reply.type = ClientMessage; + reply.xclient.window = _sapp.x11.xdnd.source; + reply.xclient.message_type = _sapp.x11.xdnd.XdndStatus; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)_sapp.x11.window; + if (_sapp.x11.xdnd.format) { + /* reply that we are ready to copy the dragged data */ + reply.xclient.data.l[1] = 1; // accept with no rectangle + if (_sapp.x11.xdnd.version >= 2) { + reply.xclient.data.l[4] = (long)_sapp.x11.xdnd.XdndActionCopy; + } + } + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } +} _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { - Bool filtered = XFilterEvent(event, None); switch (event->type) { case GenericEvent: - if (_sapp.mouse.locked && _sapp.x11.xi.available) { - if (event->xcookie.extension == _sapp.x11.xi.major_opcode) { - if (XGetEventData(_sapp.x11.display, &event->xcookie)) { - if (event->xcookie.evtype == XI_RawMotion) { - XIRawEvent* re = (XIRawEvent*) event->xcookie.data; - if (re->valuators.mask_len) { - const double* values = re->raw_values; - if (XIMaskIsSet(re->valuators.mask, 0)) { - _sapp.mouse.dx = (float) *values; - values++; - } - if (XIMaskIsSet(re->valuators.mask, 1)) { - _sapp.mouse.dy = (float) *values; - } - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); - } - } - XFreeEventData(_sapp.x11.display, &event->xcookie); - } - } - } + _sapp_x11_on_genericevent(event); break; case FocusIn: - // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW - if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { - _sapp_x11_app_event(SAPP_EVENTTYPE_FOCUSED); - } + _sapp_x11_on_focusin(event); break; case FocusOut: - /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ - if (_sapp.mouse.locked) { - _sapp_x11_lock_mouse(false); - } - // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW - if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { - _sapp_x11_app_event(SAPP_EVENTTYPE_UNFOCUSED); - } + _sapp_x11_on_focusout(event); break; case KeyPress: - { - int keycode = (int)event->xkey.keycode; - const sapp_keycode key = _sapp_x11_translate_key(keycode); - bool repeat = _sapp_x11_keycodes[keycode & 0xFF]; - _sapp_x11_keycodes[keycode & 0xFF] = true; - uint32_t mods = _sapp_x11_mods(event->xkey.state); - // X11 doesn't set modifier bit on key down, so emulate that - mods |= _sapp_x11_key_modifier_bit(key); - if (key != SAPP_KEYCODE_INVALID) { - _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_DOWN, key, repeat, mods); - } - KeySym keysym; - XLookupString(&event->xkey, NULL, 0, &keysym, NULL); - int32_t chr = _sapp_x11_keysym_to_unicode(keysym); - if (chr > 0) { - _sapp_x11_char_event((uint32_t)chr, repeat, mods); - } - } + _sapp_x11_on_keypress(event); break; case KeyRelease: - { - int keycode = (int)event->xkey.keycode; - const sapp_keycode key = _sapp_x11_translate_key(keycode); - _sapp_x11_keycodes[keycode & 0xFF] = false; - if (key != SAPP_KEYCODE_INVALID) { - uint32_t mods = _sapp_x11_mods(event->xkey.state); - // X11 doesn't clear modifier bit on key up, so emulate that - mods &= ~_sapp_x11_key_modifier_bit(key); - _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_UP, key, false, mods); - } - } + _sapp_x11_on_keyrelease(event); break; case ButtonPress: - { - _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); - const sapp_mousebutton btn = _sapp_x11_translate_button(event); - uint32_t mods = _sapp_x11_mods(event->xbutton.state); - // X11 doesn't set modifier bit on button down, so emulate that - mods |= _sapp_x11_button_modifier_bit(btn); - if (btn != SAPP_MOUSEBUTTON_INVALID) { - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods); - _sapp.x11.mouse_buttons |= (1 << btn); - } - else { - /* might be a scroll event */ - switch (event->xbutton.button) { - case 4: _sapp_x11_scroll_event(0.0f, 1.0f, mods); break; - case 5: _sapp_x11_scroll_event(0.0f, -1.0f, mods); break; - case 6: _sapp_x11_scroll_event(1.0f, 0.0f, mods); break; - case 7: _sapp_x11_scroll_event(-1.0f, 0.0f, mods); break; - } - } - } + _sapp_x11_on_buttonpress(event); break; case ButtonRelease: - { - _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); - const sapp_mousebutton btn = _sapp_x11_translate_button(event); - if (btn != SAPP_MOUSEBUTTON_INVALID) { - uint32_t mods = _sapp_x11_mods(event->xbutton.state); - // X11 doesn't clear modifier bit on button up, so emulate that - mods &= ~_sapp_x11_button_modifier_bit(btn); - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, btn, mods); - _sapp.x11.mouse_buttons &= ~(1 << btn); - } - } + _sapp_x11_on_buttonrelease(event); break; case EnterNotify: - /* don't send enter/leave events while mouse button held down */ - if (0 == _sapp.x11.mouse_buttons) { - _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true); - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); - } + _sapp_x11_on_enternotify(event); break; case LeaveNotify: - if (0 == _sapp.x11.mouse_buttons) { - _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true); - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); - } + _sapp_x11_on_leavenotify(event); break; case MotionNotify: - if (!_sapp.mouse.locked) { - _sapp_x11_mouse_update(event->xmotion.x, event->xmotion.y, false); - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); - } + _sapp_x11_on_motionnotify(event); break; case ConfigureNotify: - 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); - } + _sapp_x11_on_configurenotify(event); break; case PropertyNotify: - if (event->xproperty.state == PropertyNewValue) { - if (event->xproperty.atom == _sapp.x11.WM_STATE) { - const int state = _sapp_x11_get_window_state(); - if (state != _sapp.x11.window_state) { - _sapp.x11.window_state = state; - if (state == IconicState) { - _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED); - } - else if (state == NormalState) { - _sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED); - } - } - } - } - break; - case ClientMessage: - if (filtered) { - return; - } - if (event->xclient.message_type == _sapp.x11.WM_PROTOCOLS) { - const Atom protocol = (Atom)event->xclient.data.l[0]; - if (protocol == _sapp.x11.WM_DELETE_WINDOW) { - _sapp.quit_requested = true; - } - } - else if (event->xclient.message_type == _sapp.x11.xdnd.XdndEnter) { - const bool is_list = 0 != (event->xclient.data.l[1] & 1); - _sapp.x11.xdnd.source = (Window)event->xclient.data.l[0]; - _sapp.x11.xdnd.version = event->xclient.data.l[1] >> 24; - _sapp.x11.xdnd.format = None; - if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { - return; - } - uint32_t count = 0; - Atom* formats = 0; - if (is_list) { - count = _sapp_x11_get_window_property(_sapp.x11.xdnd.source, _sapp.x11.xdnd.XdndTypeList, XA_ATOM, (unsigned char**)&formats); - } - else { - count = 3; - formats = (Atom*) event->xclient.data.l + 2; - } - for (uint32_t i = 0; i < count; i++) { - if (formats[i] == _sapp.x11.xdnd.text_uri_list) { - _sapp.x11.xdnd.format = _sapp.x11.xdnd.text_uri_list; - break; - } - } - if (is_list && formats) { - XFree(formats); - } - } - else if (event->xclient.message_type == _sapp.x11.xdnd.XdndDrop) { - if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { - return; - } - Time time = CurrentTime; - if (_sapp.x11.xdnd.format) { - if (_sapp.x11.xdnd.version >= 1) { - time = (Time)event->xclient.data.l[2]; - } - XConvertSelection(_sapp.x11.display, - _sapp.x11.xdnd.XdndSelection, - _sapp.x11.xdnd.format, - _sapp.x11.xdnd.XdndSelection, - _sapp.x11.window, - time); - } - else if (_sapp.x11.xdnd.version >= 2) { - XEvent reply; - _sapp_clear(&reply, sizeof(reply)); - reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.xdnd.source; - reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; - reply.xclient.format = 32; - reply.xclient.data.l[0] = (long)_sapp.x11.window; - reply.xclient.data.l[1] = 0; // drag was rejected - reply.xclient.data.l[2] = None; - XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); - XFlush(_sapp.x11.display); - } - } - else if (event->xclient.message_type == _sapp.x11.xdnd.XdndPosition) { - /* drag operation has moved over the window - FIXME: we could track the mouse position here, but - this isn't implemented on other platforms either so far - */ - if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { - return; - } - XEvent reply; - _sapp_clear(&reply, sizeof(reply)); - reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.xdnd.source; - reply.xclient.message_type = _sapp.x11.xdnd.XdndStatus; - reply.xclient.format = 32; - reply.xclient.data.l[0] = (long)_sapp.x11.window; - if (_sapp.x11.xdnd.format) { - /* reply that we are ready to copy the dragged data */ - reply.xclient.data.l[1] = 1; // accept with no rectangle - if (_sapp.x11.xdnd.version >= 2) { - reply.xclient.data.l[4] = (long)_sapp.x11.xdnd.XdndActionCopy; - } - } - XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); - XFlush(_sapp.x11.display); - } + _sapp_x11_on_propertynotify(event); break; case SelectionNotify: - if (event->xselection.property == _sapp.x11.xdnd.XdndSelection) { - char* data = 0; - uint32_t result = _sapp_x11_get_window_property(event->xselection.requestor, - event->xselection.property, - event->xselection.target, - (unsigned char**) &data); - if (_sapp.drop.enabled && result) { - if (_sapp_x11_parse_dropped_files_list(data)) { - _sapp.mouse.dx = 0.0f; - _sapp.mouse.dy = 0.0f; - if (_sapp_events_enabled()) { - // FIXME: Figure out how to get modifier key state here. - // The XSelection event has no 'state' item, and - // XQueryKeymap() always returns a zeroed array. - _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); - _sapp_call_event(&_sapp.event); - } - } - } - if (_sapp.x11.xdnd.version >= 2) { - XEvent reply; - _sapp_clear(&reply, sizeof(reply)); - reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.xdnd.source; - reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; - reply.xclient.format = 32; - reply.xclient.data.l[0] = (long)_sapp.x11.window; - reply.xclient.data.l[1] = result; - reply.xclient.data.l[2] = (long)_sapp.x11.xdnd.XdndActionCopy; - XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); - XFlush(_sapp.x11.display); - } - } + _sapp_x11_on_selectionnotify(event); break; case DestroyNotify: + // not a bug + break; + case ClientMessage: + _sapp_x11_on_clientmessage(event); break; } } @@ -11101,7 +11411,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { #if !defined(_SAPP_GLX) _SOKOL_PRIVATE void _sapp_egl_init(void) { -#if defined(SOKOL_GLCORE33) +#if defined(SOKOL_GLCORE) if (!eglBindAPI(EGL_OPENGL_API)) { _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_API_FAILED); } @@ -11125,7 +11435,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; const EGLint config_attrs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, #elif defined(SOKOL_GLES3) EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, @@ -11188,7 +11498,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { } EGLint ctx_attrs[] = { - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) EGL_CONTEXT_MAJOR_VERSION, _sapp.desc.gl_major_version, EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl_minor_version, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, @@ -11252,11 +11562,12 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { } _sapp.x11.screen = DefaultScreen(_sapp.x11.display); _sapp.x11.root = DefaultRootWindow(_sapp.x11.display); - XkbSetDetectableAutoRepeat(_sapp.x11.display, true, NULL); _sapp_x11_query_system_dpi(); _sapp.dpi_scale = _sapp.x11.dpi / 96.0f; _sapp_x11_init_extensions(); _sapp_x11_create_cursors(); + XkbSetDetectableAutoRepeat(_sapp.x11.display, true, NULL); + _sapp_x11_init_keytable(); #if defined(_SAPP_GLX) _sapp_glx_init(); Visual* visual = 0; @@ -11907,6 +12218,24 @@ SOKOL_API_IMPL uint32_t sapp_gl_get_framebuffer(void) { #endif } +SOKOL_API_IMPL int sapp_gl_get_major_version(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_GLCORE) + return _sapp.desc.gl_major_version; + #else + return 0; + #endif +} + +SOKOL_API_IMPL int sapp_gl_get_minor_version(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_GLCORE) + return _sapp.desc.gl_minor_version; + #else + return 0; + #endif +} + SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) { // NOTE: _sapp.valid is not asserted here because sapp_android_get_native_activity() // needs to be callable from within sokol_main() (see: https://github.com/floooh/sokol/issues/708) diff --git a/thirdparty/sokol/sokol_audio.h b/thirdparty/sokol/sokol_audio.h index 1c246d3294..ed90fb60f3 100644 --- a/thirdparty/sokol/sokol_audio.h +++ b/thirdparty/sokol/sokol_audio.h @@ -39,6 +39,7 @@ - on macOS: AudioToolbox - on iOS: AudioToolbox, AVFoundation + - on FreeBSD: asound - on Linux: asound - on Android: link with OpenSLES or aaudio - on Windows with MSVC or Clang toolchain: no action needed, libs are defined in-source via pragma-comment-lib @@ -51,6 +52,7 @@ - Windows: WASAPI - Linux: ALSA + - FreeBSD: ALSA - macOS: CoreAudio - iOS: CoreAudio+AVAudioSession - emscripten: WebAudio with ScriptProcessorNode @@ -780,13 +782,9 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #include "aaudio/AAudio.h" #endif #elif defined(_SAUDIO_LINUX) -// __v_ start -#if !defined(__FreeBSD__) -// __v_ end - #include -// __v_ start -#endif -// __v_ end + #if !defined(__FreeBSD__) + #include + #endif #define _SAUDIO_PTHREADS (1) #include #define ALSA_PCM_NEW_HW_PARAMS_API @@ -1069,6 +1067,7 @@ typedef struct { /* sokol-audio state */ typedef struct { bool valid; + bool setup_called; void (*stream_cb)(float* buffer, int num_frames, int num_channels); void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); void* user_data; @@ -2488,9 +2487,11 @@ void _saudio_backend_shutdown(void) { // >>public SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { SOKOL_ASSERT(!_saudio.valid); + SOKOL_ASSERT(!_saudio.setup_called); SOKOL_ASSERT(desc); SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); _saudio_clear(&_saudio, sizeof(_saudio)); + _saudio.setup_called = true; _saudio.desc = *desc; _saudio.stream_cb = desc->stream_cb; _saudio.stream_userdata_cb = desc->stream_userdata_cb; @@ -2521,6 +2522,8 @@ SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { } SOKOL_API_IMPL void saudio_shutdown(void) { + SOKOL_ASSERT(_saudio.setup_called); + _saudio.setup_called = false; if (_saudio.valid) { _saudio_backend_shutdown(); _saudio_fifo_shutdown(&_saudio.fifo); @@ -2534,26 +2537,32 @@ SOKOL_API_IMPL bool saudio_isvalid(void) { } SOKOL_API_IMPL void* saudio_userdata(void) { + SOKOL_ASSERT(_saudio.setup_called); return _saudio.desc.user_data; } SOKOL_API_IMPL saudio_desc saudio_query_desc(void) { + SOKOL_ASSERT(_saudio.setup_called); return _saudio.desc; } SOKOL_API_IMPL int saudio_sample_rate(void) { + SOKOL_ASSERT(_saudio.setup_called); return _saudio.sample_rate; } SOKOL_API_IMPL int saudio_buffer_frames(void) { + SOKOL_ASSERT(_saudio.setup_called); return _saudio.buffer_frames; } SOKOL_API_IMPL int saudio_channels(void) { + SOKOL_ASSERT(_saudio.setup_called); return _saudio.num_channels; } SOKOL_API_IMPL bool saudio_suspended(void) { + SOKOL_ASSERT(_saudio.setup_called); #if defined(_SAUDIO_EMSCRIPTEN) if (_saudio.valid) { return 1 == saudio_js_suspended(); @@ -2567,6 +2576,7 @@ SOKOL_API_IMPL bool saudio_suspended(void) { } SOKOL_API_IMPL int saudio_expect(void) { + SOKOL_ASSERT(_saudio.setup_called); if (_saudio.valid) { const int num_frames = _saudio_fifo_writable_bytes(&_saudio.fifo) / _saudio.bytes_per_frame; return num_frames; @@ -2577,6 +2587,7 @@ SOKOL_API_IMPL int saudio_expect(void) { } SOKOL_API_IMPL int saudio_push(const float* frames, int num_frames) { + SOKOL_ASSERT(_saudio.setup_called); SOKOL_ASSERT(frames && (num_frames > 0)); if (_saudio.valid) { const int num_bytes = num_frames * _saudio.bytes_per_frame; diff --git a/thirdparty/sokol/sokol_gfx.h b/thirdparty/sokol/sokol_gfx.h index 7cd14a91e4..df133d1b1a 100644 --- a/thirdparty/sokol/sokol_gfx.h +++ b/thirdparty/sokol/sokol_gfx.h @@ -17,19 +17,19 @@ In the same place define one of the following to select the rendering backend: - #define SOKOL_GLCORE33 + #define SOKOL_GLCORE #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL #define SOKOL_WGPU #define SOKOL_DUMMY_BACKEND - I.e. for the GL 3.3 Core Profile it should look like this: + I.e. for the desktop GL it should look like this: #include ... #include ... #define SOKOL_IMPL - #define SOKOL_GLCORE33 + #define SOKOL_GLCORE #include "sokol_gfx.h" The dummy backend replaces the platform-specific backend code with empty @@ -141,16 +141,15 @@ object handle is required instead of an sg_swapchain struct. An offscreen pass is started like this (assuming attachments is an sg_attachments handle): - sg_begin_pass(&(sg_pass){ .action = { ... }, .attachments = attachemnts }); + sg_begin_pass(&(sg_pass){ .action = { ... }, .attachments = attachments }); --- set the render pipeline state for the next draw call with: sg_apply_pipeline(sg_pipeline pip) --- fill an sg_bindings struct with the resource bindings for the next - draw call (1..N vertex buffers, 0 or 1 index buffer, 0..N image objects and - 0..N sampler objects on the vertex-shader- and fragment-shader-stage - and then call + draw call (0..N vertex buffers, 0 or 1 index buffer, 0..N image-objects, + samplers and storage-buffers), and call: sg_apply_bindings(const sg_bindings* bindings) @@ -718,7 +717,9 @@ the sg_make_shader() function requires the following information: - Shader code or shader binary blobs for the vertex- and fragment- shader-stage: - - for the desktop GL backend, source code must be provided in '#version 330' syntax + - for the desktop GL backend, source code can be provided in '#version 410' or + '#version 430', version 430 is required for storage buffer support, but note + that this is not available on macOS - for the GLES3 backend, source code must be provided in '#version 300 es' syntax - for the D3D11 backend, shaders can be provided as source or binary blobs, the source code should be in HLSL4.0 (for best compatibility) or alternatively @@ -757,6 +758,12 @@ - please also NOTE the documentation sections about UNIFORM DATA LAYOUT and CROSS-BACKEND COMMON UNIFORM DATA LAYOUT below! + - A description of each storage buffer used in the shader: + - a boolean 'readonly' flag, note that currently only + readonly storage buffers are supported + - note that storage buffers are not supported on all backends + and platforms + - A description of each texture/image used in the shader: - the expected image type: - SG_IMAGETYPE_2D @@ -773,7 +780,7 @@ (currently it's not supported to fetch data from multisampled textures in shaders, but this is planned for a later time) - - A description of each sampler used in the shader: + - A description of each texture sampler used in the shader: - SG_SAMPLERTYPE_FILTERING, - SG_SAMPLERTYPE_NONFILTERING, - SG_SAMPLERTYPE_COMPARISON, @@ -953,6 +960,126 @@ The by far easiest way to tackle the common uniform block layout problem is to use the sokol-shdc shader cross-compiler tool! + ON STORAGE BUFFERS + ================== + Storage buffers can be used to pass large amounts of random access structured + data fromt the CPU side to the shaders. They are similar to data textures, but are + more convenient to use both on the CPU and shader side since they can be accessed + in shaders as as a 1-dimensional array of struct items. + + Storage buffers are *NOT* supported on the following platform/backend combos: + + - macOS+GL (because storage buffers require GL 4.3, while macOS only goes up to GL 4.1) + - all GLES3 platforms (WebGL2, iOS, Android - with the option that support on + Android may be added at a later point) + + Currently only 'readonly' storage buffers are supported (meaning it's not possible + to write to storage buffers from shaders). + + To use storage buffers, the following steps are required: + + - write a shader which uses storage buffers (also see the example links below) + - create one or more storage buffers via sg_make_buffer() with the + buffer type SG_BUFFERTYPE_STORAGEBUFFER + - when creating a shader via sg_make_shader(), populate the sg_shader_desc + struct with binding info (when using sokol-shdc, this step will be taken care + of automatically) + - which storage buffer bind slots on the vertex- and fragment-stage + are occupied + - whether the storage buffer on that bind slot is readonly (this is currently required + to be true) + - when calling sg_apply_bindings(), apply the matching bind slots with the previously + created storage buffers + - ...and that's it. + + For more details, see the following backend-agnostic sokol samples: + + - simple vertex pulling from a storage buffer: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.glsl + - instanced rendering via storage buffers (vertex- and instance-pulling): + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.glsl + - storage buffers both on the vertex- and fragment-stage: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.glsl + - the Ozz animation sample rewritten to pull all rendering data from storage buffers: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.cc + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.glsl + + ...also see the following backend-specific vertex pulling samples (those also don't use sokol-shdc): + + - D3D11: https://github.com/floooh/sokol-samples/blob/master/d3d11/vertexpulling-d3d11.c + - desktop GL: https://github.com/floooh/sokol-samples/blob/master/glfw/vertexpulling-glfw.c + - Metal: https://github.com/floooh/sokol-samples/blob/master/metal/vertexpulling-metal.c + - WebGPU: https://github.com/floooh/sokol-samples/blob/master/wgpu/vertexpulling-wgpu.c + + Storage buffer shader authoring caveats when using sokol-shdc: + + - declare a storage buffer interface block with `readonly buffer [name] { ... }` + - do NOT annotate storage buffers with `layout(...)`, sokol-shdc will take care of that + - declare a struct which describes a single array item in the storage buffer interface block + - only put a single flexible array member into the storage buffer interface block + + E.g. a complete example in 'sokol-shdc GLSL': + + ```glsl + // declare a struct: + struct sb_vertex { + vec3 pos; + vec4 color; + } + // declare a buffer interface block with a single flexible struct array: + readonly buffer vertices { + sb_vertex vtx[]; + } + // in the shader function, access the storage buffer like this: + void main() { + vec3 pos = vtx[gl_VertexIndex].pos; + ... + } + ``` + + Backend-specific storage-buffer caveats (not relevant when using sokol-shdc): + + D3D11: + - storage buffers are created as 'raw' Byte Address Buffers + (https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-intro#raw-views-of-buffers) + - in HLSL, use a ByteAddressBuffer to access the buffer content + (https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-byteaddressbuffer) + - in D3D11, storage buffers and textures share the same bind slots, sokol-gfx reserves + shader resource slots 0..15 for textures and 16..23 for storage buffers. + - e.g. in HLSL, storage buffer bindings start at register(t16) no matter the shader stage + + Metal: + - in Metal there is no internal difference between vertex-, uniform- and + storage-buffers, all are bound to the same 'buffer bind slots' with the + following reserved ranges: + - vertex shader stage: + - uniform buffers (internal): slots 0..3 + - vertex buffers: slots 4..11 + - storage buffers: slots 12..19 + - fragment shader stage: + - uniform buffers (internal): slots 0..3 + - storage buffers: slots 4..11 + - this means in MSL, storage buffer bindings start at [[buffer(12)]] in the vertex + shaders, and at [[buffer(4)]] in fragment shaders + + GL: + - the GL backend doesn't use name-lookup to find storage buffer bindings, this + means you must annotate buffers with `layout(std430, binding=N)` in GLSL + - ...where N is 0..7 in the vertex shader, and 8..15 in the fragment shader + + WebGPU: + - in WGSL, use the following bind locations for the various shader resource types: + - vertex shader stage: + - textures `@group(1) @binding(0..15)` + - samplers `@group(1) @binding(16..31)` + - storage buffers `@group(1) @binding(32..47)` + - fragment shader stage: + - textures `@group(1) @binding(48..63)` + - samplers `@group(1) @binding(64..79)` + - storage buffers `@group(1) @binding(80..95)` TRACE HOOKS: ============ @@ -1334,12 +1461,14 @@ offsets depending on resource type and shader stage. - Vertex shader textures must start at `@group(1) @binding(0)` - Vertex shader samplers must start at `@group(1) @binding(16)` - - Fragment shader textures must start at `@group(1) @binding(32)` - - Fragment shader samplers must start at `@group(1) @binding(48)` + - Vertex shader storage buffers must start at `@group(1) @binding(32)` + - Fragment shader textures must start at `@group(1) @binding(48)` + - Fragment shader samplers must start at `@group(1) @binding(64)` + - Fragment shader storage buffers must start at `@group(1) @binding(80)` Note that the actual number of allowed per-stage texture- and sampler-bindings in sokol-gfx is currently lower than the above ranges (currently only up to - 12 textures and 8 samplers per shader stage are allowed). + 12 textures, 8 samplers and 8 storage buffers are allowed per shader stage). If you use sokol-shdc to generate WGSL shader code, you don't need to worry about the above binding convention since sokol-shdc assigns bind slots @@ -1516,6 +1645,7 @@ enum { SG_MAX_SHADERSTAGE_IMAGES = 12, SG_MAX_SHADERSTAGE_SAMPLERS = 8, SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS = 12, + SG_MAX_SHADERSTAGE_STORAGEBUFFERS = 8, SG_MAX_SHADERSTAGE_UBS = 4, SG_MAX_UB_MEMBERS = 16, SG_MAX_VERTEX_ATTRIBUTES = 16, @@ -1537,7 +1667,7 @@ typedef struct sg_color { float r, g, b, a; } sg_color; to get the currently active backend. */ typedef enum sg_backend { - SG_BACKEND_GLCORE33, + SG_BACKEND_GLCORE, SG_BACKEND_GLES3, SG_BACKEND_D3D11, SG_BACKEND_METAL_IOS, @@ -1666,8 +1796,10 @@ typedef enum sg_pixel_format { SG_PIXELFORMAT_ETC2_RGB8A1, SG_PIXELFORMAT_ETC2_RGBA8, SG_PIXELFORMAT_ETC2_SRGB8A8, - SG_PIXELFORMAT_ETC2_RG11, - SG_PIXELFORMAT_ETC2_RG11SN, + SG_PIXELFORMAT_EAC_R11, + SG_PIXELFORMAT_EAC_R11SN, + SG_PIXELFORMAT_EAC_RG11, + SG_PIXELFORMAT_EAC_RG11SN, SG_PIXELFORMAT_ASTC_4x4_RGBA, SG_PIXELFORMAT_ASTC_4x4_SRGBA, @@ -1700,6 +1832,7 @@ typedef struct sg_features { bool image_clamp_to_border; // border color and clamp-to-border UV-wrap mode is supported bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks + bool storage_buffer; // storage buffers are supported } sg_features; /* @@ -1789,8 +1922,10 @@ typedef enum sg_usage { /* sg_buffer_type - This indicates whether a buffer contains vertex- or index-data, - used in the sg_buffer_desc.type member when creating a buffer. + Indicates whether a buffer will be bound as vertex-, + index- or storage-buffer. + + Used in the sg_buffer_desc.type member when creating a buffer. The default value is SG_BUFFERTYPE_VERTEXBUFFER. */ @@ -1798,6 +1933,7 @@ typedef enum sg_buffer_type { _SG_BUFFERTYPE_DEFAULT, // value 0 reserved for default-init SG_BUFFERTYPE_VERTEXBUFFER, SG_BUFFERTYPE_INDEXBUFFER, + SG_BUFFERTYPE_STORAGEBUFFER, _SG_BUFFERTYPE_NUM, _SG_BUFFERTYPE_FORCE_U32 = 0x7FFFFFFF } sg_buffer_type; @@ -1851,7 +1987,7 @@ typedef enum sg_image_type { is compatible with what the shader expects. Apart from the sokol-gfx validation layer, WebGPU is the only backend API which actually requires matching texture and sampler type to be provided upfront for validation - (after 3D APIs treat texture/sampler type mismatches as undefined behaviour). + (other 3D APIs treat texture/sampler type mismatches as undefined behaviour). NOTE that the following texture pixel formats require the use of SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT, combined with a sampler @@ -1921,12 +2057,12 @@ typedef enum sg_cube_face { sg_shader_stage There are 2 shader stages: vertex- and fragment-shader-stage. - Each shader stage consists of: + Each shader stage - - one slot for a shader function (provided as source- or byte-code) - - SG_MAX_SHADERSTAGE_UBS slots for uniform blocks - - SG_MAX_SHADERSTAGE_IMAGES slots for images used as textures by - the shader function + - SG_MAX_SHADERSTAGE_UBS slots for applying uniform data + - SG_MAX_SHADERSTAGE_IMAGES slots for images used as textures + - SG_MAX_SHADERSTAGE_SAMPLERS slots for texture samplers + - SG_MAX_SHADERSTAGE_STORAGEBUFFERS slots for storage buffer bindings */ typedef enum sg_shader_stage { SG_SHADERSTAGE_VS, @@ -2432,7 +2568,7 @@ typedef struct sg_pass_action { as 'type erased' void pointers: GL: on all GL backends, a GL framebuffer object must be provided. This - can be zero for the defaul framebuffer. + can be zero for the default framebuffer. D3D11: - an ID3D11RenderTargetView for the rendering surface, without @@ -2562,12 +2698,17 @@ typedef struct sg_pass { - 0..1 index buffer offsets - 0..N vertex shader stage images - 0..N vertex shader stage samplers + - 0..N vertex shader storage buffers - 0..N fragment shader stage images - 0..N fragment shader stage samplers + - 0..N fragment shader storage buffers - The max number of vertex buffer and shader stage images - are defined by the SG_MAX_VERTEX_BUFFERS and - SG_MAX_SHADERSTAGE_IMAGES configuration constants. + For the max number of bindings, see the constant definitions: + + - SG_MAX_VERTEX_BUFFERS + - SG_MAX_SHADERSTAGE_IMAGES + - SG_MAX_SHADERSTAGE_SAMPLERS + - SG_MAX_SHADERSTAGE_STORAGEBUFFERS The optional buffer offsets can be used to put different unrelated chunks of vertex- and/or index-data into the same buffer objects. @@ -2575,6 +2716,7 @@ typedef struct sg_pass { typedef struct sg_stage_bindings { sg_image images[SG_MAX_SHADERSTAGE_IMAGES]; sg_sampler samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_buffer storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; } sg_stage_bindings; typedef struct sg_bindings { @@ -2601,11 +2743,7 @@ typedef struct sg_bindings { .usage: SG_USAGE_IMMUTABLE .data.ptr 0 (*must* be valid for immutable buffers) .data.size 0 (*must* be > 0 for immutable buffers) - .label 0 (optional string label for trace hooks) - - The label will be ignored by sokol_gfx.h, it is only useful - when hooking into sg_make_buffer() or sg_init_buffer() via - the sg_install_trace_hooks() function. + .label 0 (optional string label) For immutable buffers which are initialized with initial data, keep the .size item zero-initialized, and set the size together with the @@ -2818,6 +2956,9 @@ typedef struct sg_sampler_desc { - the texture slot of the involved texture - the sampler slot of the involved sampler - for GLSL only: the name of the combined image-sampler object + - reflection info for each storage-buffer used by the shader: + - whether the storage buffer is readonly (currently this + must be true) For all GL backends, shader source-code must be provided. For D3D11 and Metal, either shader source-code or byte-code can be provided. @@ -2846,6 +2987,11 @@ typedef struct sg_shader_uniform_block_desc { sg_shader_uniform_desc uniforms[SG_MAX_UB_MEMBERS]; } sg_shader_uniform_block_desc; +typedef struct sg_shader_storage_buffer_desc { + bool used; + bool readonly; +} sg_shader_storage_buffer_desc; + typedef struct sg_shader_image_desc { bool used; bool multisampled; @@ -2871,6 +3017,7 @@ typedef struct sg_shader_stage_desc { const char* entry; const char* d3d11_target; sg_shader_uniform_block_desc uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + sg_shader_storage_buffer_desc storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; sg_shader_image_desc images[SG_MAX_SHADERSTAGE_IMAGES]; sg_shader_sampler_desc samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; sg_shader_image_sampler_pair_desc image_sampler_pairs[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; @@ -3214,9 +3361,9 @@ typedef struct sg_attachments_info { /* sg_frame_stats - Allows to track generic and backend-specific tracking stats about a + Allows to track generic and backend-specific stats about a render frame. Obtained by calling sg_query_frame_stats(). The returned - struct will contains information about the *previous* frame. + struct contains information about the *previous* frame. */ typedef struct sg_frame_stats_gl { uint32_t num_bind_buffer; @@ -3301,6 +3448,7 @@ typedef struct sg_frame_stats_metal_bindings { uint32_t num_set_vertex_buffer; uint32_t num_set_vertex_texture; uint32_t num_set_vertex_sampler_state; + uint32_t num_set_fragment_buffer; uint32_t num_set_fragment_texture; uint32_t num_set_fragment_sampler_state; } sg_frame_stats_metal_bindings; @@ -3390,6 +3538,7 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE (gl)") \ _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNKNOWN, "framebuffer completeness check failed (unknown reason) (gl)") \ _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_FAILED, "CreateBuffer() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_SRV_FAILED, "CreateShaderResourceView() failed for storage buffer (d3d11)") \ _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for depth-stencil texture (d3d11)") \ _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_FAILED, "CreateTexture2D() failed for depth-stencil texture (d3d11)") \ _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 2d-, cube- or array-texture (d3d11)") \ @@ -3436,13 +3585,14 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(WGPU_CREATE_SHADER_MODULE_FAILED, "wgpuDeviceCreateShaderModule() failed") \ _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_IMAGES, "shader uses too many sampled images on shader stage (wgpu)") \ _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_SAMPLERS, "shader uses too many samplers on shader stage (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_STORAGEBUFFERS, "shader uses too many storage buffer bindings on shader stage (wgpu)") \ _SG_LOGITEM_XMACRO(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED, "wgpuDeviceCreateBindGroupLayout() for shader stage failed") \ _SG_LOGITEM_XMACRO(WGPU_CREATE_PIPELINE_LAYOUT_FAILED, "wgpuDeviceCreatePipelineLayout() failed") \ _SG_LOGITEM_XMACRO(WGPU_CREATE_RENDER_PIPELINE_FAILED, "wgpuDeviceCreateRenderPipeline() failed") \ _SG_LOGITEM_XMACRO(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed in create attachments") \ _SG_LOGITEM_XMACRO(IDENTICAL_COMMIT_LISTENER, "attempting to add identical commit listener") \ _SG_LOGITEM_XMACRO(COMMIT_LISTENER_ARRAY_FULL, "commit listener array full") \ - _SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined") \ + _SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SOKOL_TRACE_HOOKS is not defined") \ _SG_LOGITEM_XMACRO(DEALLOC_BUFFER_INVALID_STATE, "sg_dealloc_buffer(): buffer must be in ALLOC state") \ _SG_LOGITEM_XMACRO(DEALLOC_IMAGE_INVALID_STATE, "sg_dealloc_image(): image must be in alloc state") \ _SG_LOGITEM_XMACRO(DEALLOC_SAMPLER_INVALID_STATE, "sg_dealloc_sampler(): sampler must be in alloc state") \ @@ -3480,6 +3630,8 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA, "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)") \ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA_SIZE, "immutable buffer data size differs from buffer size") \ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_NO_DATA, "dynamic/stream usage buffers cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED, "storage buffers not supported by the backend 3D API (requires OpenGL >= 4.3)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4, "size of storage buffers must be a multiple of 4") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_NODATA, "sg_image_data: no data (.ptr and/or .size is zero)") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_DATA_SIZE, "sg_image_data: data size doesn't match expected surface size") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_CANARY, "sg_image_desc not initialized") \ @@ -3513,6 +3665,8 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, "size of uniform block members doesn't match uniform block size") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_ARRAY_COUNT, "uniform array count must be >= 1") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_STORAGEBUFFERS, "shader stage storage buffers must occupy continuous slots (sg_shader_desc.vs|fs.storage_buffers[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_READONLY, "shader stage storage buffers must be readonly (sg_shader_desc.vs|fs.storage_buffers[].readonly)") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMAGES, "shader stage images must occupy continuous slots (sg_shader_desc.vs|fs.images[])") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_SAMPLERS, "shader stage samplers must occupy continuous slots (sg_shader_desc.vs|fs.samplers[])") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE, "shader stage: image-sampler-pair image slot index is out of range (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)") \ @@ -3526,11 +3680,10 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more images are note referenced by (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more samplers are not referenced by image-sampler-pairs (sg_shader_desc.vs|fs.image_sampler_pairs[].sampler_slot)") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMAGE_SAMPLER_PAIRS, "shader stage image-sampler-pairs must occupy continuous slots (sg_shader_desc.vs|fs.image_samplers[])") \ - _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_SEMANTICS, "D3D11 backend requires vertex attribute semantics") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, "vertex attribute name/semantic string too long (max len 16)") \ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_CANARY, "sg_pipeline_desc not initialized") \ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_SHADER, "sg_pipeline_desc.shader missing or invalid") \ - _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_ATTRS, "sg_pipeline_desc.layout.attrs is empty or not continuous") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_CONT_ATTRS, "sg_pipeline_desc.layout.attrs is not continuous") \ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4") \ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, "D3D11 missing vertex attribute semantics in shader") \ _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_CANARY, "sg_attachments_desc not initialized") \ @@ -3566,7 +3719,7 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SAMPLE_COUNT, "pass depth attachment sample count must match color attachment sample count") \ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_CANARY, "sg_begin_pass: pass struct not initialized") \ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_ATTACHMENTS_EXISTS, "sg_begin_pass: attachments object no longer alive") \ - _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_ATTACHMENTS_VALID, "sg_begin_pass: attachemnts object not in resource state VALID") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_ATTACHMENTS_VALID, "sg_begin_pass: attachments object not in resource state VALID") \ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE, "sg_begin_pass: one or more color attachment images are not valid") \ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE, "sg_begin_pass: one or more resolve attachment images are not valid") \ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE, "sg_begin_pass: one or more depth-stencil attachment images are not valid") \ @@ -3634,6 +3787,10 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING on vertex stage, but sampler has SG_FILTER_LINEAR filters") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on vertex stage") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_SMP_EXISTS, "sg_apply_bindings: sampler bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: storage buffer binding on vertex stage is missing or the buffer handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_STORAGEBUFFER_EXISTS, "sg_apply_bindings: storage buffer bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_STORAGEBUFFER_BINDING_BUFFERTYPE, "sg_apply_bindings: buffer bound to vertex stage storage buffer slot is not of type storage buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: unexpected storage buffer binding on vertex stage") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: image binding on fragment stage is missing or the image handle is invalid") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_EXISTS, "sg_apply_bindings: image bound to fragment stage no longer alive") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of image bound to fragment stage doesn't match shader desc") \ @@ -3647,6 +3804,10 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING on fragment stage, but sampler has SG_FILTER_LINEAR filters") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on fragment stage") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_SMP_EXISTS, "sg_apply_bindings: sampler bound to fragment stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: storage buffer binding on fragment stage is missing or the buffer handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_STORAGEBUFFER_EXISTS, "sg_apply_bindings: storage buffer bound to fragment stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_STORAGEBUFFER_BINDING_BUFFERTYPE, "sg_apply_bindings: buffer bound to frahment stage storage buffer slot is not of type storage buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: unexpected storage buffer binding on fragment stage") \ _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_PIPELINE, "sg_apply_uniforms: must be called after sg_apply_pipeline()") \ _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_UB_AT_SLOT, "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot") \ _SG_LOGITEM_XMACRO(VALIDATE_AUB_SIZE, "sg_apply_uniforms: data size doesn't match declared uniform block size") \ @@ -3673,11 +3834,6 @@ typedef enum sg_log_item { The sg_desc struct contains configuration values for sokol_gfx, it is used as parameter to the sg_setup() call. - NOTE that all callback function pointers come in two versions, one without - a userdata pointer, and one with a userdata pointer. You would - either initialize one or the other depending on whether you pass data - to your callbacks. - The default configuration is: .buffer_pool_size 128 @@ -3771,7 +3927,7 @@ typedef struct sg_d3d11_environment { } sg_d3d11_environment; typedef struct sg_wgpu_environment { - const void* device; // WGPUDevice + const void* device; } sg_wgpu_environment; typedef struct sg_environment { @@ -3816,8 +3972,9 @@ typedef struct sg_allocator { that without logging function, sokol-gfx will be completely silent, e.g. it will not report errors, warnings and validation layer messages. For maximum error verbosity, - compile in debug mode (e.g. NDEBUG *not* defined) and install - a logger (for instance the standard logging function from sokol_log.h). + compile in debug mode (e.g. NDEBUG *not* defined) and provide a + compatible logger function in the sg_setup() call + (for instance the standard logging function from sokol_log.h). */ typedef struct sg_logger { void (*func)( @@ -4166,7 +4323,7 @@ inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image( inline sg_sampler sg_make_sampler(const sg_sampler_desc& desc) { return sg_make_sampler(&desc); } inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); } inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); } -inline sg_attachments sg_make_attchments(const sg_attachments_desc& desc) { return sg_make_attachments(&desc); } +inline sg_attachments sg_make_attachments(const sg_attachments_desc& desc) { return sg_make_attachments(&desc); } inline void sg_update_image(sg_image img, const sg_image_data& data) { return sg_update_image(img, &data); } inline void sg_begin_pass(const sg_pass& pass) { return sg_begin_pass(&pass); } @@ -4202,8 +4359,8 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #ifdef SOKOL_GFX_IMPL #define SOKOL_GFX_IMPL_INCLUDED (1) -#if !(defined(SOKOL_GLCORE33)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_DUMMY_BACKEND)) -#error "Please select a backend with SOKOL_GLCORE33, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" +#if !(defined(SOKOL_GLCORE)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_DUMMY_BACKEND)) +#error "Please select a backend with SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" #endif #if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) #error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sg_desc.allocator to override memory allocation functions" @@ -4320,13 +4477,13 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #if defined(__EMSCRIPTEN__) #include #endif -#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) +#elif defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) #define _SOKOL_ANY_GL (1) // include platform specific GL headers (or on Win32: use an embedded GL loader) #if !defined(SOKOL_EXTERNAL_GL_LOADER) #if defined(_WIN32) - #if defined(SOKOL_GLCORE33) && !defined(SOKOL_EXTERNAL_GL_LOADER) + #if defined(SOKOL_GLCORE) && !defined(SOKOL_EXTERNAL_GL_LOADER) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif @@ -4353,7 +4510,7 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #include #endif #elif defined(__linux__) || defined(__unix__) - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) #define GL_GLEXT_PROTOTYPES #include #else @@ -4610,6 +4767,8 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 + #define GL_MAJOR_VERSION 0x821B + #define GL_MINOR_VERSION 0x821C #endif #ifndef GL_UNSIGNED_INT_2_10_10_10_REV @@ -4687,6 +4846,12 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #ifndef GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 #endif + #ifndef GL_COMPRESSED_R11_EAC + #define GL_COMPRESSED_R11_EAC 0x9270 + #endif + #ifndef GL_COMPRESSED_SIGNED_R11_EAC + #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 + #endif #ifndef GL_COMPRESSED_RG11_EAC #define GL_COMPRESSED_RG11_EAC 0x9272 #endif @@ -4725,6 +4890,12 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #endif #endif +// make some GL constants generally available to simplify compilation, +// use of those constants will be filtered by runtime flags +#ifndef GL_SHADER_STORAGE_BUFFER +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#endif + // ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ // ██ ██ ██ ██ ██ ██ ██ ██ ██ // ███████ ██ ██████ ██ ██ ██ ██ ███████ @@ -4756,7 +4927,7 @@ _SOKOL_PRIVATE int _sg_slot_index(uint32_t id); // constants enum { - _SG_STRING_SIZE = 16, + _SG_STRING_SIZE = 32, _SG_SLOT_SHIFT = 16, _SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1, _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), @@ -4876,6 +5047,11 @@ typedef struct { size_t size; } _sg_shader_uniform_block_t; +typedef struct { + bool used; + bool readonly; +} _sg_shader_storage_buffer_t; + typedef struct { sg_image_type image_type; sg_image_sample_type sample_type; @@ -4894,10 +5070,12 @@ typedef struct { typedef struct { int num_uniform_blocks; + int num_storage_buffers; int num_images; int num_samplers; int num_image_samplers; _sg_shader_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_shader_storage_buffer_t storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; _sg_shader_sampler_t samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; _sg_shader_image_sampler_t image_samplers[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; @@ -4952,6 +5130,16 @@ _SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_sh stage->image_samplers[img_smp_index].sampler_slot = img_smp_desc->sampler_slot; stage->num_image_samplers++; } + SOKOL_ASSERT(stage->num_storage_buffers == 0); + for (int sbuf_index = 0; sbuf_index < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; sbuf_index++) { + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + if (!sbuf_desc->used) { + break; + } + stage->storage_buffers[sbuf_index].used = sbuf_desc->used; + stage->storage_buffers[sbuf_index].readonly = sbuf_desc->readonly; + stage->num_storage_buffers++; + } } } @@ -5081,6 +5269,10 @@ typedef struct { typedef _sg_dummy_attachments_t _sg_attachments_t; #elif defined(_SOKOL_ANY_GL) + +#define _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE (SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS * SG_NUM_SHADER_STAGES) +#define _SG_GL_STORAGEBUFFER_STAGE_INDEX_PITCH (SG_MAX_SHADERSTAGE_STORAGEBUFFERS) + typedef struct { _sg_slot_t slot; _sg_buffer_common_t cmn; @@ -5206,8 +5398,6 @@ typedef struct { GLuint sampler; } _sg_gl_cache_texture_sampler_bind_slot; -#define _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE (SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS * SG_NUM_SHADER_STAGES) - typedef struct { sg_depth_state depth; sg_stencil_state stencil; @@ -5222,8 +5412,11 @@ typedef struct { _sg_gl_cache_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; GLuint vertex_buffer; GLuint index_buffer; + GLuint storage_buffer; // general bind point + GLuint stage_storage_buffers[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; GLuint stored_vertex_buffer; GLuint stored_index_buffer; + GLuint stored_storage_buffer; GLuint prog; _sg_gl_cache_texture_sampler_bind_slot texture_samplers[_SG_GL_TEXTURE_SAMPLER_CACHE_SIZE]; _sg_gl_cache_texture_sampler_bind_slot stored_texture_sampler; @@ -5256,6 +5449,7 @@ typedef struct { _sg_buffer_common_t cmn; struct { ID3D11Buffer* buf; + ID3D11ShaderResourceView* srv; } d3d11; } _sg_d3d11_buffer_t; typedef _sg_d3d11_buffer_t _sg_buffer_t; @@ -5471,6 +5665,8 @@ typedef struct { sg_image cur_fs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; sg_sampler cur_vs_sampler_ids[SG_MAX_SHADERSTAGE_SAMPLERS]; sg_sampler cur_fs_sampler_ids[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_buffer cur_vs_storagebuffer_ids[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + sg_buffer cur_fs_storagebuffer_ids[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; } _sg_mtl_state_cache_t; typedef struct { @@ -5498,6 +5694,7 @@ typedef struct { #define _SG_WGPU_NUM_BINDGROUPS (2) // 0: uniforms, 1: images and sampler on both shader stages #define _SG_WGPU_UNIFORM_BINDGROUP_INDEX (0) #define _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX (1) +#define _SG_WGPU_MAX_BINDGROUP_ENTRIES (SG_NUM_SHADER_STAGES * (SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS + SG_MAX_SHADERSTAGE_STORAGEBUFFERS)) typedef struct { _sg_slot_t slot; @@ -5586,7 +5783,7 @@ typedef struct { uint32_t id; } _sg_wgpu_bindgroup_handle_t; -#define _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS (1 + SG_NUM_SHADER_STAGES * (SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS)) +#define _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS (1 + _SG_WGPU_MAX_BINDGROUP_ENTRIES) typedef struct { uint64_t hash; uint32_t items[_SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS]; @@ -5672,16 +5869,20 @@ typedef struct { int num_vbs; int num_vs_imgs; int num_vs_smps; + int num_vs_sbufs; int num_fs_imgs; int num_fs_smps; + int num_fs_sbufs; int vb_offsets[SG_MAX_VERTEX_BUFFERS]; int ib_offset; _sg_buffer_t* vbs[SG_MAX_VERTEX_BUFFERS]; _sg_buffer_t* ib; _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES]; _sg_sampler_t* vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_buffer_t* vs_sbufs[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES]; _sg_sampler_t* fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_buffer_t* fs_sbufs[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; } _sg_bindings_t; typedef struct { @@ -5711,7 +5912,6 @@ typedef struct { } swapchain; } cur_pass; sg_pipeline cur_pipeline; - bool apply_bindings_called; bool next_draw_valid; #if defined(SOKOL_DEBUG) sg_log_item validate_error; @@ -6026,8 +6226,10 @@ _SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_ETC2_RGB8A1: case SG_PIXELFORMAT_ETC2_RGBA8: case SG_PIXELFORMAT_ETC2_SRGB8A8: - case SG_PIXELFORMAT_ETC2_RG11: - case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: case SG_PIXELFORMAT_ASTC_4x4_RGBA: case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return true; @@ -6155,6 +6357,8 @@ _SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) case SG_PIXELFORMAT_ETC2_RGB8: case SG_PIXELFORMAT_ETC2_SRGB8: case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: pitch = ((width + 3) / 4) * 8; pitch = pitch < 8 ? 8 : pitch; break; @@ -6169,8 +6373,8 @@ _SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) case SG_PIXELFORMAT_BC7_SRGBA: case SG_PIXELFORMAT_ETC2_RGBA8: case SG_PIXELFORMAT_ETC2_SRGB8A8: - case SG_PIXELFORMAT_ETC2_RG11: - case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: case SG_PIXELFORMAT_ASTC_4x4_RGBA: case SG_PIXELFORMAT_ASTC_4x4_SRGBA: pitch = ((width + 3) / 4) * 16; @@ -6204,8 +6408,10 @@ _SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { case SG_PIXELFORMAT_ETC2_RGB8A1: case SG_PIXELFORMAT_ETC2_RGBA8: case SG_PIXELFORMAT_ETC2_SRGB8A8: - case SG_PIXELFORMAT_ETC2_RG11: - case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: case SG_PIXELFORMAT_BC2_RGBA: case SG_PIXELFORMAT_BC3_RGBA: case SG_PIXELFORMAT_BC3_SRGBA: @@ -6277,6 +6483,12 @@ _SOKOL_PRIVATE void _sg_pixelformat_sr(_sg_pixelformat_info_t* pfi) { pfi->render = true; } +_SOKOL_PRIVATE void _sg_pixelformat_sfr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->render = true; +} + _SOKOL_PRIVATE void _sg_pixelformat_srmd(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->render = true; @@ -6701,7 +6913,8 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glSamplerParameteri, void, (GLuint sampler, GLenum pname, GLint param)) \ _SG_XMACRO(glSamplerParameterf, void, (GLuint sampler, GLenum pname, GLfloat param)) \ _SG_XMACRO(glSamplerParameterfv, void, (GLuint sampler, GLenum pname, const GLfloat* params)) \ - _SG_XMACRO(glDeleteSamplers, void, (GLsizei n, const GLuint* samplers)) + _SG_XMACRO(glDeleteSamplers, void, (GLsizei n, const GLuint* samplers)) \ + _SG_XMACRO(glBindBufferBase, void, (GLenum target, GLuint index, GLuint buffer)) // generate GL function pointer typedefs #define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; @@ -6748,6 +6961,7 @@ _SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { switch (t) { case SG_BUFFERTYPE_VERTEXBUFFER: return GL_ARRAY_BUFFER; case SG_BUFFERTYPE_INDEXBUFFER: return GL_ELEMENT_ARRAY_BUFFER; + case SG_BUFFERTYPE_STORAGEBUFFER: return GL_SHADER_STORAGE_BUFFER; default: SOKOL_UNREACHABLE; return 0; } } @@ -6957,7 +7171,7 @@ _SOKOL_PRIVATE GLenum _sg_gl_mag_filter(sg_filter mag_f) { _SOKOL_PRIVATE GLenum _sg_gl_wrap(sg_wrap w) { switch (w) { case SG_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; #else case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_EDGE; @@ -7126,9 +7340,13 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { return GL_COMPRESSED_RGBA8_ETC2_EAC; case SG_PIXELFORMAT_ETC2_SRGB8A8: return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; - case SG_PIXELFORMAT_ETC2_RG11: + case SG_PIXELFORMAT_EAC_R11: + return GL_COMPRESSED_R11_EAC; + case SG_PIXELFORMAT_EAC_R11SN: + return GL_COMPRESSED_SIGNED_R11_EAC; + case SG_PIXELFORMAT_EAC_RG11: return GL_COMPRESSED_RG11_EAC; - case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_EAC_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; case SG_PIXELFORMAT_ASTC_4x4_RGBA: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; @@ -7210,8 +7428,10 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC; case SG_PIXELFORMAT_ETC2_SRGB8A8: return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; - case SG_PIXELFORMAT_ETC2_RG11: return GL_COMPRESSED_RG11_EAC; - case SG_PIXELFORMAT_ETC2_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; + case SG_PIXELFORMAT_EAC_R11: return GL_COMPRESSED_R11_EAC; + case SG_PIXELFORMAT_EAC_R11SN: return GL_COMPRESSED_SIGNED_R11_EAC; + case SG_PIXELFORMAT_EAC_RG11: return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_EAC_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; case SG_PIXELFORMAT_ASTC_4x4_RGBA: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; default: SOKOL_UNREACHABLE; return 0; @@ -7356,8 +7576,10 @@ _SOKOL_PRIVATE void _sg_gl_init_pixelformats_etc2(void) { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); } _SOKOL_PRIVATE void _sg_gl_init_pixelformats_astc(void) { @@ -7402,14 +7624,20 @@ _SOKOL_PRIVATE void _sg_gl_init_limits(void) { _sg.limits.gl_max_combined_texture_image_units = gl_int; } -#if defined(SOKOL_GLCORE33) -_SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { - _sg.backend = SG_BACKEND_GLCORE33; +#if defined(SOKOL_GLCORE) +_SOKOL_PRIVATE void _sg_gl_init_caps_glcore(void) { + _sg.backend = SG_BACKEND_GLCORE; + GLint major_version = 0; + GLint minor_version = 0; + glGetIntegerv(GL_MAJOR_VERSION, &major_version); + glGetIntegerv(GL_MINOR_VERSION, &minor_version); + const int version = major_version * 100 + minor_version * 10; _sg.features.origin_top_left = false; _sg.features.image_clamp_to_border = true; _sg.features.mrt_independent_blend_state = false; _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = version >= 430; // scan extensions bool has_s3tc = false; // BC1..BC3 @@ -7482,6 +7710,7 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { _sg.features.image_clamp_to_border = false; _sg.features.mrt_independent_blend_state = false; _sg.features.mrt_independent_write_mask = false; + _sg.features.storage_buffer = false; bool has_s3tc = false; // BC1..BC3 bool has_rgtc = false; // BC4 and BC5 @@ -7571,6 +7800,12 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { #endif //-- state cache implementation ------------------------------------------------ +_SOKOL_PRIVATE GLuint _sg_gl_storagebuffer_bind_index(int stage, int slot) { + SOKOL_ASSERT((stage >= 0) && (stage < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((slot >= 0) && (slot < SG_MAX_SHADERSTAGE_STORAGEBUFFERS)); + return (GLuint) (stage * _SG_GL_STORAGEBUFFER_STAGE_INDEX_PITCH + slot); +} + _SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { if (force || (_sg.gl.cache.vertex_buffer != 0)) { glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -7582,30 +7817,77 @@ _SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { _sg.gl.cache.index_buffer = 0; _sg_stats_add(gl.num_bind_buffer, 1); } + if (force || (_sg.gl.cache.storage_buffer != 0)) { + if (_sg.features.storage_buffer) { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + } + _sg.gl.cache.storage_buffer = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + for (int stage = 0; stage < SG_NUM_SHADER_STAGES; stage++) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + if (force || (_sg.gl.cache.stage_storage_buffers[stage][i] != 0)) { + const GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, i); + if (_sg.features.storage_buffer) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, 0); + } + _sg.gl.cache.stage_storage_buffers[stage][i] = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + } + } } _SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { - SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target)); + SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target) || (GL_SHADER_STORAGE_BUFFER == target)); if (target == GL_ARRAY_BUFFER) { if (_sg.gl.cache.vertex_buffer != buffer) { _sg.gl.cache.vertex_buffer = buffer; glBindBuffer(target, buffer); _sg_stats_add(gl.num_bind_buffer, 1); } - } else { + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { if (_sg.gl.cache.index_buffer != buffer) { _sg.gl.cache.index_buffer = buffer; glBindBuffer(target, buffer); _sg_stats_add(gl.num_bind_buffer, 1); } + } else if (target == GL_SHADER_STORAGE_BUFFER) { + if (_sg.gl.cache.storage_buffer != buffer) { + _sg.gl.cache.storage_buffer = buffer; + if (_sg.features.storage_buffer) { + glBindBuffer(target, buffer); + } + _sg_stats_add(gl.num_bind_buffer, 1); + } + } else { + SOKOL_UNREACHABLE; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_storage_buffer(int stage, int slot, GLuint buffer) { + SOKOL_ASSERT((stage >= 0) && (stage < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((slot >= 0) && (slot < SG_MAX_SHADERSTAGE_STORAGEBUFFERS)); + if (_sg.gl.cache.stage_storage_buffers[stage][slot] != buffer) { + _sg.gl.cache.stage_storage_buffers[stage][slot] = buffer; + _sg.gl.cache.storage_buffer = buffer; // not a bug + GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, slot); + if (_sg.features.storage_buffer) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, buffer); + } + _sg_stats_add(gl.num_bind_buffer, 1); } } _SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) { if (target == GL_ARRAY_BUFFER) { _sg.gl.cache.stored_vertex_buffer = _sg.gl.cache.vertex_buffer; - } else { + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer; + } else if (target == GL_SHADER_STORAGE_BUFFER) { + _sg.gl.cache.stored_storage_buffer = _sg.gl.cache.storage_buffer; + } else { + SOKOL_UNREACHABLE; } } @@ -7616,12 +7898,20 @@ _SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); _sg.gl.cache.stored_vertex_buffer = 0; } - } else { + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { if (_sg.gl.cache.stored_index_buffer != 0) { // we only care about restoring valid ids _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_index_buffer); _sg.gl.cache.stored_index_buffer = 0; } + } else if (target == GL_SHADER_STORAGE_BUFFER) { + if (_sg.gl.cache.stored_storage_buffer != 0) { + // we only care about restoring valid ids + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_storage_buffer); + _sg.gl.cache.stored_storage_buffer = 0; + } + } else { + SOKOL_UNREACHABLE; } } @@ -7637,12 +7927,31 @@ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); _sg_stats_add(gl.num_bind_buffer, 1); } + if (buf == _sg.gl.cache.storage_buffer) { + _sg.gl.cache.storage_buffer = 0; + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + for (int stage = 0; stage < SG_NUM_SHADER_STAGES; stage++) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + if (buf == _sg.gl.cache.stage_storage_buffers[stage][i]) { + _sg.gl.cache.stage_storage_buffers[stage][i] = 0; + _sg.gl.cache.storage_buffer = 0; // not a bug! + const GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, i); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + } + } if (buf == _sg.gl.cache.stored_vertex_buffer) { _sg.gl.cache.stored_vertex_buffer = 0; } if (buf == _sg.gl.cache.stored_index_buffer) { _sg.gl.cache.stored_index_buffer = 0; } + if (buf == _sg.gl.cache.stored_storage_buffer) { + _sg.gl.cache.stored_storage_buffer = 0; + } for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { if (buf == _sg.gl.cache.attrs[i].gl_vbuf) { _sg.gl.cache.attrs[i].gl_vbuf = 0; @@ -7852,7 +8161,7 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { glEnable(GL_DITHER); glDisable(GL_POLYGON_OFFSET_FILL); _sg_stats_add(gl.num_render_state, 10); - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) glEnable(GL_MULTISAMPLE); glEnable(GL_PROGRAM_POINT_SIZE); _sg_stats_add(gl.num_render_state, 2); @@ -7873,8 +8182,8 @@ _SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { #if defined(SOKOL_DEBUG) while (glGetError() != GL_NO_ERROR); #endif - #if defined(SOKOL_GLCORE33) - _sg_gl_init_caps_glcore33(); + #if defined(SOKOL_GLCORE) + _sg_gl_init_caps_glcore(); #elif defined(SOKOL_GLES3) _sg_gl_init_caps_gles3(); #endif @@ -7884,7 +8193,7 @@ _SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { _SG_GL_CHECK_ERROR(); // incoming texture data is generally expected to be packed tightly glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) // enable seamless cubemap sampling (only desktop GL) glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); #endif @@ -7991,40 +8300,57 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ _sg_gl_cache_store_texture_sampler_binding(0); _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[slot], 0); glTexParameteri(img->gl.target, GL_TEXTURE_MAX_LEVEL, img->cmn.num_mipmaps - 1); - const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; - int data_index = 0; - for (int face_index = 0; face_index < num_faces; face_index++) { - for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) { - GLenum gl_img_target = img->gl.target; - if (SG_IMAGETYPE_CUBE == img->cmn.type) { - gl_img_target = _sg_gl_cubeface_target(face_index); - } - const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr; - const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); - const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + + // NOTE: workaround for https://issues.chromium.org/issues/355605685 + // FIXME: on GLES3 and GL 4.3 (e.g. not macOS) the texture initialization + // should be rewritten to use glTexStorage + glTexSubImage + bool tex_storage_allocated = false; + #if defined(__EMSCRIPTEN__) + if (desc->data.subimage[0][0].ptr == 0) { + tex_storage_allocated = true; if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { - if (is_compressed) { - const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; - glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, - mip_width, mip_height, 0, data_size, data_ptr); - } else { - const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, - mip_width, mip_height, 0, gl_format, gl_type, data_ptr); - } + glTexStorage2D(img->gl.target, img->cmn.num_mipmaps, gl_internal_format, img->cmn.width, img->cmn.height); } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { - int mip_depth = img->cmn.num_slices; - if (SG_IMAGETYPE_3D == img->cmn.type) { - mip_depth = _sg_miplevel_dim(mip_depth, mip_index); + glTexStorage3D(img->gl.target, img->cmn.num_mipmaps, gl_internal_format, img->cmn.width, img->cmn.height, img->cmn.num_slices); + } + } + #endif + if (!tex_storage_allocated) { + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; + int data_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + gl_img_target = _sg_gl_cubeface_target(face_index); } - if (is_compressed) { - const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; - glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, - mip_width, mip_height, mip_depth, 0, data_size, data_ptr); - } else { - const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, - mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); + const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, 0, data_size, data_ptr); + } else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, 0, gl_format, gl_type, data_ptr); + } + } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { + int mip_depth = img->cmn.num_slices; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mip_depth = _sg_miplevel_dim(mip_depth, mip_index); + } + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, mip_depth, 0, data_size, data_ptr); + } else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); + } } } } @@ -8075,7 +8401,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_sampler(_sg_sampler_t* smp, const glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_S, (GLint)_sg_gl_wrap(smp->cmn.wrap_u)); glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(smp->cmn.wrap_v)); glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(smp->cmn.wrap_w)); - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) float border[4]; switch (smp->cmn.border_color) { case SG_BORDERCOLOR_TRANSPARENT_BLACK: @@ -8559,13 +8885,13 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(const sg_pass* pass) { if (atts) { // offscreen pass SOKOL_ASSERT(atts->gl.fb); - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) glEnable(GL_FRAMEBUFFER_SRGB); #endif glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.fb); } else { // default pass - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) glDisable(GL_FRAMEBUFFER_SRGB); #endif // NOTE: on some platforms, the default framebuffer of a context @@ -8865,7 +9191,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { if (pip->gl.color_write_mask[i] != _sg.gl.cache.color_write_mask[i]) { const sg_color_mask cm = pip->gl.color_write_mask[i]; _sg.gl.cache.color_write_mask[i] = cm; - #ifdef SOKOL_GLCORE33 + #ifdef SOKOL_GLCORE glColorMaski(i, (cm & SG_COLORMASK_R) != 0, (cm & SG_COLORMASK_G) != 0, @@ -8922,7 +9248,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { } _sg_stats_add(gl.num_render_state, 1); } - #ifdef SOKOL_GLCORE33 + #ifdef SOKOL_GLCORE if (pip->gl.sample_count != _sg.gl.cache.sample_count) { _sg.gl.cache.sample_count = pip->gl.sample_count; if (pip->gl.sample_count > 1) { @@ -8978,6 +9304,18 @@ _SOKOL_PRIVATE bool _sg_gl_apply_bindings(_sg_bindings_t* bnd) { } _SG_GL_CHECK_ERROR(); + // bind storage buffers + for (int slot = 0; slot < bnd->num_vs_sbufs; slot++) { + _sg_buffer_t* sb = bnd->vs_sbufs[slot]; + GLuint gl_sb = sb->gl.buf[sb->cmn.active_slot]; + _sg_gl_cache_bind_storage_buffer(SG_SHADERSTAGE_VS, slot, gl_sb); + } + for (int slot = 0; slot < bnd->num_fs_sbufs; slot++) { + _sg_buffer_t* sb = bnd->fs_sbufs[slot]; + GLuint gl_sb = sb->gl.buf[sb->cmn.active_slot]; + _sg_gl_cache_bind_storage_buffer(SG_SHADERSTAGE_FS, slot, gl_sb); + } + // index buffer (can be 0) const GLuint gl_ib = bnd->ib ? bnd->ib->gl.buf[bnd->ib->cmn.active_slot] : 0; _sg_gl_cache_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); @@ -9093,19 +9431,20 @@ _SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_inst SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); const GLenum i_type = _sg.gl.cache.cur_index_type; const GLenum p_type = _sg.gl.cache.cur_primitive_type; + const bool use_instanced_draw = (num_instances > 1) || (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw); if (0 != i_type) { // indexed rendering const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; const int ib_offset = _sg.gl.cache.cur_ib_offset; const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); - if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { + if (use_instanced_draw) { glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); } else { glDrawElements(p_type, num_elements, i_type, indices); } } else { // non-indexed rendering - if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { + if (use_instanced_draw) { glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); } else { glDrawArrays(p_type, base_element, num_elements); @@ -9211,6 +9550,10 @@ _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* d // >>d3d11 backend #elif defined(SOKOL_D3D11) +#define _SG_D3D11_MAX_SHADERSTAGE_SRVS (32) +#define _SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET (0) +#define _SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET (16) + #if defined(__cplusplus) #define _sg_d3d11_AddRef(self) (self)->AddRef() #else @@ -9223,6 +9566,29 @@ _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* d #define _sg_d3d11_Release(self) (self)->lpVtbl->Release(self) #endif +// NOTE: This needs to be a macro since we can't use the polymorphism in C. It's called on many kinds of resources. +// NOTE: Based on microsoft docs, it's fine to call this with pData=NULL if DataSize is also zero. +#if defined(__cplusplus) +#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->SetPrivateData(guid, DataSize, pData) +#else +#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->lpVtbl->SetPrivateData(self, guid, DataSize, pData) +#endif + +#if defined(__cplusplus) +#define _sg_win32_refguid(guid) guid +#else +#define _sg_win32_refguid(guid) &guid +#endif + +static const GUID _sg_d3d11_WKPDID_D3DDebugObjectName = { 0x429b8c22,0x9188,0x4b0c, {0x87,0x42,0xac,0xb0,0xbf,0x85,0xc2,0x00} }; + +#if defined(SOKOL_DEBUG) +#define _sg_d3d11_setlabel(self, label) _sg_d3d11_SetPrivateData(self, _sg_win32_refguid(_sg_d3d11_WKPDID_D3DDebugObjectName), label ? (UINT)strlen(label) : 0, label) +#else +#define _sg_d3d11_setlabel(self, label) +#endif + + //-- D3D11 C/C++ wrappers ------------------------------------------------------ static inline HRESULT _sg_d3d11_CheckFormatSupport(ID3D11Device* self, DXGI_FORMAT Format, UINT* pFormatSupport) { #if defined(__cplusplus) @@ -9606,6 +9972,34 @@ _SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_usage(sg_usage usg) { } } +_SOKOL_PRIVATE UINT _sg_d3d11_buffer_bind_flags(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: + return D3D11_BIND_VERTEX_BUFFER; + case SG_BUFFERTYPE_INDEXBUFFER: + return D3D11_BIND_INDEX_BUFFER; + case SG_BUFFERTYPE_STORAGEBUFFER: + // FIXME: for compute shaders we'd want UNORDERED_ACCESS? + return D3D11_BIND_SHADER_RESOURCE; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_buffer_misc_flags(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: + case SG_BUFFERTYPE_INDEXBUFFER: + return 0; + case SG_BUFFERTYPE_STORAGEBUFFER: + return D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + _SOKOL_PRIVATE UINT _sg_d3d11_cpu_access_flags(sg_usage usg) { switch (usg) { case SG_USAGE_IMMUTABLE: @@ -9914,6 +10308,7 @@ _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { _sg.features.image_clamp_to_border = true; _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; _sg.limits.max_image_size_2d = 16 * 1024; _sg.limits.max_image_size_cube = 16 * 1024; @@ -9972,13 +10367,15 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, cons if (injected) { buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer; _sg_d3d11_AddRef(buf->d3d11.buf); + // FIXME: for storage buffers also need to inject resource view } else { - D3D11_BUFFER_DESC d3d11_desc; - _sg_clear(&d3d11_desc, sizeof(d3d11_desc)); - d3d11_desc.ByteWidth = (UINT)buf->cmn.size; - d3d11_desc.Usage = _sg_d3d11_usage(buf->cmn.usage); - d3d11_desc.BindFlags = buf->cmn.type == SG_BUFFERTYPE_VERTEXBUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER; - d3d11_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->cmn.usage); + D3D11_BUFFER_DESC d3d11_buf_desc; + _sg_clear(&d3d11_buf_desc, sizeof(d3d11_buf_desc)); + d3d11_buf_desc.ByteWidth = (UINT)buf->cmn.size; + d3d11_buf_desc.Usage = _sg_d3d11_usage(buf->cmn.usage); + d3d11_buf_desc.BindFlags = _sg_d3d11_buffer_bind_flags(buf->cmn.type); + d3d11_buf_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->cmn.usage); + d3d11_buf_desc.MiscFlags = _sg_d3d11_buffer_misc_flags(buf->cmn.type); D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; D3D11_SUBRESOURCE_DATA init_data; _sg_clear(&init_data, sizeof(init_data)); @@ -9987,11 +10384,31 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, cons init_data.pSysMem = desc->data.ptr; init_data_ptr = &init_data; } - HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11.buf); + HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_buf_desc, init_data_ptr, &buf->d3d11.buf); if (!(SUCCEEDED(hr) && buf->d3d11.buf)) { _SG_ERROR(D3D11_CREATE_BUFFER_FAILED); return SG_RESOURCESTATE_FAILED; } + + // for storage buffers need to create a view object + if (buf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER) { + // FIXME: currently only shader-resource-view, in future also UAV + // storage buffer size must be multiple of 4 + SOKOL_ASSERT(_sg_multiple_u64(buf->cmn.size, 4)); + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = DXGI_FORMAT_R32_TYPELESS; + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX; + d3d11_srv_desc.BufferEx.FirstElement = 0; + d3d11_srv_desc.BufferEx.NumElements = buf->cmn.size / 4; + d3d11_srv_desc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW; + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)buf->d3d11.buf, &d3d11_srv_desc, &buf->d3d11.srv); + if (!(SUCCEEDED(hr) && buf->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_BUFFER_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + _sg_d3d11_setlabel(buf->d3d11.buf, desc->label); } return SG_RESOURCESTATE_VALID; } @@ -10001,6 +10418,9 @@ _SOKOL_PRIVATE void _sg_d3d11_discard_buffer(_sg_buffer_t* buf) { if (buf->d3d11.buf) { _sg_d3d11_Release(buf->d3d11.buf); } + if (buf->d3d11.srv) { + _sg_d3d11_Release(buf->d3d11.srv); + } } _SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_data* data) { @@ -10098,6 +10518,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const _SG_ERROR(D3D11_CREATE_2D_TEXTURE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(img->d3d11.tex2d, desc->label); // create shader-resource-view for 2D texture // FIXME: currently we don't support setting MSAA texture as shader resource @@ -10127,6 +10548,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(img->d3d11.srv, desc->label); } } SOKOL_ASSERT(img->d3d11.tex2d); @@ -10168,6 +10590,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const _SG_ERROR(D3D11_CREATE_3D_TEXTURE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(img->d3d11.tex3d, desc->label); // create shader-resource-view for 3D texture if (!msaa) { @@ -10181,6 +10604,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const _SG_ERROR(D3D11_CREATE_3D_SRV_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(img->d3d11.srv, desc->label); } } SOKOL_ASSERT(img->d3d11.tex3d); @@ -10244,6 +10668,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_sampler(_sg_sampler_t* smp, co _SG_ERROR(D3D11_CREATE_SAMPLER_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(smp->d3d11.smp, desc->label); } return SG_RESOURCESTATE_VALID; } @@ -10338,6 +10763,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons _SG_ERROR(D3D11_CREATE_CONSTANT_BUFFER_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(d3d11_stage->cbufs[ub_index], desc->label); } } @@ -10376,6 +10802,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons SOKOL_ASSERT(shd->d3d11.vs_blob); memcpy(shd->d3d11.vs_blob, vs_ptr, vs_length); result = SG_RESOURCESTATE_VALID; + _sg_d3d11_setlabel(shd->d3d11.vs, desc->label); + _sg_d3d11_setlabel(shd->d3d11.fs, desc->label); } } if (vs_blob) { @@ -10457,15 +10885,18 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, pip->d3d11.vb_strides[layout_index] = 0; } } - hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev, - d3d11_comps, // pInputElementDesc - (UINT)attr_index, // NumElements - shd->d3d11.vs_blob, // pShaderByteCodeWithInputSignature - shd->d3d11.vs_blob_length, // BytecodeLength - &pip->d3d11.il); - if (!(SUCCEEDED(hr) && pip->d3d11.il)) { - _SG_ERROR(D3D11_CREATE_INPUT_LAYOUT_FAILED); - return SG_RESOURCESTATE_FAILED; + if (attr_index > 0) { + hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev, + d3d11_comps, // pInputElementDesc + (UINT)attr_index, // NumElements + shd->d3d11.vs_blob, // pShaderByteCodeWithInputSignature + shd->d3d11.vs_blob_length, // BytecodeLength + &pip->d3d11.il); + if (!(SUCCEEDED(hr) && pip->d3d11.il)) { + _SG_ERROR(D3D11_CREATE_INPUT_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.il, desc->label); } // create rasterizer state @@ -10486,6 +10917,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _SG_ERROR(D3D11_CREATE_RASTERIZER_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(pip->d3d11.rs, desc->label); // create depth-stencil state D3D11_DEPTH_STENCIL_DESC dss_desc; @@ -10511,6 +10943,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _SG_ERROR(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(pip->d3d11.dss, desc->label); // create blend state D3D11_BLEND_DESC bs_desc; @@ -10545,6 +10978,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _SG_ERROR(D3D11_CREATE_BLEND_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(pip->d3d11.bs, desc->label); return SG_RESOURCESTATE_VALID; } @@ -10640,6 +11074,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_attachments(_sg_attachments_t* _SG_ERROR(D3D11_CREATE_RTV_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(atts->d3d11.colors[i].view.rtv, desc->label); } SOKOL_ASSERT(0 == atts->d3d11.depth_stencil.view.dsv); if (ds_desc->image.id != SG_INVALID_ID) { @@ -10674,6 +11109,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_attachments(_sg_attachments_t* _SG_ERROR(D3D11_CREATE_DSV_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(atts->d3d11.depth_stencil.view.dsv, desc->label); } return SG_RESOURCESTATE_VALID; } @@ -10756,7 +11192,7 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(const sg_pass* pass) { // perform clear action for (int i = 0; i < num_rtvs; i++) { if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { - _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, rtvs[i], &action->colors[i].clear_value.r); + _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, rtvs[i], (float*)&action->colors[i].clear_value); _sg_stats_add(d3d11.pass.num_clear_render_target_view, 1); } } @@ -10864,7 +11300,7 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss && pip->d3d11.il); + SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss); _sg.d3d11.cur_pipeline = pip; _sg.d3d11.cur_pipeline_id.id = pip->slot.id; @@ -10873,7 +11309,7 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { _sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs); _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref); - _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, &pip->cmn.blend_color.r, 0xFFFFFFFF); + _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, (float*)&pip->cmn.blend_color, 0xFFFFFFFF); _sg_d3d11_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11.topology); _sg_d3d11_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11.il); _sg_d3d11_VSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.vs, NULL, 0); @@ -10900,8 +11336,8 @@ _SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { ID3D11Buffer* d3d11_ib = bnd->ib ? bnd->ib->d3d11.buf : 0; ID3D11Buffer* d3d11_vbs[SG_MAX_VERTEX_BUFFERS] = {0}; UINT d3d11_vb_offsets[SG_MAX_VERTEX_BUFFERS] = {0}; - ID3D11ShaderResourceView* d3d11_vs_srvs[SG_MAX_SHADERSTAGE_IMAGES] = {0}; - ID3D11ShaderResourceView* d3d11_fs_srvs[SG_MAX_SHADERSTAGE_IMAGES] = {0}; + ID3D11ShaderResourceView* d3d11_vs_srvs[_SG_D3D11_MAX_SHADERSTAGE_SRVS] = {0}; + ID3D11ShaderResourceView* d3d11_fs_srvs[_SG_D3D11_MAX_SHADERSTAGE_SRVS] = {0}; ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; for (int i = 0; i < bnd->num_vbs; i++) { @@ -10911,11 +11347,19 @@ _SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { } for (int i = 0; i < bnd->num_vs_imgs; i++) { SOKOL_ASSERT(bnd->vs_imgs[i]->d3d11.srv); - d3d11_vs_srvs[i] = bnd->vs_imgs[i]->d3d11.srv; + d3d11_vs_srvs[_SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET + i] = bnd->vs_imgs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + SOKOL_ASSERT(bnd->vs_sbufs[i]->d3d11.srv); + d3d11_vs_srvs[_SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET + i] = bnd->vs_sbufs[i]->d3d11.srv; } for (int i = 0; i < bnd->num_fs_imgs; i++) { SOKOL_ASSERT(bnd->fs_imgs[i]->d3d11.srv); - d3d11_fs_srvs[i] = bnd->fs_imgs[i]->d3d11.srv; + d3d11_fs_srvs[_SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET + i] = bnd->fs_imgs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + SOKOL_ASSERT(bnd->fs_sbufs[i]->d3d11.srv); + d3d11_fs_srvs[_SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET + i] = bnd->fs_sbufs[i]->d3d11.srv; } for (int i = 0; i < bnd->num_vs_smps; i++) { SOKOL_ASSERT(bnd->vs_smps[i]->d3d11.smp); @@ -10927,8 +11371,8 @@ _SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { } _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_VERTEX_BUFFERS, d3d11_vbs, bnd->pip->d3d11.vb_strides, d3d11_vb_offsets); _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, bnd->pip->d3d11.index_format, (UINT)bnd->ib_offset); - _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_srvs); - _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_srvs); + _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_SHADERSTAGE_SRVS, d3d11_vs_srvs); + _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_SHADERSTAGE_SRVS, d3d11_fs_srvs); _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_vs_smps); _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_fs_smps); _sg_stats_add(d3d11.bindings.num_ia_set_vertex_buffers, 1); @@ -10953,8 +11397,9 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub } _SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) { + const bool use_instanced_draw = (num_instances > 1) || (_sg.d3d11.use_instanced_draw); if (_sg.d3d11.use_indexed_draw) { - if (_sg.d3d11.use_instanced_draw) { + if (use_instanced_draw) { _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); _sg_stats_add(d3d11.draw.num_draw_indexed_instanced, 1); } else { @@ -10962,7 +11407,7 @@ _SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_i _sg_stats_add(d3d11.draw.num_draw_indexed, 1); } } else { - if (_sg.d3d11.use_instanced_draw) { + if (use_instanced_draw) { _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); _sg_stats_add(d3d11.draw.num_draw_instanced, 1); } else { @@ -11010,12 +11455,15 @@ _SOKOL_PRIVATE void _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* d } } +// see: https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-subresources +// also see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11calcsubresource _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) { SOKOL_ASSERT(img && data); SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(img->d3d11.res); const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; + const int num_depth_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? img->cmn.num_slices:1; UINT subres_index = 0; HRESULT hr; D3D11_MAPPED_SUBRESOURCE d3d11_msr; @@ -11025,26 +11473,35 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); - const int src_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const int src_row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const int src_depth_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); const size_t slice_size = subimg_data->size / (size_t)num_slices; + SOKOL_ASSERT(slice_size == (size_t)(src_depth_pitch * num_depth_slices)); const size_t slice_offset = slice_size * (size_t)slice_index; const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset; hr = _sg_d3d11_Map(_sg.d3d11.ctx, img->d3d11.res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); _sg_stats_add(d3d11.num_map, 1); if (SUCCEEDED(hr)) { - // FIXME: need to handle difference in depth-pitch for 3D textures as well! - if (src_pitch == (int)d3d11_msr.RowPitch) { - memcpy(d3d11_msr.pData, slice_ptr, slice_size); - } else { - SOKOL_ASSERT(src_pitch < (int)d3d11_msr.RowPitch); - const uint8_t* src_ptr = slice_ptr; - uint8_t* dst_ptr = (uint8_t*) d3d11_msr.pData; - for (int row_index = 0; row_index < mip_height; row_index++) { - memcpy(dst_ptr, src_ptr, (size_t)src_pitch); - src_ptr += src_pitch; - dst_ptr += d3d11_msr.RowPitch; + const uint8_t* src_ptr = slice_ptr; + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData; + for (int depth_index = 0; depth_index < num_depth_slices; depth_index++) { + if (src_row_pitch == (int)d3d11_msr.RowPitch) { + const size_t copy_size = slice_size / (size_t)num_depth_slices; + SOKOL_ASSERT((copy_size * (size_t)num_depth_slices) == slice_size); + memcpy(dst_ptr, src_ptr, copy_size); + } else { + SOKOL_ASSERT(src_row_pitch < (int)d3d11_msr.RowPitch); + const uint8_t* src_row_ptr = src_ptr; + uint8_t* dst_row_ptr = dst_ptr; + for (int row_index = 0; row_index < mip_height; row_index++) { + memcpy(dst_row_ptr, src_row_ptr, (size_t)src_row_pitch); + src_row_ptr += src_row_pitch; + dst_row_ptr += d3d11_msr.RowPitch; + } } + src_ptr += src_depth_pitch; + dst_ptr += d3d11_msr.DepthPitch; } _sg_d3d11_Unmap(_sg.d3d11.ctx, img->d3d11.res, subres_index); _sg_stats_add(d3d11.num_unmap, 1); @@ -11239,8 +11696,10 @@ _SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_ETC2_RGB8A1: return MTLPixelFormatETC2_RGB8A1; case SG_PIXELFORMAT_ETC2_RGBA8: return MTLPixelFormatEAC_RGBA8; case SG_PIXELFORMAT_ETC2_SRGB8A8: return MTLPixelFormatEAC_RGBA8_sRGB; - case SG_PIXELFORMAT_ETC2_RG11: return MTLPixelFormatEAC_RG11Unorm; - case SG_PIXELFORMAT_ETC2_RG11SN: return MTLPixelFormatEAC_RG11Snorm; + case SG_PIXELFORMAT_EAC_R11: return MTLPixelFormatEAC_R11Unorm; + case SG_PIXELFORMAT_EAC_R11SN: return MTLPixelFormatEAC_R11Snorm; + case SG_PIXELFORMAT_EAC_RG11: return MTLPixelFormatEAC_RG11Unorm; + case SG_PIXELFORMAT_EAC_RG11SN: return MTLPixelFormatEAC_RG11Snorm; case SG_PIXELFORMAT_ASTC_4x4_RGBA: return MTLPixelFormatASTC_4x4_LDR; case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return MTLPixelFormatASTC_4x4_sRGB; #endif @@ -11580,6 +12039,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg.features.origin_top_left = true; _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; _sg.features.image_clamp_to_border = false; #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) @@ -11713,8 +12173,10 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); @@ -12684,7 +13146,7 @@ _SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { } } - // apply vertex shader images + // apply vertex stage images for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_imgs; slot++) { const _sg_image_t* img = bnd->vs_imgs[slot]; if (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id) { @@ -12695,7 +13157,7 @@ _SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { } } - // apply vertex shader samplers + // apply vertex stage samplers for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_smps; slot++) { const _sg_sampler_t* smp = bnd->vs_smps[slot]; if (_sg.mtl.state_cache.cur_vs_sampler_ids[slot].id != smp->slot.id) { @@ -12706,7 +13168,19 @@ _SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { } } - // apply fragment shader images + // apply vertex stage storage buffers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_sbufs; slot++) { + const _sg_buffer_t* sbuf = bnd->vs_sbufs[slot]; + if (_sg.mtl.state_cache.cur_vs_storagebuffer_ids[slot].id != sbuf->slot.id) { + _sg.mtl.state_cache.cur_vs_storagebuffer_ids[slot].id = sbuf->slot.id; + SOKOL_ASSERT(sbuf->mtl.buf[sbuf->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + SG_MAX_VERTEX_BUFFERS + slot; + [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(sbuf->mtl.buf[sbuf->cmn.active_slot]) offset:0 atIndex:mtl_slot]; + _sg_stats_add(metal.bindings.num_set_vertex_buffer, 1); + } + } + + // apply fragment stage images for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_imgs; slot++) { const _sg_image_t* img = bnd->fs_imgs[slot]; if (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id) { @@ -12717,7 +13191,7 @@ _SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { } } - // apply fragment shader samplers + // apply fragment stage samplers for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_smps; slot++) { const _sg_sampler_t* smp = bnd->fs_smps[slot]; if (_sg.mtl.state_cache.cur_fs_sampler_ids[slot].id != smp->slot.id) { @@ -12727,6 +13201,19 @@ _SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { _sg_stats_add(metal.bindings.num_set_fragment_sampler_state, 1); } } + + // apply fragment stage storage buffers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_sbufs; slot++) { + const _sg_buffer_t* sbuf = bnd->fs_sbufs[slot]; + if (_sg.mtl.state_cache.cur_fs_storagebuffer_ids[slot].id != sbuf->slot.id) { + _sg.mtl.state_cache.cur_fs_storagebuffer_ids[slot].id = sbuf->slot.id; + SOKOL_ASSERT(sbuf->mtl.buf[sbuf->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; + [_sg.mtl.cmd_encoder setFragmentBuffer:_sg_mtl_id(sbuf->mtl.buf[sbuf->cmn.active_slot]) offset:0 atIndex:mtl_slot]; + _sg_stats_add(metal.bindings.num_set_fragment_buffer, 1); + } + } + return true; } @@ -12845,9 +13332,11 @@ _SOKOL_PRIVATE void _sg_mtl_pop_debug_group(void) { _SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_usage u) { WGPUBufferUsageFlags res = 0; if (SG_BUFFERTYPE_VERTEXBUFFER == t) { - res |= WGPUBufferUsage_Vertex; + res = WGPUBufferUsage_Vertex; + } else if (SG_BUFFERTYPE_STORAGEBUFFER == t) { + res = WGPUBufferUsage_Storage; } else { - res |= WGPUBufferUsage_Index; + res = WGPUBufferUsage_Index; } if (SG_USAGE_IMMUTABLE != u) { res |= WGPUBufferUsage_CopyDst; @@ -13091,8 +13580,10 @@ _SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { case SG_PIXELFORMAT_ETC2_RGBA8: return WGPUTextureFormat_ETC2RGBA8Unorm; case SG_PIXELFORMAT_ETC2_SRGB8: return WGPUTextureFormat_ETC2RGB8UnormSrgb; case SG_PIXELFORMAT_ETC2_SRGB8A8: return WGPUTextureFormat_ETC2RGBA8UnormSrgb; - case SG_PIXELFORMAT_ETC2_RG11: return WGPUTextureFormat_EACR11Unorm; - case SG_PIXELFORMAT_ETC2_RG11SN: return WGPUTextureFormat_EACR11Snorm; + case SG_PIXELFORMAT_EAC_R11: return WGPUTextureFormat_EACR11Unorm; + case SG_PIXELFORMAT_EAC_R11SN: return WGPUTextureFormat_EACR11Snorm; + case SG_PIXELFORMAT_EAC_RG11: return WGPUTextureFormat_EACRG11Unorm; + case SG_PIXELFORMAT_EAC_RG11SN: return WGPUTextureFormat_EACRG11Snorm; case SG_PIXELFORMAT_RGB9E5: return WGPUTextureFormat_RGB9E5Ufloat; case SG_PIXELFORMAT_ASTC_4x4_RGBA: return WGPUTextureFormat_ASTC4x4Unorm; case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return WGPUTextureFormat_ASTC4x4UnormSrgb; @@ -13204,15 +13695,17 @@ _SOKOL_PRIVATE WGPUColorWriteMaskFlags _sg_wgpu_colorwritemask(uint8_t m) { // - all images and sampler are in @group(1) // - vertex stage images start at @binding(0) // - vertex stage samplers start at @binding(16) -// - fragment stage images start at @binding(32) -// - fragment stage samplers start at @binding(48) +// - vertex stage storage buffers start at @binding(32) +// - fragment stage images start at @binding(48) +// - fragment stage samplers start at @binding(64) +// - fragment stage storage buffers start at @binding(80) // _SOKOL_PRIVATE uint32_t _sg_wgpu_image_binding(sg_shader_stage stage, int img_slot) { SOKOL_ASSERT((img_slot >= 0) && (img_slot < 16)); if (SG_SHADERSTAGE_VS == stage) { return 0 + (uint32_t)img_slot; } else { - return 32 + (uint32_t)img_slot; + return 48 + (uint32_t)img_slot; } } @@ -13221,7 +13714,16 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_sampler_binding(sg_shader_stage stage, int smp_ if (SG_SHADERSTAGE_VS == stage) { return 16 + (uint32_t)smp_slot; } else { - return 48 + (uint32_t)smp_slot; + return 64 + (uint32_t)smp_slot; + } +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_storagebuffer_binding(sg_shader_stage stage, int sbuf_slot) { + SOKOL_ASSERT((sbuf_slot >= 0) && (sbuf_slot < 16)); + if (SG_SHADERSTAGE_VS == stage) { + return 32 + (uint32_t)sbuf_slot; + } else { + return 80 + (uint32_t)sbuf_slot; } } @@ -13239,6 +13741,7 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg.features.image_clamp_to_border = false; _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; wgpuDeviceGetLimits(_sg.wgpu.dev, &_sg.wgpu.limits); @@ -13289,10 +13792,15 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); - // FIXME: can be made filterable with extension - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_Float32Filterable)) { + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); @@ -13319,8 +13827,10 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); } if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionASTC)) { @@ -13530,16 +14040,20 @@ _SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache SOKOL_ASSERT(bnd->pip); SOKOL_ASSERT(bnd->num_vs_imgs <= SG_MAX_SHADERSTAGE_IMAGES); SOKOL_ASSERT(bnd->num_vs_smps <= SG_MAX_SHADERSTAGE_SAMPLERS); + SOKOL_ASSERT(bnd->num_vs_sbufs <= SG_MAX_SHADERSTAGE_STORAGEBUFFERS); SOKOL_ASSERT(bnd->num_fs_imgs <= SG_MAX_SHADERSTAGE_IMAGES); SOKOL_ASSERT(bnd->num_fs_smps <= SG_MAX_SHADERSTAGE_SAMPLERS); + SOKOL_ASSERT(bnd->num_fs_sbufs <= SG_MAX_SHADERSTAGE_STORAGEBUFFERS); _sg_clear(key->items, sizeof(key->items)); key->items[0] = bnd->pip->slot.id; const int vs_imgs_offset = 1; const int vs_smps_offset = vs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; - const int fs_imgs_offset = vs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS; + const int vs_sbufs_offset = vs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS; + const int fs_imgs_offset = vs_sbufs_offset + SG_MAX_SHADERSTAGE_STORAGEBUFFERS; const int fs_smps_offset = fs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; - SOKOL_ASSERT((fs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS) == _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS); + const int fs_sbufs_offset = fs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS; + SOKOL_ASSERT((fs_sbufs_offset + SG_MAX_SHADERSTAGE_STORAGEBUFFERS) == _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS); for (int i = 0; i < bnd->num_vs_imgs; i++) { SOKOL_ASSERT(bnd->vs_imgs[i]); key->items[vs_imgs_offset + i] = bnd->vs_imgs[i]->slot.id; @@ -13548,6 +14062,10 @@ _SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache SOKOL_ASSERT(bnd->vs_smps[i]); key->items[vs_smps_offset + i] = bnd->vs_smps[i]->slot.id; } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + SOKOL_ASSERT(bnd->vs_sbufs[i]); + key->items[vs_sbufs_offset + i] = bnd->vs_sbufs[i]->slot.id; + } for (int i = 0; i < bnd->num_fs_imgs; i++) { SOKOL_ASSERT(bnd->fs_imgs[i]); key->items[fs_imgs_offset + i] = bnd->fs_imgs[i]->slot.id; @@ -13556,6 +14074,10 @@ _SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache SOKOL_ASSERT(bnd->fs_smps[i]); key->items[fs_smps_offset + i] = bnd->fs_smps[i]->slot.id; } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + SOKOL_ASSERT(bnd->fs_sbufs[i]); + key->items[fs_sbufs_offset + i] = bnd->fs_sbufs[i]->slot.id; + } key->hash = _sg_wgpu_hash(&key->items, (int)sizeof(key->items), 0x1234567887654321); } @@ -13586,7 +14108,7 @@ _SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_create_bindgroup(_sg_bindings_t* b // create wgpu bindgroup object WGPUBindGroupLayout bgl = bnd->pip->shader->wgpu.bind_group_layout; SOKOL_ASSERT(bgl); - WGPUBindGroupEntry wgpu_entries[SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS]; + WGPUBindGroupEntry wgpu_entries[_SG_WGPU_MAX_BINDGROUP_ENTRIES]; _sg_clear(&wgpu_entries, sizeof(wgpu_entries)); int bge_index = 0; for (int i = 0; i < bnd->num_vs_imgs; i++) { @@ -13599,6 +14121,12 @@ _SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_create_bindgroup(_sg_bindings_t* b wgpu_entry->binding = _sg_wgpu_sampler_binding(SG_SHADERSTAGE_VS, i); wgpu_entry->sampler = bnd->vs_smps[i]->wgpu.smp; } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_storagebuffer_binding(SG_SHADERSTAGE_VS, i); + wgpu_entry->buffer = bnd->vs_sbufs[i]->wgpu.buf; + wgpu_entry->size = (uint64_t) bnd->vs_sbufs[i]->cmn.size; + } for (int i = 0; i < bnd->num_fs_imgs; i++) { WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; wgpu_entry->binding = _sg_wgpu_image_binding(SG_SHADERSTAGE_FS, i); @@ -13609,6 +14137,12 @@ _SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_create_bindgroup(_sg_bindings_t* b wgpu_entry->binding = _sg_wgpu_sampler_binding(SG_SHADERSTAGE_FS, i); wgpu_entry->sampler = bnd->fs_smps[i]->wgpu.smp; } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_storagebuffer_binding(SG_SHADERSTAGE_FS, i); + wgpu_entry->buffer = bnd->fs_sbufs[i]->wgpu.buf; + wgpu_entry->size = (uint64_t) bnd->fs_sbufs[i]->cmn.size; + } WGPUBindGroupDescriptor bg_desc; _sg_clear(&bg_desc, sizeof(bg_desc)); bg_desc.layout = bgl; @@ -13755,7 +14289,7 @@ _SOKOL_PRIVATE void _sg_wgpu_bindings_cache_bg_update(const _sg_wgpu_bindgroup_t } } -_SOKOL_PRIVATE void _sg_wgpu_set_image_sampler_bindgroup(_sg_wgpu_bindgroup_t* bg) { +_SOKOL_PRIVATE void _sg_wgpu_set_bindings_bindgroup(_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); @@ -13773,7 +14307,7 @@ _SOKOL_PRIVATE void _sg_wgpu_set_image_sampler_bindgroup(_sg_wgpu_bindgroup_t* b } _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { - if ((bnd->num_vs_imgs + bnd->num_vs_smps + bnd->num_fs_imgs + bnd->num_fs_smps) > 0) { + if ((bnd->num_vs_imgs + bnd->num_vs_smps + bnd->num_vs_sbufs + bnd->num_fs_imgs + bnd->num_fs_smps + bnd->num_fs_sbufs) > 0) { if (!_sg.desc.wgpu_disable_bindgroups_cache) { _sg_wgpu_bindgroup_t* bg = 0; _sg_wgpu_bindgroups_cache_key_t key; @@ -13801,7 +14335,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { _sg_wgpu_bindgroups_cache_set(key.hash, bg->slot.id); } if (bg && bg->slot.state == SG_RESOURCESTATE_VALID) { - _sg_wgpu_set_image_sampler_bindgroup(bg); + _sg_wgpu_set_bindings_bindgroup(bg); } else { return false; } @@ -13810,7 +14344,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { _sg_wgpu_bindgroup_t* bg = _sg_wgpu_create_bindgroup(bnd); if (bg) { if (bg->slot.state == SG_RESOURCESTATE_VALID) { - _sg_wgpu_set_image_sampler_bindgroup(bg); + _sg_wgpu_set_bindings_bindgroup(bg); } _sg_wgpu_discard_bindgroup(bg); } else { @@ -13818,10 +14352,11 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { } } } else { - _sg_wgpu_set_image_sampler_bindgroup(0); + _sg_wgpu_set_bindings_bindgroup(0); } return true; } + _SOKOL_PRIVATE bool _sg_wgpu_apply_index_buffer(_sg_bindings_t* bnd) { if (_sg_wgpu_bindings_cache_ib_dirty(bnd->ib, bnd->ib_offset)) { _sg_wgpu_bindings_cache_ib_update(bnd->ib, bnd->ib_offset); @@ -14143,8 +14678,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const SOKOL_ASSERT(shd && desc); SOKOL_ASSERT(desc->vs.source && desc->fs.source); - #define _sg_wgpu_create_shader_max_bgl_entries (SG_NUM_SHADER_STAGES * (SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS)) - WGPUBindGroupLayoutEntry wgpu_bgl_entries[_sg_wgpu_create_shader_max_bgl_entries]; + WGPUBindGroupLayoutEntry wgpu_bgl_entries[_SG_WGPU_MAX_BINDGROUP_ENTRIES]; _sg_clear(wgpu_bgl_entries, sizeof(wgpu_bgl_entries)); int bgl_index = 0; for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { @@ -14180,8 +14714,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const _SG_ERROR(WGPU_SHADER_TOO_MANY_SAMPLERS); return SG_RESOURCESTATE_FAILED; } + const int num_sbufs = cmn_stage->num_storage_buffers; + if (num_sbufs > (int)_sg.wgpu.limits.limits.maxStorageBuffersPerShaderStage) { + _SG_ERROR(WGPU_SHADER_TOO_MANY_STORAGEBUFFERS); + return SG_RESOURCESTATE_FAILED; + } for (int img_index = 0; img_index < num_images; img_index++) { - SOKOL_ASSERT(bgl_index < _sg_wgpu_create_shader_max_bgl_entries); + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; wgpu_bgl_entry->binding = _sg_wgpu_image_binding((sg_shader_stage)stage_index, img_index); @@ -14191,13 +14730,21 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const wgpu_bgl_entry->texture.multisampled = img_desc->multisampled; } for (int smp_index = 0; smp_index < num_samplers; smp_index++) { - SOKOL_ASSERT(bgl_index < _sg_wgpu_create_shader_max_bgl_entries); + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; - wgpu_bgl_entry->binding =_sg_wgpu_sampler_binding((sg_shader_stage)stage_index, smp_index); + wgpu_bgl_entry->binding = _sg_wgpu_sampler_binding((sg_shader_stage)stage_index, smp_index); wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); wgpu_bgl_entry->sampler.type = _sg_wgpu_sampler_binding_type(smp_desc->sampler_type); } + for (int sbuf_index = 0; sbuf_index < num_sbufs; sbuf_index++) { + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); + WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + wgpu_bgl_entry->binding = _sg_wgpu_storagebuffer_binding((sg_shader_stage)stage_index, sbuf_index); + wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); + wgpu_bgl_entry->buffer.type = sbuf_desc->readonly ? WGPUBufferBindingType_ReadOnlyStorage : WGPUBufferBindingType_Storage; + } } WGPUBindGroupLayoutDescriptor wgpu_bgl_desc; @@ -14209,8 +14756,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const _SG_ERROR(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED); return SG_RESOURCESTATE_FAILED; } - - #undef _sg_wgpu_create_shader_max_bgl_entries return SG_RESOURCESTATE_VALID; } @@ -14592,7 +15137,7 @@ _SOKOL_PRIVATE void _sg_wgpu_commit(void) { _SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.wgpu.pass_enc); - // FIXME: CLIPPING THE VIEWPORT HERE IS WRONG!!! + // FIXME FIXME FIXME: CLIPPING THE VIEWPORT HERE IS WRONG!!! // (but currently required because WebGPU insists that the viewport rectangle must be // fully contained inside the framebuffer, but this doesn't make any sense, and also // isn't required by the backend APIs) @@ -15588,6 +16133,10 @@ _SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { } else { _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_BUFFERDESC_NO_DATA); } + if (desc->type == SG_BUFFERTYPE_STORAGEBUFFER) { + _SG_VALIDATE(_sg.features.storage_buffer, VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED); + _SG_VALIDATE(_sg_multiple_u64(desc->size, 4), VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4); + } return _sg_validate_end(); #endif } @@ -15725,10 +16274,7 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { _sg_validate_begin(); _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SHADERDESC_CANARY); _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SHADERDESC_CANARY); - #if defined(SOKOL_D3D11) - _SG_VALIDATE(0 != desc->attrs[0].sem_name, VALIDATE_SHADERDESC_ATTR_SEMANTICS); - #endif - #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) || defined(SOKOL_WGPU) + #if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) || defined(SOKOL_WGPU) // on GL or WebGPU, must provide shader source code _SG_VALIDATE(0 != desc->vs.source, VALIDATE_SHADERDESC_SOURCE); _SG_VALIDATE(0 != desc->fs.source, VALIDATE_SHADERDESC_SOURCE); @@ -15799,6 +16345,16 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { uniform_blocks_continuous = false; } } + bool storage_buffers_continuous = true; + for (int sbuf_index = 0; sbuf_index < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; sbuf_index++) { + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + if (sbuf_desc->used) { + _SG_VALIDATE(storage_buffers_continuous, VALIDATE_SHADERDESC_NO_CONT_STORAGEBUFFERS); + _SG_VALIDATE(sbuf_desc->readonly, VALIDATE_SHADERDESC_STORAGEBUFFER_READONLY); + } else { + storage_buffers_continuous = false; + } + } bool images_continuous = true; int num_images = 0; for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { @@ -15893,7 +16449,6 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { } _SG_VALIDATE(_sg_multiple_u64((uint64_t)l_state->stride, 4), VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); } - _SG_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, VALIDATE_PIPELINEDESC_NO_ATTRS); const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); _SG_VALIDATE(0 != shd, VALIDATE_PIPELINEDESC_SHADER); if (shd) { @@ -15905,7 +16460,7 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { attrs_cont = false; continue; } - _SG_VALIDATE(attrs_cont, VALIDATE_PIPELINEDESC_NO_ATTRS); + _SG_VALIDATE(attrs_cont, VALIDATE_PIPELINEDESC_NO_CONT_ATTRS); SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); #if defined(SOKOL_D3D11) // on D3D11, semantic names (and semantic indices) must be provided @@ -16292,6 +16847,23 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { } } + // has expected vertex shader storage buffers + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->storage_buffers[i].used) { + _SG_VALIDATE(bindings->vs.storage_buffers[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_STORAGEBUFFER_BINDING); + if (bindings->vs.storage_buffers[i].id != SG_INVALID_ID) { + const _sg_buffer_t* sbuf = _sg_lookup_buffer(&_sg.pools, bindings->vs.storage_buffers[i].id); + _SG_VALIDATE(sbuf != 0, VALIDATE_ABND_VS_STORAGEBUFFER_EXISTS); + if (sbuf) { + _SG_VALIDATE(sbuf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER, VALIDATE_ABND_VS_STORAGEBUFFER_BINDING_BUFFERTYPE); + } + } + } else { + _SG_VALIDATE(bindings->vs.storage_buffers[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_STORAGEBUFFER_BINDING); + } + } + // has expected fragment shader images for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; @@ -16347,6 +16919,24 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { _SG_VALIDATE(bindings->fs.samplers[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING); } } + + // has expected fragment shader storage buffers + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (stage->storage_buffers[i].used) { + _SG_VALIDATE(bindings->fs.storage_buffers[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_STORAGEBUFFER_BINDING); + if (bindings->fs.storage_buffers[i].id != SG_INVALID_ID) { + const _sg_buffer_t* sbuf = _sg_lookup_buffer(&_sg.pools, bindings->fs.storage_buffers[i].id); + _SG_VALIDATE(sbuf != 0, VALIDATE_ABND_FS_STORAGEBUFFER_EXISTS); + if (sbuf) { + _SG_VALIDATE(sbuf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER, VALIDATE_ABND_FS_STORAGEBUFFER_BINDING_BUFFERTYPE); + } + } + } else { + _SG_VALIDATE(bindings->fs.storage_buffers[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_STORAGEBUFFER_BINDING); + } + } + return _sg_validate_end(); #endif } @@ -17786,7 +18376,6 @@ SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { SOKOL_ASSERT(_sg.valid); SOKOL_ASSERT(_sg.cur_pass.in_pass); _sg_stats_add(num_apply_pipeline, 1); - _sg.apply_bindings_called = false; if (!_sg_validate_apply_pipeline(pip_id)) { _sg.next_draw_valid = false; return; @@ -17809,7 +18398,6 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { SOKOL_ASSERT(bindings); SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); _sg_stats_add(num_apply_bindings, 1); - _sg.apply_bindings_called = true; if (!_sg_validate_apply_bindings(bindings)) { _sg.next_draw_valid = false; return; @@ -17877,6 +18465,19 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { } } + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++, bnd.num_vs_sbufs++) { + if (bindings->vs.storage_buffers[i].id) { + bnd.vs_sbufs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vs.storage_buffers[i].id); + if (bnd.vs_sbufs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vs_sbufs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, bnd.num_fs_imgs++) { if (bindings->fs.images[i].id) { bnd.fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); @@ -17903,6 +18504,18 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { } } + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++, bnd.num_fs_sbufs++) { + if (bindings->fs.storage_buffers[i].id) { + bnd.fs_sbufs[i] = _sg_lookup_buffer(&_sg.pools, bindings->fs.storage_buffers[i].id); + if (bnd.fs_sbufs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.fs_sbufs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } if (_sg.next_draw_valid) { _sg.next_draw_valid &= _sg_apply_bindings(&bnd); _SG_TRACE_ARGS(apply_bindings, bindings); @@ -17938,20 +18551,12 @@ SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instance SOKOL_ASSERT(num_elements >= 0); SOKOL_ASSERT(num_instances >= 0); _sg_stats_add(num_draw, 1); - #if defined(SOKOL_DEBUG) - if (!_sg.apply_bindings_called) { - _SG_WARN(DRAW_WITHOUT_BINDINGS); - } - #endif if (!_sg.cur_pass.valid) { return; } if (!_sg.next_draw_valid) { return; } - if (!_sg.apply_bindings_called) { - return; - } /* attempting to draw with zero elements or instances is not technically an error, but might be handled as an error in the backend API (e.g. on Metal) */ diff --git a/thirdparty/sokol/sokol_v.post.h b/thirdparty/sokol/sokol_v.post.h index 4e0110d468..be34073f1e 100644 --- a/thirdparty/sokol/sokol_v.post.h +++ b/thirdparty/sokol/sokol_v.post.h @@ -1,4 +1,4 @@ -#if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +#if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) void v_sapp_gl_read_rgba_pixels(int x, int y, int width, int height, unsigned char* pixels) { glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); } diff --git a/thirdparty/sokol/util/sokol_fontstash.h b/thirdparty/sokol/util/sokol_fontstash.h index 048c821555..a650585769 100644 --- a/thirdparty/sokol/util/sokol_fontstash.h +++ b/thirdparty/sokol/util/sokol_fontstash.h @@ -19,7 +19,7 @@ platform-specific embedded shader code (these are the same defines as used by sokol_gfx.h and sokol_app.h): - SOKOL_GLCORE33 + SOKOL_GLCORE SOKOL_GLES3 SOKOL_D3D11 SOKOL_METAL @@ -278,11 +278,11 @@ SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, ui #define _SOKOL_UNUSED(x) (void)(x) #endif -#if defined(SOKOL_GLCORE33) +#if defined(SOKOL_GLCORE) /* Embedded source code compiled with: - sokol-shdc -i sfons.glsl -o sfons.h -l glsl330:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl -b + sokol-shdc -i sfons.glsl -o sfons.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl -b (not that for Metal and D3D11 byte code, sokol-shdc must be run on macOS and Windows) @@ -321,55 +321,61 @@ SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, ui @program sfontstash vs fs */ -static const char _sfons_vs_source_glsl330[478] = { - 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, +static const uint8_t _sfons_vs_source_glsl410[520] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, 0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, 0x3d,0x20,0x33,0x29,0x20,0x69,0x6e,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x70,0x73, - 0x69,0x7a,0x65,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76, - 0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, - 0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74, - 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65, - 0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, - 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69, - 0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a, - 0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20, - 0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, - 0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30, - 0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c, - 0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76, - 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70, - 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, - 0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a, - 0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34, - 0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76, - 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f, - 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61, - 0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28, - 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20, - 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20, - 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + 0x69,0x7a,0x65,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65, + 0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f, + 0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76, + 0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6c, + 0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x31,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53, + 0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, + 0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x37, + 0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; -static const char _sfons_fs_source_glsl330[203] = { - 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, +static const uint8_t _sfons_fs_source_glsl410[245] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, - 0x6f,0x72,0x3b,0x0a,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a, - 0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a, - 0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20, - 0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76, - 0x65,0x63,0x34,0x28,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e, - 0x30,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73, - 0x6d,0x70,0x2c,0x20,0x75,0x76,0x2e,0x78,0x79,0x29,0x2e,0x78,0x29,0x20,0x2a,0x20, - 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34, + 0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x31,0x2e, + 0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76, + 0x2e,0x78,0x79,0x29,0x2e,0x78,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x7d,0x0a,0x0a,0x00, }; #elif defined(SOKOL_GLES3) -static const char _sfons_vs_source_glsl300es[481] = { +static const uint8_t _sfons_vs_source_glsl300es[481] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, 0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73, 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f, @@ -402,7 +408,7 @@ static const char _sfons_vs_source_glsl300es[481] = { 0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a, 0x00, }; -static const char _sfons_fs_source_glsl300es[276] = { +static const uint8_t _sfons_fs_source_glsl300es[276] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, @@ -1204,7 +1210,7 @@ static const uint8_t _sfons_fs_bytecode_metal_ios[2841] = { 0xc8,0x70,0x04,0x8d,0x05,0x91,0x7c,0x66,0x1b,0x94,0x00,0xc8,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; -static const char _sfons_vs_source_metal_sim[756] = { +static const uint8_t _sfons_vs_source_metal_sim[756] = { 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, @@ -1254,7 +1260,7 @@ static const char _sfons_vs_source_metal_sim[756] = { 0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a, 0x7d,0x0a,0x0a,0x00, }; -static const char _sfons_fs_source_metal_sim[464] = { +static const uint8_t _sfons_fs_source_metal_sim[464] = { 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, @@ -1397,7 +1403,7 @@ static const uint8_t _sfons_fs_bytecode_hlsl4[628] = { 0x00,0x00,0x00,0x00, }; #elif defined(SOKOL_WGPU) -static const char _sfons_vs_source_wgsl[1162] = { +static const uint8_t _sfons_vs_source_wgsl[1162] = { 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, @@ -1472,17 +1478,17 @@ static const char _sfons_vs_source_wgsl[1162] = { 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63,0x6f, 0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; -static const char _sfons_fs_source_wgsl[674] = { +static const uint8_t _sfons_fs_source_wgsl[674] = { 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75, - 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x34,0x38, 0x29,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74, 0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x67, 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, - 0x28,0x34,0x38,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73, + 0x28,0x36,0x34,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73, 0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, 0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, @@ -1521,7 +1527,7 @@ static const char _sfons_fs_source_wgsl[674] = { static const char* _sfons_vs_source_dummy = ""; static const char* _sfons_fs_source_dummy = ""; #else -#error "Please define one of SOKOL_GLCORE33, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" #endif typedef struct _sfons_t { @@ -1601,12 +1607,12 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { shd_desc.fs.image_sampler_pairs[0].image_slot = 0; shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; shd_desc.label = "sokol-fontstash-shader"; - #if defined(SOKOL_GLCORE33) - shd_desc.vs.source = _sfons_vs_source_glsl330; - shd_desc.fs.source = _sfons_fs_source_glsl330; + #if defined(SOKOL_GLCORE) + shd_desc.vs.source = (const char*)_sfons_vs_source_glsl410; + shd_desc.fs.source = (const char*)_sfons_fs_source_glsl410; #elif defined(SOKOL_GLES3) - shd_desc.vs.source = _sfons_vs_source_glsl300es; - shd_desc.fs.source = _sfons_fs_source_glsl300es; + shd_desc.vs.source = (const char*)_sfons_vs_source_glsl300es; + shd_desc.fs.source = (const char*)_sfons_fs_source_glsl300es; #elif defined(SOKOL_METAL) shd_desc.vs.entry = "main0"; shd_desc.fs.entry = "main0"; @@ -1620,16 +1626,16 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { shd_desc.fs.bytecode = SG_RANGE(_sfons_fs_bytecode_metal_ios); break; default: - shd_desc.vs.source = _sfons_vs_source_metal_sim; - shd_desc.fs.source = _sfons_fs_source_metal_sim; + shd_desc.vs.source = (const char*)_sfons_vs_source_metal_sim; + shd_desc.fs.source = (const char*)_sfons_fs_source_metal_sim; break; } #elif defined(SOKOL_D3D11) shd_desc.vs.bytecode = SG_RANGE(_sfons_vs_bytecode_hlsl4); shd_desc.fs.bytecode = SG_RANGE(_sfons_fs_bytecode_hlsl4); #elif defined(SOKOL_WGPU) - shd_desc.vs.source = _sfons_vs_source_wgsl; - shd_desc.fs.source = _sfons_fs_source_wgsl; + shd_desc.vs.source = (const char*)_sfons_vs_source_wgsl; + shd_desc.fs.source = (const char*)_sfons_fs_source_wgsl; #else shd_desc.vs.source = _sfons_vs_source_dummy; shd_desc.fs.source = _sfons_fs_source_dummy; diff --git a/thirdparty/sokol/util/sokol_gl.h b/thirdparty/sokol/util/sokol_gl.h index f72f0b103f..9b7da3ee98 100644 --- a/thirdparty/sokol/util/sokol_gl.h +++ b/thirdparty/sokol/util/sokol_gl.h @@ -17,7 +17,7 @@ platform-specific embedded shader code (these are the same defines as used by sokol_gfx.h and sokol_app.h): - SOKOL_GLCORE33 + SOKOL_GLCORE SOKOL_GLES3 SOKOL_D3D11 SOKOL_METAL @@ -973,7 +973,7 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline /* Embedded source code compiled with: - sokol-shdc -i sgl.glsl -o sgl.h -l glsl330:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgpu -b + sokol-shdc -i sgl.glsl -o sgl.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgpu -b (not that for Metal and D3D11 byte code, sokol-shdc must be run on macOS and Windows) @@ -1013,55 +1013,60 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline @program sgl vs fs */ -#if defined(SOKOL_GLCORE33) -static const char _sgl_vs_source_glsl330[478] = { - 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, +#if defined(SOKOL_GLCORE) +static const uint8_t _sgl_vs_source_glsl410[520] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, 0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, 0x3d,0x20,0x33,0x29,0x20,0x69,0x6e,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x70,0x73, - 0x69,0x7a,0x65,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76, - 0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, - 0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74, - 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65, - 0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, - 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69, - 0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a, - 0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20, - 0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, - 0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30, - 0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c, - 0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76, - 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70, - 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, - 0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a, - 0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34, - 0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76, - 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f, - 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61, - 0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28, - 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20, - 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20, - 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + 0x69,0x7a,0x65,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65, + 0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f, + 0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76, + 0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6c, + 0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x31,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53, + 0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, + 0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x37, + 0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; -static const char _sgl_fs_source_glsl330[180] = { - 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, +static const uint8_t _sgl_fs_source_glsl410[222] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, - 0x6f,0x72,0x3b,0x0a,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a, - 0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a, - 0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20, - 0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74, - 0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20, - 0x75,0x76,0x2e,0x78,0x79,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, - 0x7d,0x0a,0x0a,0x00, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34, + 0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x2e,0x78,0x79,0x29, + 0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; #elif defined(SOKOL_GLES3) -static const char _sgl_vs_source_glsl300es[481] = { +static const uint8_t _sgl_vs_source_glsl300es[481] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, 0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73, 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f, @@ -1094,7 +1099,7 @@ static const char _sgl_vs_source_glsl300es[481] = { 0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a, 0x00, }; -static const char _sgl_fs_source_glsl300es[253] = { +static const uint8_t _sgl_fs_source_glsl300es[253] = { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, @@ -1890,7 +1895,7 @@ static const uint8_t _sgl_fs_bytecode_metal_ios[2809] = { 0x42,0x40,0x29,0x49,0x50,0x20,0x86,0x60,0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; -static const char _sgl_vs_source_metal_sim[756] = { +static const uint8_t _sgl_vs_source_metal_sim[756] = { 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, @@ -1940,7 +1945,7 @@ static const char _sgl_vs_source_metal_sim[756] = { 0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a, 0x7d,0x0a,0x0a,0x00, }; -static const char _sgl_fs_source_metal_sim[439] = { +static const uint8_t _sgl_fs_source_metal_sim[439] = { 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, @@ -2080,7 +2085,7 @@ static const uint8_t _sgl_fs_bytecode_hlsl4[608] = { }; #elif defined(SOKOL_WGPU) -static const char _sgl_vs_source_wgsl[1162] = { +static const uint8_t _sgl_vs_source_wgsl[1162] = { 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, @@ -2155,17 +2160,17 @@ static const char _sgl_vs_source_wgsl[1162] = { 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63,0x6f, 0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; -static const char _sgl_fs_source_wgsl[647] = { +static const uint8_t _sgl_fs_source_wgsl[647] = { 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75, - 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x33,0x32, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x34,0x38, 0x29,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74, 0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x67, 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, - 0x28,0x34,0x38,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73, + 0x28,0x36,0x34,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73, 0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, 0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, @@ -2202,7 +2207,7 @@ static const char _sgl_fs_source_wgsl[647] = { static const char* _sgl_vs_source_dummy = ""; static const char* _sgl_fs_source_dummy = ""; #else -#error "Please define one of SOKOL_GLCORE33, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" #endif // ████████ ██ ██ ██████ ███████ ███████ @@ -3286,12 +3291,12 @@ static void _sgl_setup_common(void) { shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; shd_desc.fs.image_sampler_pairs[0].glsl_name = "tex_smp"; shd_desc.label = "sgl-shader"; - #if defined(SOKOL_GLCORE33) - shd_desc.vs.source = _sgl_vs_source_glsl330; - shd_desc.fs.source = _sgl_fs_source_glsl330; + #if defined(SOKOL_GLCORE) + shd_desc.vs.source = (const char*)_sgl_vs_source_glsl410; + shd_desc.fs.source = (const char*)_sgl_fs_source_glsl410; #elif defined(SOKOL_GLES3) - shd_desc.vs.source = _sgl_vs_source_glsl300es; - shd_desc.fs.source = _sgl_fs_source_glsl300es; + shd_desc.vs.source = (const char*)_sgl_vs_source_glsl300es; + shd_desc.fs.source = (const char*)_sgl_fs_source_glsl300es; #elif defined(SOKOL_METAL) shd_desc.vs.entry = "main0"; shd_desc.fs.entry = "main0"; @@ -3305,16 +3310,16 @@ static void _sgl_setup_common(void) { shd_desc.fs.bytecode = SG_RANGE(_sgl_fs_bytecode_metal_ios); break; default: - shd_desc.vs.source = _sgl_vs_source_metal_sim; - shd_desc.fs.source = _sgl_fs_source_metal_sim; + shd_desc.vs.source = (const char*)_sgl_vs_source_metal_sim; + shd_desc.fs.source = (const char*)_sgl_fs_source_metal_sim; break; } #elif defined(SOKOL_D3D11) shd_desc.vs.bytecode = SG_RANGE(_sgl_vs_bytecode_hlsl4); shd_desc.fs.bytecode = SG_RANGE(_sgl_fs_bytecode_hlsl4); #elif defined(SOKOL_WGPU) - shd_desc.vs.source = _sgl_vs_source_wgsl; - shd_desc.fs.source = _sgl_fs_source_wgsl; + shd_desc.vs.source = (const char*)_sgl_vs_source_wgsl; + shd_desc.fs.source = (const char*)_sgl_fs_source_wgsl; #else shd_desc.vs.source = _sgl_vs_source_dummy; shd_desc.fs.source = _sgl_fs_source_dummy; diff --git a/vlib/sokol/c/declaration.c.v b/vlib/sokol/c/declaration.c.v index a54668cea2..9160dac2fa 100644 --- a/vlib/sokol/c/declaration.c.v +++ b/vlib/sokol/c/declaration.c.v @@ -26,7 +26,7 @@ $if !tinyc { // METAL $if macos { $if darwin_sokol_glcore33 ? { - #flag darwin -DSOKOL_GLCORE33 -framework OpenGL -framework Cocoa -framework QuartzCore + #flag darwin -DSOKOL_GLCORE -framework OpenGL -framework Cocoa -framework QuartzCore } $else { #flag -DSOKOL_METAL #flag -framework Metal -framework Cocoa -framework MetalKit -framework QuartzCore @@ -47,12 +47,12 @@ $if emscripten ? { } // OPENGL -#flag linux -DSOKOL_GLCORE33 -#flag freebsd -DSOKOL_GLCORE33 -#flag openbsd -DSOKOL_GLCORE33 +#flag linux -DSOKOL_GLCORE +#flag freebsd -DSOKOL_GLCORE +#flag openbsd -DSOKOL_GLCORE //#flag darwin -framework OpenGL -framework Cocoa -framework QuartzCore // D3D -#flag windows -DSOKOL_GLCORE33 +#flag windows -DSOKOL_GLCORE //#flag windows -DSOKOL_D3D11 // for simplicity, all header includes are here because import order matters and we dont have any way // to ensure import order with V yet diff --git a/vlib/sokol/gfx/enums.v b/vlib/sokol/gfx/enums.v index b64b6f7c87..57745e9cb9 100644 --- a/vlib/sokol/gfx/enums.v +++ b/vlib/sokol/gfx/enums.v @@ -88,9 +88,10 @@ pub enum PixelFormat as u32 { etc2_rgb8a1 etc2_rgba8 etc2_srgb8a8 - etc2_rg11 - etc2_rg11sn - // + eac_r11 + eac_r11sn + eac_rg11 + eac_rg11sn astc_4x4_rgba astc_4x4_srgba // @@ -120,8 +121,9 @@ pub enum BufferType as u32 { _default // value 0 reserved for default-init vertexbuffer indexbuffer + storagebuffer _num - _force_u32 = 0x7FFFFFFF + _force_u32 = 0x7FFFFFFF } pub enum IndexType as u32 { diff --git a/vlib/sokol/gfx/gfx_structs.c.v b/vlib/sokol/gfx/gfx_structs.c.v index 06cd92561c..1bea7ac6e6 100644 --- a/vlib/sokol/gfx/gfx_structs.c.v +++ b/vlib/sokol/gfx/gfx_structs.c.v @@ -254,6 +254,7 @@ pub mut: entry &char d3d11_target &char uniform_blocks [4]ShaderUniformBlockDesc + storage_buffers [8]ShaderStorageBufferDesc images [12]ShaderImageDesc samplers [8]ShaderSamplerDesc image_sampler_pairs [12]ShaderImageSamplerPairDesc @@ -296,6 +297,14 @@ pub mut: pub type ShaderImageDesc = C.sg_shader_image_desc +pub struct C.sg_shader_storage_buffer_desc { +pub mut: + used bool + readonly bool +} + +pub type ShaderStorageBufferDesc = C.sg_shader_storage_buffer_desc + pub struct C.sg_shader_sampler_desc { pub mut: used bool @@ -461,6 +470,7 @@ pub struct C.sg_frame_stats_metal_bindings { num_set_vertex_buffer u32 num_set_vertex_texture u32 num_set_vertex_sampler_state u32 + num_set_fragment_buffer u32 num_set_fragment_texture u32 num_set_fragment_sampler_state u32 } @@ -770,11 +780,7 @@ pub: image_clamp_to_border bool // border color and clamp-to-border UV-wrap mode is supported mrt_independent_blend_state bool // multiple-render-target rendering can use per-render-target blend state mrt_independent_write_mask bool // multiple-render-target rendering can use per-render-target color write masks - // instancing bool // hardware instancing supported - // multiple_render_targets bool // offscreen render passes can have multiple render targets attached - // msaa_render_targets bool // offscreen render passes support MSAA antialiasing - // imagetype_3d bool // creation of SG_IMAGETYPE_3D images is supported - // imagetype_array bool // creation of SG_IMAGETYPE_ARRAY images is supported + storage_buffer bool // storage buffers are supported } pub type Features = C.sg_features