commit 883b714
uint
·
2026-07-21 14:36:34 +0000 UTC
parent 9f17975
vendor sokol_gfx
1 files changed,
+26836,
-0
+26836,
-0
1@@ -0,0 +1,26836 @@
2+#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_IMPL)
3+#define SOKOL_GFX_IMPL
4+#endif
5+#ifndef SOKOL_GFX_INCLUDED
6+/*
7+ sokol_gfx.h -- simple 3D API wrapper
8+
9+ Project URL: https://github.com/floooh/sokol
10+
11+ Example code: https://github.com/floooh/sokol-samples
12+
13+ Do this:
14+ #define SOKOL_IMPL or
15+ #define SOKOL_GFX_IMPL
16+ before you include this file in *one* C or C++ file to create the
17+ implementation.
18+
19+ In the same place define one of the following to select the rendering
20+ backend:
21+ #define SOKOL_GLCORE
22+ #define SOKOL_GLES3
23+ #define SOKOL_D3D11
24+ #define SOKOL_METAL
25+ #define SOKOL_WGPU
26+ #define SOKOL_VULKAN
27+ #define SOKOL_DUMMY_BACKEND
28+
29+ I.e. for the desktop GL it should look like this:
30+
31+ #include ...
32+ #include ...
33+ #define SOKOL_IMPL
34+ #define SOKOL_GLCORE
35+ #include "sokol_gfx.h"
36+
37+ The dummy backend replaces the platform-specific backend code with empty
38+ stub functions. This is useful for writing tests that need to run on the
39+ command line.
40+
41+ Optionally provide the following defines with your own implementations:
42+
43+ SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
44+ SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
45+ SOKOL_GFX_API_DECL - public function declaration prefix (default: extern)
46+ SOKOL_API_DECL - same as SOKOL_GFX_API_DECL
47+ SOKOL_API_IMPL - public function implementation prefix (default: -)
48+ SOKOL_TRACE_HOOKS - enable trace hook callbacks (search below for TRACE HOOKS)
49+ SOKOL_EXTERNAL_GL_LOADER - indicates that you're using your own GL loader, in this case
50+ sokol_gfx.h will not include any platform GL headers and disable
51+ the integrated Win32 GL loader
52+
53+ If sokol_gfx.h is compiled as a DLL, define the following before
54+ including the declaration or implementation:
55+
56+ SOKOL_DLL
57+
58+ On Windows, SOKOL_DLL will define SOKOL_GFX_API_DECL as __declspec(dllexport)
59+ or __declspec(dllimport) as needed.
60+
61+ Optionally define the following to force debug checks and validations
62+ even in release mode:
63+
64+ SOKOL_DEBUG - by default this is defined if NDEBUG is not defined
65+
66+ Link with the following system libraries (note that sokol_app.h has
67+ additional linker requirements):
68+
69+ - on macOS/iOS with Metal: Metal
70+ - on macOS with GL: OpenGL
71+ - on iOS with GL: OpenGLES
72+ - on Linux with EGL: GL or GLESv2
73+ - on Linux with GLX: GL
74+ - on Linux with Vulkan: vulkan
75+ - on Android: GLESv3, log, android
76+ - on Windows:
77+ - with Vulkan: link with vulkan-1 (this is explicit in case you want to
78+ use your own Vulkan loader library)
79+ - with D3D11:
80+ - on MSVC or Clang: no action needed, libs are defined in-source via pragma-comment-lib
81+ - on MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined and link with -ld3d11
82+ - with GL: no linking needed since sokol_gfx.h comes with its own GL loader on Windows
83+
84+ On macOS and iOS, the implementation must be compiled as Objective-C.
85+
86+ For Linux+Vulkan install the following packages (or equivalents):
87+ - libvulkan-dev
88+ - vulkan-validationlayers
89+ - vulkan-tools
90+
91+ For Windows+Vulkan install the Vulkan SDK and in your build system:
92+ - add a header search path to $ENV{VULKAN_SDK}/Include
93+ - add a link search path to $ENV{VULKAN_SDK}/Env
94+
95+ On Emscripten:
96+ - for WebGL2: add the linker option `-s USE_WEBGL2=1`
97+ - for WebGPU: compile and link with `--use-port=emdawnwebgpu`
98+ (for more exotic situations, read: https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/pkg/README.md)
99+
100+ sokol_gfx DOES NOT:
101+ ===================
102+ - create a window, swapchain or the 3D-API context/device, you must do this
103+ before sokol_gfx is initialized, and pass any required information
104+ (like 3D device pointers) to the sokol_gfx initialization call
105+
106+ - present the rendered frame, how this is done exactly usually depends
107+ on how the window and 3D-API context/device was created
108+
109+ - provide a unified shader language, instead 3D-API-specific shader
110+ source-code or shader-bytecode must be provided (for the "official"
111+ offline shader cross-compiler / code-generator, see here:
112+ https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md)
113+
114+
115+ STEP BY STEP
116+ ============
117+ --- to initialize sokol_gfx, after creating a window and a 3D-API
118+ context/device, call:
119+
120+ sg_setup(const sg_desc*)
121+
122+ Depending on the selected 3D backend, sokol-gfx requires some
123+ information about its runtime environment, like a GPU device pointer,
124+ default swapchain pixel formats and so on. If you are using sokol_app.h
125+ for the window system glue, you can use a helper function provided in
126+ the sokol_glue.h header:
127+
128+ #include "sokol_gfx.h"
129+ #include "sokol_app.h"
130+ #include "sokol_glue.h"
131+ //...
132+ sg_setup(&(sg_desc){
133+ .environment = sglue_environment(),
134+ });
135+
136+ To get any logging output for errors and from the validation layer, you
137+ need to provide a logging callback. Easiest way is through sokol_log.h:
138+
139+ #include "sokol_log.h"
140+ //...
141+ sg_setup(&(sg_desc){
142+ //...
143+ .logger.func = slog_func,
144+ });
145+
146+ --- create resource objects (buffers, images, views, samplers, shaders
147+ and pipeline objects)
148+
149+ sg_buffer sg_make_buffer(const sg_buffer_desc*)
150+ sg_image sg_make_image(const sg_image_desc*)
151+ sg_view sg_make_view(const sg_view_desc*)
152+ sg_sampler sg_make_sampler(const sg_sampler_desc*)
153+ sg_shader sg_make_shader(const sg_shader_desc*)
154+ sg_pipeline sg_make_pipeline(const sg_pipeline_desc*)
155+
156+ --- start a render- or compute-pass:
157+
158+ sg_begin_pass(const sg_pass* pass);
159+
160+ Typically, render passes render into an externally provided swapchain which
161+ presents the rendering result on the display. Such a 'swapchain pass'
162+ is started like this:
163+
164+ sg_begin_pass(&(sg_pass){ .action = { ... }, .swapchain = sglue_swapchain() })
165+
166+ ...where .action is an sg_pass_action struct containing actions to be performed
167+ at the start and end of a render pass (such as clearing the render surfaces to
168+ a specific color), and .swapchain is an sg_swapchain struct with all the required
169+ information to render into the swapchain's surfaces.
170+
171+ To start an 'offscreen render pass' into sokol-gfx image objects, populate
172+ the sg_pass.attachments nested struct with attachment view objects
173+ (1..4 color-attachment-views for to render into, a depth-stencil-attachment-view
174+ to provide the depth-stencil-buffer, and optionally 1..4 resolve-attachment-views
175+ for an MSAA-resolve operation:
176+
177+ sg_begin_pass(&(sg_pass){
178+ .action = { ... },
179+ .attachments = {
180+ .colors[0] = color_attachment_view,
181+ .resolves[0] = optional_resolve_attachment_view,
182+ .depth_stencil = depth_stencil_attachment_view,
183+ },
184+ });
185+
186+ To start a compute-pass, just set the .compute item to true:
187+
188+ sg_begin_pass(&(sg_pass){ .compute = true });
189+
190+ --- set the pipeline state for the next draw call with:
191+
192+ sg_apply_pipeline(sg_pipeline pip)
193+
194+ --- fill an sg_bindings struct with the resource bindings for the next
195+ draw- or dispatch-call (0..N vertex buffers, 0 or 1 index buffer, 0..N views,
196+ 0..N samplers), and call
197+
198+ sg_apply_bindings(const sg_bindings* bindings)
199+
200+ ...to update the resource bindings. Note that in a compute pass, no vertex-
201+ or index-buffer bindings can be used, and in render passes, no storage-image bindings
202+ are allowed. Those restrictions will be checked by the sokol-gfx validation layer.
203+
204+ --- optionally update shader uniform data with:
205+
206+ sg_apply_uniforms(int ub_slot, const sg_range* data)
207+
208+ Read the section 'UNIFORM DATA LAYOUT' to learn about the expected memory layout
209+ of the uniform data passed into sg_apply_uniforms().
210+
211+ --- kick off a draw call with:
212+
213+ sg_draw(int base_element, int num_elements, int num_instances)
214+
215+ The sg_draw() function unifies all the different ways to render primitives
216+ in a single call (indexed vs non-indexed rendering, and instanced vs non-instanced
217+ rendering). In case of indexed rendering, base_element and num_element specify
218+ indices in the currently bound index buffer. In case of non-indexed rendering
219+ base_element and num_elements specify vertices in the currently bound
220+ vertex-buffer(s). To perform instanced rendering, the rendering pipeline
221+ must be setup for instancing (see sg_pipeline_desc below), a separate vertex buffer
222+ containing per-instance data must be bound, and the num_instances parameter
223+ must be > 1.
224+
225+ Alternatively, call:
226+
227+ sg_draw_ex(...)
228+
229+ to provide a base-vertex and/or base-instance which allows to render
230+ from different sections of a vertex buffer without rebinding the
231+ vertex buffer with a different offset. Note that the `sg_draw_ex()`
232+ only has limited portability on OpenGL, check the sg_limits struct
233+ members .draw_base_vertex and .draw_base_instance for runtime support,
234+ those are generally true on non-GL-backends, and on GL the feature
235+ flags are set according to the GL version:
236+
237+ - on GL base_instance != 0 is only supported since GL 4.2
238+ - on GLES3.x, base_instance != 0 is not supported
239+ - on GLES3.x, base_vertex is only supported since GLES3.2
240+ (e.g. not supported on WebGL2)
241+
242+ --- ...or kick of a dispatch call to invoke a compute shader workload:
243+
244+ sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z)
245+
246+ The dispatch args define the number of 'compute workgroups' processed
247+ by the currently applied compute shader.
248+
249+ --- finish the current pass with:
250+
251+ sg_end_pass()
252+
253+ --- when done with the current frame, call
254+
255+ sg_commit()
256+
257+ --- at the end of your program, shutdown sokol_gfx with:
258+
259+ sg_shutdown()
260+
261+ --- if you need to destroy resources before sg_shutdown(), call:
262+
263+ sg_destroy_buffer(sg_buffer buf)
264+ sg_destroy_image(sg_image img)
265+ sg_destroy_sampler(sg_sampler smp)
266+ sg_destroy_shader(sg_shader shd)
267+ sg_destroy_pipeline(sg_pipeline pip)
268+ sg_destroy_view(sg_view view)
269+
270+ --- to set a new viewport rectangle, call:
271+
272+ sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left)
273+
274+ ...or if you want to specify the viewport rectangle with float values:
275+
276+ sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left)
277+
278+ --- to set a new scissor rect, call:
279+
280+ sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left)
281+
282+ ...or with float values:
283+
284+ sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left)
285+
286+ Both sg_apply_viewport() and sg_apply_scissor_rect() must be called
287+ inside a rendering pass (e.g. not in a compute pass, or outside a pass)
288+
289+ Note that sg_begin_pass() will reset both the viewport and scissor
290+ rectangles to cover the entire framebuffer.
291+
292+ --- to update (overwrite) the content of buffer and image resources, call:
293+
294+ sg_update_buffer(sg_buffer buf, const sg_range* data)
295+ sg_update_image(sg_image img, const sg_image_data* data)
296+
297+ Buffers and images to be updated must have been created with
298+ sg_buffer_desc.usage.dynamic_update or .stream_update.
299+
300+ Only one update per frame is allowed for buffer and image resources when
301+ using the sg_update_*() functions. The rationale is to have a simple
302+ protection from the CPU scribbling over data the GPU is currently
303+ using, or the CPU having to wait for the GPU
304+
305+ Buffer and image updates can be partial, as long as a rendering
306+ operation only references the valid (updated) data in the
307+ buffer or image.
308+
309+ --- to append a chunk of data to a buffer resource, call:
310+
311+ int sg_append_buffer(sg_buffer buf, const sg_range* data)
312+
313+ The difference to sg_update_buffer() is that sg_append_buffer()
314+ can be called multiple times per frame to append new data to the
315+ buffer piece by piece, optionally interleaved with draw calls referencing
316+ the previously written data.
317+
318+ sg_append_buffer() returns a byte offset to the start of the
319+ written data, this offset can be assigned to
320+ sg_bindings.vertex_buffer_offsets[n] or
321+ sg_bindings.index_buffer_offset
322+
323+ Code example:
324+
325+ for (...) {
326+ const void* data = ...;
327+ const int num_bytes = ...;
328+ int offset = sg_append_buffer(buf, &(sg_range) { .ptr=data, .size=num_bytes });
329+ bindings.vertex_buffer_offsets[0] = offset;
330+ sg_apply_pipeline(pip);
331+ sg_apply_bindings(&bindings);
332+ sg_apply_uniforms(...);
333+ sg_draw(...);
334+ }
335+
336+ A buffer to be used with sg_append_buffer() must have been created
337+ with sg_buffer_desc.usage.dynamic_update or .stream_update.
338+
339+ If the application appends more data to the buffer then fits into
340+ the buffer, the buffer will go into the "overflow" state for the
341+ rest of the frame.
342+
343+ Any draw calls attempting to render an overflown buffer will be
344+ silently dropped (in debug mode this will also result in a
345+ validation error).
346+
347+ You can also check manually if a buffer is in overflow-state by calling
348+
349+ bool sg_query_buffer_overflow(sg_buffer buf)
350+
351+ You can manually check to see if an overflow would occur before adding
352+ any data to a buffer by calling
353+
354+ bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size)
355+
356+ NOTE: Due to restrictions in underlying 3D-APIs, appended chunks of
357+ data will be 4-byte aligned in the destination buffer. This means
358+ that there will be gaps in index buffers containing 16-bit indices
359+ when the number of indices in a call to sg_append_buffer() is
360+ odd. This isn't a problem when each call to sg_append_buffer()
361+ is associated with one draw call, but will be problematic when
362+ a single indexed draw call spans several appended chunks of indices.
363+
364+ --- to check at runtime for optional features, limits and pixelformat support,
365+ call:
366+
367+ sg_features sg_query_features()
368+ sg_limits sg_query_limits()
369+ sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt)
370+
371+ --- if you need to call into the underlying 3D-API directly, you must call:
372+
373+ sg_reset_state_cache()
374+
375+ ...before calling sokol_gfx functions again
376+
377+ --- you can inspect the original sg_desc structure handed to sg_setup()
378+ by calling sg_query_desc(). This will return an sg_desc struct with
379+ the default values patched in instead of any zero-initialized values
380+
381+ --- you can get a desc struct matching the creation attributes of a
382+ specific resource object via:
383+
384+ sg_buffer_desc sg_query_buffer_desc(sg_buffer buf)
385+ sg_image_desc sg_query_image_desc(sg_image img)
386+ sg_sampler_desc sg_query_sampler_desc(sg_sampler smp)
387+ sg_shader_desc sq_query_shader_desc(sg_shader shd)
388+ sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip)
389+ sg_view_desc sg_query_view_desc(sg_view view)
390+
391+ ...but NOTE that the returned desc structs may be incomplete, only
392+ creation attributes that are kept around internally after resource
393+ creation will be filled in, and in some cases (like shaders) that's
394+ very little. Any missing attributes will be set to zero. The returned
395+ desc structs might still be useful as partial blueprint for creating
396+ similar resources if filled up with the missing attributes.
397+
398+ Calling the query-desc functions on an invalid resource will return
399+ completely zeroed structs (it makes sense to check the resource state
400+ with sg_query_*_state() first)
401+
402+ --- you can query the default resource creation parameters through the functions
403+
404+ sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc)
405+ sg_image_desc sg_query_image_defaults(const sg_image_desc* desc)
406+ sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc)
407+ sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc)
408+ sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc)
409+ sg_view_desc sg_query_view_defaults(const sg_view_desc* desc)
410+
411+ These functions take a pointer to a desc structure which may contain
412+ zero-initialized items for default values. These zero-init values
413+ will be replaced with their concrete values in the returned desc
414+ struct.
415+
416+ --- you can inspect various internal resource runtime values via:
417+
418+ sg_buffer_info sg_query_buffer_info(sg_buffer buf)
419+ sg_image_info sg_query_image_info(sg_image img)
420+ sg_sampler_info sg_query_sampler_info(sg_sampler smp)
421+ sg_shader_info sg_query_shader_info(sg_shader shd)
422+ sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip)
423+ sg_view_info sg_query_view_info(sg_view view)
424+
425+ ...please note that the returned info-structs are tied quite closely
426+ to sokol_gfx.h internals, and may change more often than other
427+ public API functions and structs.
428+
429+ -- you can query the type/flavour and parent resource of a view:
430+
431+ sg_view_type sg_query_view_type(sg_view view)
432+ sg_image sg_query_view_image(sg_view view)
433+ sg_buffer sg_query_view_buffer(sg_view view)
434+
435+ --- you can query stats and control stats collection via:
436+
437+ sg_query_stats()
438+ sg_enable_stats()
439+ sg_disable_stats()
440+ sg_stats_enabled()
441+
442+ --- you can ask at runtime what backend sokol_gfx.h has been compiled for:
443+
444+ sg_backend sg_query_backend(void)
445+
446+ --- call the following helper functions to compute the number of
447+ bytes in a texture row or surface for a specific pixel format.
448+ These functions might be helpful when preparing image data for consumption
449+ by sg_make_image() or sg_update_image():
450+
451+ int sg_query_row_pitch(sg_pixel_format fmt, int width, int int row_align_bytes);
452+ int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes);
453+
454+ Width and height are generally in number pixels, but note that 'row' has different meaning
455+ for uncompressed vs compressed pixel formats: for uncompressed formats, a row is identical
456+ with a single line if pixels, while in compressed formats, one row is a line of *compression blocks*.
457+
458+ This is why calling sg_query_surface_pitch() for a compressed pixel format and height
459+ N, N+1, N+2, ... may return the same result.
460+
461+ The row_align_bytes parameter is for added flexibility. For image data that goes into
462+ the sg_make_image() or sg_update_image() this should generally be 1, because these
463+ functions take tightly packed image data as input no matter what alignment restrictions
464+ exist in the backend 3D APIs.
465+
466+ ON INITIALIZATION:
467+ ==================
468+ When calling sg_setup(), a pointer to an sg_desc struct must be provided
469+ which contains initialization options. These options provide two types
470+ of information to sokol-gfx:
471+
472+ (1) upper bounds and limits needed to allocate various internal
473+ data structures:
474+ - the max number of resources of each type that can
475+ be alive at the same time, this is used for allocating
476+ internal pools
477+ - the max overall size of uniform data that can be
478+ updated per frame, including a worst-case alignment
479+ per uniform update (this worst-case alignment is 256 bytes)
480+ - the max size of all dynamic resource updates (sg_update_buffer,
481+ sg_append_buffer and sg_update_image) per frame
482+ - the max number of compute-dispatch calls in a compute pass
483+ Not all of those limit values are used by all backends, but it is
484+ good practice to provide them none-the-less.
485+
486+ (2) 3D backend "environment information" in a nested sg_environment struct:
487+ - pointers to backend-specific context- or device-objects (for instance
488+ the D3D11, WebGPU or Metal device objects)
489+ - defaults for external swapchain pixel formats and sample counts,
490+ these will be used as default values in image and pipeline objects,
491+ and the sg_swapchain struct passed into sg_begin_pass()
492+ Usually you provide a complete sg_environment struct through
493+ a helper function, as an example look at the sglue_environment()
494+ function in the sokol_glue.h header.
495+
496+ See the documentation block of the sg_desc struct below for more information.
497+
498+
499+ ON RENDER PASSES
500+ ================
501+ Relevant samples:
502+ - https://floooh.github.io/sokol-html5/offscreen-sapp.html
503+ - https://floooh.github.io/sokol-html5/offscreen-msaa-sapp.html
504+ - https://floooh.github.io/sokol-html5/mrt-sapp.html
505+ - https://floooh.github.io/sokol-html5/mrt-pixelformats-sapp.html
506+
507+ A render pass groups rendering commands into a set of render target images
508+ (called 'render pass attachments'). Render target images can be used in subsequent
509+ passes as textures (it is invalid to use the same image both as render target
510+ and as texture in the same pass).
511+
512+ The following sokol-gfx functions must only be called inside a render-pass:
513+
514+ sg_apply_viewport[f]
515+ sg_apply_scissor_rect[f]
516+ sg_draw
517+
518+ The following function may be called inside a render- or compute-pass, but
519+ not outside a pass:
520+
521+ sg_apply_pipeline
522+ sg_apply_bindings
523+ sg_apply_uniforms
524+
525+ A frame must have at least one 'swapchain render pass' which renders into an
526+ externally provided swapchain provided as an sg_swapchain struct to the
527+ sg_begin_pass() function. If you use sokol_gfx.h together with sokol_app.h,
528+ just call the sglue_swapchain() helper function in sokol_glue.h to
529+ provide the swapchain information. Otherwise the following information
530+ must be provided:
531+
532+ - the color pixel-format of the swapchain's render surface
533+ - an optional depth/stencil pixel format if the swapchain
534+ has a depth/stencil buffer
535+ - an optional sample-count for MSAA rendering
536+ - NOTE: the above three values can be zero-initialized, in that
537+ case the defaults from the sg_environment struct will be used that
538+ had been passed to the sg_setup() function.
539+ - a number of backend specific objects:
540+ - GL/GLES3: just a GL framebuffer handle
541+ - D3D11:
542+ - an ID3D11RenderTargetView for the rendering surface
543+ - if MSAA is used, an ID3D11RenderTargetView as
544+ MSAA resolve-target
545+ - an optional ID3D11DepthStencilView for the
546+ depth/stencil buffer
547+ - WebGPU
548+ - a WGPUTextureView object for the rendering surface
549+ - if MSAA is used, a WGPUTextureView object as MSAA resolve target
550+ - an optional WGPUTextureView for the
551+ - Metal (NOTE that the roles of provided surfaces is slightly
552+ different in Metal than in D3D11 or WebGPU, notably, the
553+ CAMetalDrawable is either rendered to directly, or serves
554+ as MSAA resolve target):
555+ - a CAMetalDrawable object which is either rendered
556+ into directly, or in case of MSAA rendering, serves
557+ as MSAA-resolve-target
558+ - if MSAA is used, an multisampled MTLTexture where
559+ rendering goes into
560+ - an optional MTLTexture for the depth/stencil buffer
561+
562+ A sg_swapchain struct provided to sg_begin_pass() can indicate that the
563+ swapchain is in an 'invalid state' via the boolean `sg_swapchain.invalid`.
564+ When this flag is set, all other sg_swapchain members must be zeroed.
565+ An invalid swapchain will cause all rendering operations in that pass
566+ to be silently skipped.
567+
568+ It's recommended that you create a helper function which returns an
569+ initialized sg_swapchain struct by value. This can then be directly plugged
570+ into the sg_begin_pass function like this:
571+
572+ sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() });
573+
574+ As an example for such a helper function check out the function sglue_swapchain()
575+ in the sokol_glue.h header.
576+
577+ For offscreen render passes, the render target images used in a render pass
578+ must be provided as sg_view objects specialized for the specific pass-attachment
579+ types:
580+
581+ - color-attachment-views for color-rendering
582+ - depth-stencil-attachment-views for the depth-stencil-buffer surface
583+ - resolve-attachment-views for MSAA-resolve operations
584+
585+ For a simple offscreen scenario with one color-, one depth-stencil-render
586+ target and without multisampling, setting up the required image-
587+ and view-objects looks like this:
588+
589+ First create two render target images, one with a color pixel format,
590+ and one with the depth- or depth-stencil pixel format. Both images
591+ must have the same dimensions. Also not the usage flags:
592+
593+ const sg_image color_img = sg_make_image(&(sg_image_desc){
594+ .usage.color_attachment = true,
595+ .width = 256,
596+ .height = 256,
597+ .pixel_format = SG_PIXELFORMAT_RGBA8,
598+ .sample_count = 1,
599+ });
600+ const sg_image depth_img = sg_make_image(&(sg_image_desc){
601+ .usage.depth_stencil_attachment = true,
602+ .width = 256,
603+ .height = 256,
604+ .pixel_format = SG_PIXELFORMAT_DEPTH,
605+ .sample_count = 1,
606+ });
607+
608+ NOTE: when creating render target images, have in mind that some default values
609+ are aligned with the default environment attributes in the sg_environment struct
610+ that was passed into the sg_setup() call:
611+
612+ - the default value for sg_image_desc.pixel_format is taken from
613+ sg_environment.defaults.color_format
614+ - the default value for sg_image_desc.sample_count is taken from
615+ sg_environment.defaults.sample_count
616+ - the default value for sg_image_desc.num_mipmaps is always 1
617+
618+ Next, create two view objects, one color-attachment-view and one
619+ depth-stencil-attachment view:
620+
621+ const sg_view color_att_view = sg_make_view(&(sg_view_desc){
622+ .color_attachment.image = color_img,
623+ });
624+ const sg_view depth_att_view = sg_make_view(&(sg_view_desc){
625+ .depth_stencil_attachment.image = depth_img,
626+ });
627+
628+ You'll typically also want to create a texture-view on the color image
629+ to sample the color attachment image as texture in a later pass:
630+
631+ const sg_view tex_view = sg_make_view(&(sg_view_desc){
632+ .texture.image = color_img,
633+ });
634+
635+ The attachment-view objects are then passed into the sg_begin_pass function in
636+ place of the nested swapchain struct:
637+
638+ sg_begin_pass(&(sg_pass){
639+ .attachments = {
640+ .colors[0] = color_att_view,
641+ .depth_stencil = depth_att_view,
642+ },
643+ });
644+
645+ ...in a later pass when you want to sample the color attachment image as
646+ texture, use the texture view in the sg_apply_bindings() call:
647+
648+ sg_apply_bindings(&(sg_bindings){
649+ .vertex_buffers[0] = ...,
650+ .index_buffer = ...,
651+ .views[VIEW_tex] = tex_view,
652+ .samplers[SMP_smp] = smp,
653+ });
654+
655+ Swapchain and offscreen passes form dependency trees with a swapchain
656+ pass at the root, offscreen passes as nodes, and attachment images as
657+ dependencies between passes.
658+
659+ sg_pass_action structs are used to define actions that should happen at the
660+ start and end of render passes (such as clearing pass attachments to a
661+ specific color or depth-value, or performing an MSAA resolve operation at
662+ the end of a pass).
663+
664+ A typical sg_pass_action object which clears the color attachment to black
665+ might look like this:
666+
667+ const sg_pass_action = {
668+ .colors[0] = {
669+ .load_action = SG_LOADACTION_CLEAR,
670+ .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f }
671+ }
672+ };
673+
674+ This omits the defaults for the color attachment store action, and
675+ the depth-stencil-attachments actions. The same pass action with the
676+ defaults explicitly filled in would look like this:
677+
678+ const sg_pass_action pass_action = {
679+ .colors[0] = {
680+ .load_action = SG_LOADACTION_CLEAR,
681+ .store_action = SG_STOREACTION_STORE,
682+ .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f }
683+ },
684+ .depth = = {
685+ .load_action = SG_LOADACTION_CLEAR,
686+ .store_action = SG_STOREACTION_DONTCARE,
687+ .clear_value = 1.0f,
688+ },
689+ .stencil = {
690+ .load_action = SG_LOADACTION_CLEAR,
691+ .store_action = SG_STOREACTION_DONTCARE,
692+ .clear_value = 0
693+ }
694+ };
695+
696+ With the sg_pass object and sg_pass_action struct in place everything
697+ is ready now for the actual render pass:
698+
699+ Using such this prepared sg_pass_action in a swapchain pass looks like
700+ this:
701+
702+ sg_begin_pass(&(sg_pass){
703+ .action = pass_action,
704+ .swapchain = sglue_swapchain()
705+ });
706+ ...
707+ sg_end_pass();
708+
709+ ...of alternatively in one offscreen pass:
710+
711+ sg_begin_pass(&(sg_pass){
712+ .action = pass_action,
713+ .attachments = {
714+ .colors[0] = color_att_view,
715+ .depth_stencil = ds_att_view,
716+ },
717+ });
718+ ...
719+ sg_end_pass();
720+
721+ Offscreen rendering can also go into a mipmap, or a slice/face of
722+ a cube-, array- or 3d-image (which some restrictions, for instance
723+ it's not possible to create a 3D image with a depth/stencil pixel format,
724+ these exceptions are generally caught by the sokol-gfx validation layer).
725+
726+ The mipmap/slice selection is baked into the attachment-view objects, for
727+ instance to create a color-attachment-view for rendering into mip-level
728+ 2 and slice 3 of an array texture:
729+
730+ const sg_view color_att_view = sg_make_view(&(sg_view_desc){
731+ .color_attachment = {
732+ .image = color_img,
733+ .mip_level = 2,
734+ .slice = 3,
735+ },
736+ });
737+
738+ If MSAA offscreen rendering is desired, the multi-sample rendering result
739+ must be 'resolved' into a separate 'resolve image', before that image can
740+ be used as texture.
741+
742+ Setting up MSAA offscreen 3D rendering requires three image objects
743+ (one color-attachment image with a sample count > 1), a resolve-attachment
744+ image with a sample count of 1, and a depth-stencil-attachment image
745+ with the same sample count as the color-attachment image:
746+
747+ const sg_image color_img = sg_make_image(&(sg_image_desc){
748+ .usage.color_attachment = true,
749+ .width = 256,
750+ .height = 256,
751+ .pixel_format = SG_PIXELFORMAT_RGBA8,
752+ .sample_count = 4,
753+ });
754+ const sg_image resolve_img = sg_make_image(&(sg_image_desc){
755+ .usage.resolve_attachment = true,
756+ .width = 256,
757+ .height = 256,
758+ .pixel_format = SG_PIXELFORMAT_RGBA8,
759+ .sample_count = 1,
760+ });
761+ const sg_image depth_img = sg_make_image(&(sg_image_desc){
762+ .usage.depth_stencil_attachment = true,
763+ .width = 256,
764+ .height = 256,
765+ .pixel_format = SG_PIXELFORMAT_DEPTH,
766+ .sample_count = 4,
767+ });
768+
769+ Next you'll need the corresponding attachment-view objects:
770+
771+ const sg_view color_att_view = sg_make_view(&(sg_view_desc){
772+ .color_attachment.image = color_img,
773+ });
774+ const sg_view resolve_att_view = sg_make_view(&(sg_view_desc){
775+ .resolve_attachment.image = resolve_img,
776+ });
777+ const sg_view depth_att_view = sg_make_view(&(sg_view_desc){
778+ .depth_stencil_attachment.image = depth_img,
779+ });
780+
781+ To sample the rendered image as a texture in a later pass you'll also
782+ need a texture-view on the resolve-attachment-image (not the color-attachment-image!):
783+
784+ const sg_view tex_view = sg_make_view(&(sg_view_desc){
785+ .texture.image = resolve_img,
786+ });
787+
788+ Next start the render pass with all attachment-views, as soon as a
789+ resolve-attachment-view is provided, an MSAA resolve operation will happen
790+ at the end of the pass. Also note that the content of the MSAA color-attachment-image
791+ doesn't need to be preserved, since it's only needed until the MSAA-resolve
792+ at the end of the pass, so the .store_action should be set to "don't care":
793+
794+ sg_begin_pass(&(sg_pass){
795+ .attachments = {
796+ .colors[0] = color_att_view,
797+ .resolves[0] = resolve_att_view,
798+ .depth_stencil = depth_att_view,
799+ },
800+ .action = {
801+ .colors[0] = {
802+ .load_action = SG_LOADACTION_CLEAR,
803+ .store_action = SG_STOREACTION_DONTCARE,
804+ .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f },
805+ }
806+ },
807+ });
808+
809+ ...in a later pass, use the texture-view that had been created on the
810+ resolve-image to use the rendering result as texture:
811+
812+ sg_apply_bindings(&(sg_bindings){
813+ .vertex_buffers[0] = ...,
814+ .index_buffer = ...,
815+ .views[VIEW_tex] = tex_view,
816+ .samplers[SMP_smp] = smp,
817+ });
818+
819+ ON COMPUTE PASSES
820+ =================
821+ Compute passes are used to update the content of storage buffers and
822+ storage images by running compute shader code on
823+ the GPU. Updating storage resources with a compute shader will almost always
824+ be more efficient than computing the same data on the CPU and then uploading
825+ it via `sg_update_buffer()` or `sg_update_image()`.
826+
827+ NOTE: compute passes are only supported on the following platforms and
828+ backends:
829+
830+ - macOS and iOS with Metal
831+ - Windows with D3D11 and OpenGL
832+ - Linux with OpenGL or GLES3.1+
833+ - Web with WebGPU
834+ - Android with GLES3.1+
835+
836+ ...this means compute shaders can't be used on the following platform/backend
837+ combos (the same restrictions apply to using storage buffers without compute
838+ shaders):
839+
840+ - macOS with GL
841+ - iOS with GLES3
842+ - Web with WebGL2
843+
844+ A compute pass is started with:
845+
846+ sg_begin_pass(&(sg_pass){ .compute = true });
847+
848+ ...and finished with a regular:
849+
850+ sg_end_pass();
851+
852+ Typically the following functions will be called inside a compute pass:
853+
854+ sg_apply_pipeline()
855+ sg_apply_bindings()
856+ sg_apply_uniforms()
857+ sg_dispatch()
858+
859+ The following functions are disallowed inside a compute pass
860+ and will cause validation layer errors:
861+
862+ sg_apply_viewport[f]()
863+ sg_apply_scissor_rect[f]()
864+ sg_draw()
865+
866+ Only special 'compute shaders' and 'compute pipelines' can be used in
867+ compute passes. A compute shader only has a compute-function instead
868+ of a vertex- and fragment-function pair, and it doesn't accept vertex-
869+ and index-buffers as bindings, only storage-buffer-views (readable
870+ and writable), storage-image-views (read/write or writeonly) and
871+ texture-views (read-only).
872+
873+ A compute pipeline is created by providing a compute shader object,
874+ setting the .compute creation parameter to true and not defining any
875+ 'render state':
876+
877+ sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){
878+ .compute = true,
879+ .shader = compute_shader,
880+ });
881+
882+ The sg_apply_bindings and sg_apply_uniforms calls are the same as in
883+ render passes, with the exception that no vertex- and index-buffers
884+ can be bound in the sg_apply_bindings call.
885+
886+ Finally to kick off a compute workload, call sg_dispatch with the
887+ number of workgroups in the x, y and z-dimension:
888+
889+ sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z)
890+
891+ Also see the following compute-shader samples:
892+
893+ - https://floooh.github.io/sokol-webgpu/instancing-compute-sapp.html
894+ - https://floooh.github.io/sokol-webgpu/computeboids-sapp.html
895+ - https://floooh.github.io/sokol-webgpu/imageblur-sapp.html
896+
897+
898+ ON SHADER CREATION
899+ ==================
900+ sokol-gfx doesn't come with an integrated shader cross-compiler, instead
901+ backend-specific shader sources or binary blobs need to be provided when
902+ creating a shader object, along with reflection information about the
903+ shader resource binding interface needed to bind sokol-gfx resources to the
904+ proper shader inputs.
905+
906+ The easiest way to provide all this shader creation data is to use the
907+ sokol-shdc shader compiler tool to compile shaders from a common
908+ GLSL syntax into backend-specific sources or binary blobs, along with
909+ shader interface information and uniform blocks and storage buffer array items
910+ mapped to C structs.
911+
912+ To create a shader using a C header which has been code-generated by sokol-shdc:
913+
914+ // include the C header code-generated by sokol-shdc:
915+ #include "myshader.glsl.h"
916+ ...
917+
918+ // create shader using a code-generated helper function from the C header:
919+ sg_shader shd = sg_make_shader(myshader_shader_desc(sg_query_backend()));
920+
921+ The samples in the 'sapp' subdirectory of the sokol-samples project
922+ also use the sokol-shdc approach:
923+
924+ https://github.com/floooh/sokol-samples/tree/master/sapp
925+
926+ If you're planning to use sokol-shdc, you can stop reading here, instead
927+ continue with the sokol-shdc documentation:
928+
929+ https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md
930+
931+ To create shaders with backend-specific shader code or binary blobs,
932+ the sg_make_shader() function requires the following information:
933+
934+ - Shader code or shader binary blobs for the vertex- and fragment-, or the
935+ compute-shader-stage:
936+ - for the desktop GL backend, source code can be provided in '#version 410' or
937+ '#version 430', version 430 is required when using storage buffers and
938+ compute shaders, but note that this is not available on macOS
939+ - for the GLES3 backend, source code must be provided in '#version 300 es' or
940+ '#version 310 es' syntax (version 310 is required for storage buffer and
941+ compute shader support, but note that this is not supported on WebGL2)
942+ - for the D3D11 backend, shaders can be provided as source or binary
943+ blobs, the source code should be in HLSL4.0 (for compatibility with old
944+ low-end GPUs) or preferably in HLSL5.0 syntax, note that when
945+ shader source code is provided for the D3D11 backend, sokol-gfx will
946+ dynamically load 'd3dcompiler_47.dll'
947+ - for the Metal backends, shaders can be provided as source or binary blobs, the
948+ MSL version should be in 'metal-1.1' (other versions may work but are not tested)
949+ - for the WebGPU backend, shaders must be provided as WGSL source code
950+ - optionally the following shader-code related attributes can be provided:
951+ - an entry function name (only on D3D11 or Metal, but not OpenGL)
952+ - on D3D11 only, a compilation target (default is "vs_4_0" and "ps_4_0")
953+
954+ - Information about the input vertex attributes used by the vertex shader,
955+ most of that backend-specific:
956+ - An optional 'base type' (float, signed-/unsigned-int) for each vertex
957+ attribute. When provided, this is used by the validation layer to check
958+ that the CPU-side input vertex format is compatible with the input
959+ vertex declaration of the vertex shader.
960+ - Metal: no location information needed since vertex attributes are always bound
961+ by their attribute location defined in the shader via '[[attribute(N)]]'
962+ - WebGPU: no location information needed since vertex attributes are always
963+ bound by their attribute location defined in the shader via `@location(N)`
964+ - GLSL: vertex attribute names can be optionally provided, in that case their
965+ location will be looked up by name, otherwise, the vertex attribute location
966+ can be defined with 'layout(location = N)'
967+ - D3D11: a 'semantic name' and 'semantic index' must be provided for each vertex
968+ attribute, e.g. if the vertex attribute is defined as 'TEXCOORD1' in the shader,
969+ the semantic name would be 'TEXCOORD', and the semantic index would be '1'
970+
971+ NOTE that vertex attributes currently must not have gaps. This requirement
972+ may be relaxed in the future.
973+
974+ - Specifically for Metal compute shaders, the 'number of threads per threadgroup'
975+ must be provided. Normally this is extracted by sokol-shdc from the GLSL
976+ shader source code. For instance the following statement in the input
977+ GLSL:
978+
979+ layout(local_size_x=64, local_size_y=1, local_size_z=1) in;
980+
981+ ...will be communicated to the sokol-gfx Metal backend in the
982+ code-generated sg_shader_desc struct:
983+
984+ (sg_shader_desc){
985+ .mtl_threads_per_threadgroup = { .x = 64, .y = 1, .z = 1 },
986+ }
987+
988+ - Information about each uniform block binding used in the shader:
989+ - the shader stage of the uniform block (vertex, fragment or compute)
990+ - the size of the uniform block in number of bytes
991+ - a memory layout hint (currently 'native' or 'std140') where 'native' defines a
992+ backend-specific memory layout which shouldn't be used for cross-platform code.
993+ Only std140 guarantees a backend-agnostic memory layout.
994+ - a backend-specific bind slot:
995+ - D3D11/HLSL: the buffer register N (`register(bN)`) where N is 0..7
996+ - Metal/MSL: the buffer bind slot N (`[[buffer(N)]]`) where N is 0..7
997+ - WebGPU: the binding N in `@group(0) @binding(N)` where N is 0..15
998+ - For GLSL only: a description of the internal uniform block layout, which maps
999+ member types and their offsets on the CPU side to uniform variable names
1000+ in the GLSL shader
1001+ - please also NOTE the documentation sections about UNIFORM DATA LAYOUT
1002+ and CROSS-BACKEND COMMON UNIFORM DATA LAYOUT below!
1003+
1004+ - A description of each resource binding (texture-, storage-buffer-
1005+ and storage-image-bindings) which directly map to the sg_bindings.view[]
1006+ array slots.
1007+
1008+ Each resource binding slot comes in three flavours:
1009+
1010+ 1. Texture bindings with the following properties:
1011+ - the shader stage of the texture (vertex, fragment or compute)
1012+ - the expected image type:
1013+ - SG_IMAGETYPE_2D
1014+ - SG_IMAGETYPE_CUBE
1015+ - SG_IMAGETYPE_3D
1016+ - SG_IMAGETYPE_ARRAY
1017+ - the expected 'image sample type':
1018+ - SG_IMAGESAMPLETYPE_FLOAT
1019+ - SG_IMAGESAMPLETYPE_DEPTH
1020+ - SG_IMAGESAMPLETYPE_SINT
1021+ - SG_IMAGESAMPLETYPE_UINT
1022+ - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT
1023+ - a flag whether the texture is expected to be multisampled
1024+ - a backend-specific bind slot:
1025+ - D3D11/HLSL: the texture register N (`register(tN)`) where N is 0..31
1026+ (in HLSL, readonly storage buffers and texture share the same bind space)
1027+ - Metal/MSL: the texture bind slot N (`[[texture(N)]]`) where N is 0..31
1028+ (the bind slot must not collide with storage image bindings on the same stage)
1029+ - WebGPU/WGSL: the binding N in `@group(0) @binding(N)` where N is 0..127
1030+
1031+ 2. Storage buffer bindings with the following properties:
1032+ - the shader stage of the storage buffer
1033+ - a boolean 'readonly' flag, this is used for validation and hazard
1034+ tracking in some 3D backends. Note that in render passes, only
1035+ readonly storage buffer bindings are allowed. In compute passes, any
1036+ read/write storage buffer binding is assumed to be written to by the
1037+ compute shader.
1038+ - a backend-specific bind slot:
1039+ - D3D11/HLSL:
1040+ - for readonly storage buffer bindings: the texture register N
1041+ (`register(tN)`) where N is 0..31 (in HLSL, readonly storage
1042+ buffers and textures share the same bind space for
1043+ 'shader resource views')
1044+ - for read/write storage buffer buffer bindings: the UAV register N
1045+ (`register(uN)`) where N is 0..31 (in HLSL, readwrite storage
1046+ buffers use their own bind space for 'unordered access views')
1047+ - Metal/MSL: the buffer bind slot N (`[[buffer(N)]]`) where N is 8..23
1048+ - WebGPU/WGSL: the binding N in `@group(0) @binding(N)` where N is 0..127
1049+ - GL/GLSL: the buffer binding N in `layout(binding=N)`
1050+ where N is 0..sg_limits.max_storage_buffer_bindings_per_stage
1051+ - note that storage buffer bindings are not supported on all backends
1052+ and platforms
1053+
1054+ 3. Storage image bindings with the following properties:
1055+ - the shader stage (*must* be compute)
1056+ - the expected image type:
1057+ - SG_IMAGETYPE_2D
1058+ - SG_IMAGETYPE_CUBE
1059+ - SG_IMAGETYPE_3D
1060+ - SG_IMAGETYPE_ARRAY
1061+ - the 'access pixel format', this is currently limited to:
1062+ - SG_PIXELFORMAT_RGBA8
1063+ - SG_PIXELFORMAT_RGBA8SN/UI/SI
1064+ - SG_PIXELFORMAT_RGBA16UI/SI/F
1065+ - SG_PIXELFORMAT_R32UIUI/SI/F
1066+ - SG_PIXELFORMAT_RG32UI/SI/F
1067+ - SG_PIXELFORMAT_RGBA32UI/SI/F
1068+ - the access type (readwrite or writeonly)
1069+ - a backend-specific bind slot:
1070+ - D3D11/HLSL: the UAV register N (`register(uN)` where N is 0..31, the
1071+ bind slot must not collide with UAV storage buffer bindings
1072+ - Metal/MSL: the texture bind slot N (`[[texture(N)]])` where N is 0..31,
1073+ the bind slot must not collide with other texture bindings on the same
1074+ stage
1075+ - WebGPU/WGSL: the binding N in `@group(1) @binding(N)` where N is 0..127
1076+ - GL/GLSL: the buffer binding N in `layout(binding=N)`
1077+ where N is 0.._sg.max_storage_image_bindings_per_stage
1078+ - note that storage image bindings are not supported on all backends and platforms
1079+
1080+ - A description of each sampler used in the shader:
1081+ - the shader stage of the sampler (vertex, fragment or compute)
1082+ - the expected sampler type:
1083+ - SG_SAMPLERTYPE_FILTERING,
1084+ - SG_SAMPLERTYPE_NONFILTERING,
1085+ - SG_SAMPLERTYPE_COMPARISON,
1086+ - a backend-specific bind slot:
1087+ - D3D11/HLSL: the sampler register N (`register(sN)`) where N is 0..SG_MAX_SAMPLER_BINDINGS
1088+ - Metal/MSL: the sampler bind slot N (`[[sampler(N)]]`) where N is 0..SG_MAX_SAMPLER_BINDINGS
1089+ - WebGPU/WGSL: the binding N in `@group(0) @binding(N)` where N is 0..127
1090+
1091+ - An array of 'texture-sampler-pairs' used by the shader to sample textures,
1092+ for D3D11, Metal and WebGPU this is used for validation purposes to check
1093+ whether the texture and sampler are compatible with each other (especially
1094+ WebGPU is very picky about combining the correct
1095+ texture-sample-type with the correct sampler-type). For GLSL an
1096+ additional 'combined-image-sampler name' must be provided because 'OpenGL
1097+ style GLSL' cannot handle separate texture and sampler objects, but still
1098+ groups them into a traditional GLSL 'sampler object'.
1099+
1100+ Compatibility rules for image-sample-type vs sampler-type are as follows:
1101+
1102+ - SG_IMAGESAMPLETYPE_FLOAT => (SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING)
1103+ - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT => SG_SAMPLERTYPE_NONFILTERING
1104+ - SG_IMAGESAMPLETYPE_SINT => SG_SAMPLERTYPE_NONFILTERING
1105+ - SG_IMAGESAMPLETYPE_UINT => SG_SAMPLERTYPE_NONFILTERING
1106+ - SG_IMAGESAMPLETYPE_DEPTH => SG_SAMPLERTYPE_COMPARISON
1107+
1108+ Backend-specific bindslot ranges (not relevant when using sokol-shdc):
1109+
1110+ - D3D11/HLSL:
1111+ - separate bindslot space per shader stage
1112+ - uniform block bindings (as cbuffer): `register(b0..b7)`
1113+ - texture- and readonly storage buffer bindings: `register(t0..t31)`
1114+ - read/write storage buffer and storage image bindings: `register(u0..u31)`
1115+ - samplers: `register(s0..s11)`
1116+ - Metal/MSL:
1117+ - separate bindslot space per shader stage
1118+ - uniform blocks: `[[buffer(0..7)]]`
1119+ - storage buffers: `[[buffer(8..23)]]`
1120+ - textures and storage image bindings: `[[texture(0..31)]]`
1121+ - samplers: `[[sampler(0..11)]]`
1122+ - WebGPU/WGSL:
1123+ - common bindslot space across shader stages
1124+ - uniform blocks: `@group(0) @binding(0..15)`
1125+ - textures, storage-images, storage-buffers and sampler: `@group(1) @binding(0..127)`
1126+ - GL/GLSL:
1127+ - uniforms and image-samplers are bound by name
1128+ - storage buffer bindings: `layout(std430, binding=0..sg_limits.max_storage_buffer_bindings_per_stage` (common
1129+ bindslot space across shader stages)
1130+ - storage image bindings: `layout(binding=0..sg_limits.max_storage_image_bindings_per_stage, [access_format])`
1131+
1132+ For example code of how to create backend-specific shader objects,
1133+ please refer to the following samples:
1134+
1135+ - for D3D11: https://github.com/floooh/sokol-samples/tree/master/d3d11
1136+ - for Metal: https://github.com/floooh/sokol-samples/tree/master/metal
1137+ - for OpenGL: https://github.com/floooh/sokol-samples/tree/master/glfw
1138+ - for GLES3: https://github.com/floooh/sokol-samples/tree/master/html5
1139+ - for WebGPU: https://github.com/floooh/sokol-samples/tree/master/wgpu
1140+
1141+
1142+ ON SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT AND SG_SAMPLERTYPE_NONFILTERING
1143+ ========================================================================
1144+ The WebGPU backend introduces the concept of 'unfilterable-float' textures,
1145+ which can only be combined with 'nonfiltering' samplers (this is a restriction
1146+ specific to WebGPU, but since the same sokol-gfx code should work across
1147+ all backend, the sokol-gfx validation layer also enforces this restriction
1148+ - the alternative would be undefined behaviour in some backend APIs on
1149+ some devices).
1150+
1151+ The background is that some mobile devices (most notably iOS devices) can
1152+ not perform linear filtering when sampling textures with certain pixel
1153+ formats, most notable the 32F formats:
1154+
1155+ - SG_PIXELFORMAT_R32F
1156+ - SG_PIXELFORMAT_RG32F
1157+ - SG_PIXELFORMAT_RGBA32F
1158+
1159+ The information of whether a shader is going to be used with such an
1160+ unfilterable-float texture must already be provided in the sg_shader_desc
1161+ struct when creating the shader (see the above section "ON SHADER CREATION").
1162+
1163+ If you are using the sokol-shdc shader compiler, the information whether a
1164+ texture/sampler binding expects an 'unfilterable-float/nonfiltering'
1165+ texture/sampler combination cannot be inferred from the shader source
1166+ alone, you'll need to provide this hint via annotation-tags. For instance
1167+ here is an example from the ozz-skin-sapp.c sample shader which samples an
1168+ RGBA32F texture with skinning matrices in the vertex shader:
1169+
1170+ ```glsl
1171+ @image_sample_type joint_tex unfilterable_float
1172+ uniform texture2D joint_tex;
1173+ @sampler_type smp nonfiltering
1174+ uniform sampler smp;
1175+ ```
1176+
1177+ This will result in SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT and
1178+ SG_SAMPLERTYPE_NONFILTERING being written to the code-generated
1179+ sg_shader_desc struct.
1180+
1181+
1182+ ON VERTEX FORMATS
1183+ =================
1184+ Sokol-gfx implements the same strict mapping rules from CPU-side
1185+ vertex component formats to GPU-side vertex input data types:
1186+
1187+ - float and packed normalized CPU-side formats must be used as
1188+ floating point base type in the vertex shader
1189+ - packed signed-integer CPU-side formats must be used as signed
1190+ integer base type in the vertex shader
1191+ - packed unsigned-integer CPU-side formats must be used as unsigned
1192+ integer base type in the vertex shader
1193+
1194+ These mapping rules are enforced by the sokol-gfx validation layer,
1195+ but only when sufficient reflection information is provided in
1196+ `sg_shader_desc.attrs[].base_type`. This is the case when sokol-shdc
1197+ is used, otherwise the default base_type will be SG_SHADERATTRBASETYPE_UNDEFINED
1198+ which causes the sokol-gfx validation check to be skipped (of course you
1199+ can also provide the per-attribute base type information manually when
1200+ not using sokol-shdc).
1201+
1202+ The detailed mapping rules from SG_VERTEXFORMAT_* to GLSL data types
1203+ are as follows:
1204+
1205+ - FLOAT[*] => float, vec*
1206+ - BYTE4N => vec* (scaled to -1.0 .. +1.0)
1207+ - UBYTE4N => vec* (scaled to 0.0 .. +1.0)
1208+ - SHORT[*]N => vec* (scaled to -1.0 .. +1.0)
1209+ - USHORT[*]N => vec* (scaled to 0.0 .. +1.0)
1210+ - INT[*] => int, ivec*
1211+ - UINT[*] => uint, uvec*
1212+ - BYTE4 => int*
1213+ - UBYTE4 => uint*
1214+ - SHORT[*] => int*
1215+ - USHORT[*] => uint*
1216+
1217+ NOTE that sokol-gfx only provides vertex formats with sizes of a multiple
1218+ of 4 (e.g. BYTE4N but not BYTE2N). This is because vertex components must
1219+ be 4-byte aligned anyway.
1220+
1221+
1222+ UNIFORM DATA LAYOUT:
1223+ ====================
1224+ NOTE: if you use the sokol-shdc shader compiler tool, you don't need to worry
1225+ about the following details.
1226+
1227+ The data that's passed into the sg_apply_uniforms() function must adhere to
1228+ specific layout rules so that the GPU shader finds the uniform block
1229+ items at the right offset.
1230+
1231+ For the D3D11 and Metal backends, sokol-gfx only cares about the size of uniform
1232+ blocks, but not about the internal layout. The data will just be copied into
1233+ a uniform/constant buffer in a single operation and it's up you to arrange the
1234+ CPU-side layout so that it matches the GPU side layout. This also means that with
1235+ the D3D11 and Metal backends you are not limited to a 'cross-platform' subset
1236+ of uniform variable types.
1237+
1238+ If you ever only use one of the D3D11, Metal *or* WebGPU backend, you can stop reading here.
1239+
1240+ For the GL backends, the internal layout of uniform blocks matters though,
1241+ and you are limited to a small number of uniform variable types. This is
1242+ because sokol-gfx must be able to locate the uniform block members in order
1243+ to upload them to the GPU with glUniformXXX() calls.
1244+
1245+ To describe the uniform block layout to sokol-gfx, the following information
1246+ must be passed to the sg_make_shader() call in the sg_shader_desc struct:
1247+
1248+ - a hint about the used packing rule (either SG_UNIFORMLAYOUT_NATIVE or
1249+ SG_UNIFORMLAYOUT_STD140)
1250+ - a list of the uniform block members types in the correct order they
1251+ appear on the CPU side
1252+
1253+ For example if the GLSL shader has the following uniform declarations:
1254+
1255+ uniform mat4 mvp;
1256+ uniform vec2 offset0;
1257+ uniform vec2 offset1;
1258+ uniform vec2 offset2;
1259+
1260+ ...and on the CPU side, there's a similar C struct:
1261+
1262+ typedef struct {
1263+ float mvp[16];
1264+ float offset0[2];
1265+ float offset1[2];
1266+ float offset2[2];
1267+ } params_t;
1268+
1269+ ...the uniform block description in the sg_shader_desc must look like this:
1270+
1271+ sg_shader_desc desc = {
1272+ .vs.uniform_blocks[0] = {
1273+ .size = sizeof(params_t),
1274+ .layout = SG_UNIFORMLAYOUT_NATIVE, // this is the default and can be omitted
1275+ .uniforms = {
1276+ // order must be the same as in 'params_t':
1277+ [0] = { .name = "mvp", .type = SG_UNIFORMTYPE_MAT4 },
1278+ [1] = { .name = "offset0", .type = SG_UNIFORMTYPE_VEC2 },
1279+ [2] = { .name = "offset1", .type = SG_UNIFORMTYPE_VEC2 },
1280+ [3] = { .name = "offset2", .type = SG_UNIFORMTYPE_VEC2 },
1281+ }
1282+ }
1283+ };
1284+
1285+ With this information sokol-gfx can now compute the correct offsets of the data items
1286+ within the uniform block struct.
1287+
1288+ The SG_UNIFORMLAYOUT_NATIVE packing rule works fine if only the GL backends are used,
1289+ but for proper D3D11/Metal/GL a subset of the std140 layout must be used which is
1290+ described in the next section:
1291+
1292+
1293+ CROSS-BACKEND COMMON UNIFORM DATA LAYOUT
1294+ ========================================
1295+ For cross-platform / cross-3D-backend code it is important that the same uniform block
1296+ layout on the CPU side can be used for all sokol-gfx backends. To achieve this,
1297+ a common subset of the std140 layout must be used:
1298+
1299+ - The uniform block layout hint in sg_shader_desc must be explicitly set to
1300+ SG_UNIFORMLAYOUT_STD140.
1301+ - Only the following GLSL uniform types can be used (with their associated sokol-gfx enums):
1302+ - float => SG_UNIFORMTYPE_FLOAT
1303+ - vec2 => SG_UNIFORMTYPE_FLOAT2
1304+ - vec3 => SG_UNIFORMTYPE_FLOAT3
1305+ - vec4 => SG_UNIFORMTYPE_FLOAT4
1306+ - int => SG_UNIFORMTYPE_INT
1307+ - ivec2 => SG_UNIFORMTYPE_INT2
1308+ - ivec3 => SG_UNIFORMTYPE_INT3
1309+ - ivec4 => SG_UNIFORMTYPE_INT4
1310+ - mat4 => SG_UNIFORMTYPE_MAT4
1311+ - Alignment for those types must be as follows (in bytes):
1312+ - float => 4
1313+ - vec2 => 8
1314+ - vec3 => 16
1315+ - vec4 => 16
1316+ - int => 4
1317+ - ivec2 => 8
1318+ - ivec3 => 16
1319+ - ivec4 => 16
1320+ - mat4 => 16
1321+ - Arrays are only allowed for the following types: vec4, int4, mat4.
1322+
1323+ Note that the HLSL cbuffer layout rules are slightly different from the
1324+ std140 layout rules, this means that the cbuffer declarations in HLSL code
1325+ must be tweaked so that the layout is compatible with std140.
1326+
1327+ The by far easiest way to tackle the common uniform block layout problem is
1328+ to use the sokol-shdc shader cross-compiler tool!
1329+
1330+
1331+ ON STORAGE BUFFERS
1332+ ==================
1333+ The two main purpose of storage buffers are:
1334+
1335+ - to be populated by compute shaders with dynamically generated data
1336+ - for providing random-access data to all shader stages
1337+
1338+ Storage buffers can be used to pass large amounts of random access structured
1339+ data from the CPU side to the shaders. They are similar to data textures, but are
1340+ more convenient to use both on the CPU and shader side since they can be accessed
1341+ in shaders as as a 1-dimensional array of struct items.
1342+
1343+ Storage buffers are *NOT* supported on the following platform/backend combos:
1344+
1345+ - macOS+GL (because storage buffers require GL 4.3, while macOS only goes up to GL 4.1)
1346+ - platforms which only support a GLES3.0 context (WebGL2 and iOS)
1347+
1348+ To use storage buffers, the following steps are required:
1349+
1350+ - write a shader which uses storage buffers (vertex- and fragment-shaders
1351+ can only read from storage buffers, while compute-shaders can both read
1352+ and write storage buffers)
1353+ - create one or more storage buffers via sg_make_buffer() with the
1354+ `.usage.storage_buffer = true`
1355+ - when creating a shader via sg_make_shader(), populate the sg_shader_desc
1356+ struct with binding info (when using sokol-shdc, this step will be taken care
1357+ of automatically)
1358+ - which storage buffer bind slots on the vertex-, fragment- or compute-stage
1359+ are occupied
1360+ - whether the storage buffer on that bind slot is readonly (readonly
1361+ bindings are required for vertex- and fragment-shaders, and in compute
1362+ shaders the readonly flag is used to control hazard tracking in some
1363+ 3D backends)
1364+
1365+ - when calling sg_apply_bindings(), apply the matching bind slots with the previously
1366+ created storage buffers
1367+ - ...and that's it.
1368+
1369+ For more details, see the following backend-agnostic sokol samples:
1370+
1371+ - simple vertex pulling from a storage buffer:
1372+ - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.c
1373+ - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.glsl
1374+ - instanced rendering via storage buffers (vertex- and instance-pulling):
1375+ - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.c
1376+ - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.glsl
1377+ - storage buffers both on the vertex- and fragment-stage:
1378+ - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.c
1379+ - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.glsl
1380+ - the Ozz animation sample rewritten to pull all rendering data from storage buffers:
1381+ - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.cc
1382+ - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.glsl
1383+ - the instancing sample modified to use compute shaders:
1384+ - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-compute-sapp.c
1385+ - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-compute-sapp.glsl
1386+ - the Compute Boids sample ported to sokol-gfx:
1387+ - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/computeboids-sapp.c
1388+ - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/computeboids-sapp.glsl
1389+
1390+ ...also see the following backend-specific vertex pulling samples (those also don't use sokol-shdc):
1391+
1392+ - D3D11: https://github.com/floooh/sokol-samples/blob/master/d3d11/vertexpulling-d3d11.c
1393+ - desktop GL: https://github.com/floooh/sokol-samples/blob/master/glfw/vertexpulling-glfw.c
1394+ - Metal: https://github.com/floooh/sokol-samples/blob/master/metal/vertexpulling-metal.c
1395+ - WebGPU: https://github.com/floooh/sokol-samples/blob/master/wgpu/vertexpulling-wgpu.c
1396+
1397+ ...and the backend specific compute shader samples:
1398+
1399+ - D3D11: https://github.com/floooh/sokol-samples/blob/master/d3d11/instancing-compute-d3d11.c
1400+ - desktop GL: https://github.com/floooh/sokol-samples/blob/master/glfw/instancing-compute-glfw.c
1401+ - Metal: https://github.com/floooh/sokol-samples/blob/master/metal/instancing-compute-metal.c
1402+ - WebGPU: https://github.com/floooh/sokol-samples/blob/master/wgpu/instancing-compute-wgpu.c
1403+
1404+ Storage buffer shader authoring caveats when using sokol-shdc:
1405+
1406+ - declare a read-only storage buffer interface block with `layout(binding=N) readonly buffer [name] { ... }`
1407+ (where 'N' is the index in `sg_bindings.storage_buffers[N]`)
1408+ - ...or a read/write storage buffer interface block with `layout(binding=N) buffer [name] { ... }`
1409+ - declare a struct which describes a single array item in the storage buffer interface block
1410+ - only put a single flexible array member into the storage buffer interface block
1411+
1412+ E.g. a complete example in 'sokol-shdc GLSL':
1413+
1414+ ```glsl
1415+ @vs
1416+ // declare a struct:
1417+ struct sb_vertex {
1418+ vec3 pos;
1419+ vec4 color;
1420+ }
1421+ // declare a buffer interface block with a single flexible struct array:
1422+ layout(binding=0) readonly buffer vertices {
1423+ sb_vertex vtx[];
1424+ }
1425+ // in the shader function, access the storage buffer like this:
1426+ void main() {
1427+ vec3 pos = vtx[gl_VertexIndex].pos;
1428+ ...
1429+ }
1430+ @end
1431+ ```
1432+
1433+ In a compute shader you can read and write the same item in the same
1434+ storage buffer (but you'll have to be careful for random access since
1435+ many threads of the same compute function run in parallel):
1436+
1437+ @cs
1438+ struct sb_item {
1439+ vec3 pos;
1440+ vec3 vel;
1441+ }
1442+ layout(binding=0) buffer items_ssbo {
1443+ sb_item items[];
1444+ }
1445+ layout(local_size_x=64, local_size_y=1, local_size_z=1) in;
1446+ void main() {
1447+ uint idx = gl_GlobalInvocationID.x;
1448+ vec3 pos = items[idx].pos;
1449+ ...
1450+ items[idx].pos = pos;
1451+ }
1452+ @end
1453+
1454+ Backend-specific storage-buffer caveats (not relevant when using sokol-shdc):
1455+
1456+ D3D11:
1457+ - storage buffers are created as 'raw' Byte Address Buffers
1458+ (https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-intro#raw-views-of-buffers)
1459+ - in HLSL, use a ByteAddressBuffer for readonly access of the buffer content:
1460+ (https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-byteaddressbuffer)
1461+ - ...or RWByteAddressBuffer for read/write access:
1462+ (https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-rwbyteaddressbuffer)
1463+ - readonly-storage buffers and textures are both bound as 'shader-resource-view' and
1464+ share the same bind slots (declared as `register(tN)` in HLSL), where N must be in the range 0..23)
1465+ - read/write storage buffers and storage images are bound as 'unordered-access-view'
1466+ (declared as `register(uN)` in HLSL where N is in the range 0..11)
1467+
1468+ Metal:
1469+ - in Metal there is no internal difference between vertex-, uniform- and
1470+ storage-buffers, all are bound to the same 'buffer bind slots' with the
1471+ following reserved ranges:
1472+ - vertex shader stage:
1473+ - uniform buffers: slots 0..7
1474+ - storage buffers: slots 8..15
1475+ - vertex buffers: slots 15..23
1476+ - fragment shader stage:
1477+ - uniform buffers: slots 0..7
1478+ - storage buffers: slots 8..15
1479+ - this means in MSL, storage buffer bindings start at [[buffer(8)]] both in
1480+ the vertex and fragment stage
1481+
1482+ GL:
1483+ - the GL backend doesn't use name-lookup to find storage buffer bindings, this
1484+ means you must annotate buffers with `layout(std430, binding=N)` in GLSL
1485+ - ...where N is 0..sg_limits.max_storage_buffer_bindings_per_stage.
1486+
1487+ WebGPU:
1488+ - in WGSL, textures, samplers and storage buffers all use a shared
1489+ bindspace across all shader stages on bindgroup 1:
1490+
1491+ `@group(1) @binding(0..127)
1492+
1493+ ON STORAGE IMAGES:
1494+ ==================
1495+ To write pixel data to texture objects in compute shaders, first an image
1496+ object must be created with `storage_image usage`:
1497+
1498+ sg_image storage_image = sg_make_image(&(sg_image_desc){
1499+ .usage.storage_image = true,
1500+ },
1501+ .width = ...,
1502+ .height = ...,
1503+ .pixel_format = ...,
1504+ });
1505+
1506+ Next a storage-image-view object is required which also allows to pick
1507+ a specific mip-level or slice for the compute-shader to access:
1508+
1509+ sg_view simg_view = sg_make_view(&(sg_view_desc){
1510+ .storage_image = {
1511+ .image = storage_image,
1512+ .mip_level = ...,
1513+ .slice = ...
1514+ },
1515+ });
1516+
1517+ Finally 'bind' the storage-image-view via a regular sg_apply_bindings() call
1518+ inside a compute pass:
1519+
1520+ sg_begin_pass(&(sg_pass){ .compute = true });
1521+ sg_apply_pipeline(...);
1522+ sg_apply_bindings(&(sg_bindings){
1523+ .views[VIEW_simg] = simg_view,
1524+ });
1525+ sg_dispatch(...);
1526+ sg_end_pass();
1527+
1528+ Currently, storage images can only be used with `readwrite` or `writeonly` access in
1529+ shaders. For readonly access use a regular texture binding instead.
1530+
1531+ For an example of using storage images in compute shaders see imageblur-sapp:
1532+
1533+ - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/imageblur-sapp.c
1534+ - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/imageblur-sapp.glsl
1535+
1536+ TRACE HOOKS:
1537+ ============
1538+ sokol_gfx.h optionally allows to install "trace hook" callbacks for
1539+ each public API functions. When a public API function is called, and
1540+ a trace hook callback has been installed for this function, the
1541+ callback will be invoked with the parameters and result of the function.
1542+ This is useful for things like debugging- and profiling-tools, or
1543+ keeping track of resource creation and destruction.
1544+
1545+ To use the trace hook feature:
1546+
1547+ --- Define SOKOL_TRACE_HOOKS before including the implementation.
1548+
1549+ --- Setup an sg_trace_hooks structure with your callback function
1550+ pointers (keep all function pointers you're not interested
1551+ in zero-initialized), optionally set the user_data member
1552+ in the sg_trace_hooks struct.
1553+
1554+ --- Install the trace hooks by calling sg_install_trace_hooks(),
1555+ the return value of this function is another sg_trace_hooks
1556+ struct which contains the previously set of trace hooks.
1557+ You should keep this struct around, and call those previous
1558+ functions pointers from your own trace callbacks for proper
1559+ chaining.
1560+
1561+ As an example of how trace hooks are used, have a look at the
1562+ imgui/sokol_gfx_imgui.h header which implements a realtime
1563+ debugging UI for sokol_gfx.h on top of Dear ImGui.
1564+
1565+
1566+ MEMORY ALLOCATION OVERRIDE
1567+ ==========================
1568+ You can override the memory allocation functions at initialization time
1569+ like this:
1570+
1571+ void* my_alloc(size_t size, void* user_data) {
1572+ return malloc(size);
1573+ }
1574+
1575+ void my_free(void* ptr, void* user_data) {
1576+ free(ptr);
1577+ }
1578+
1579+ ...
1580+ sg_setup(&(sg_desc){
1581+ // ...
1582+ .allocator = {
1583+ .alloc_fn = my_alloc,
1584+ .free_fn = my_free,
1585+ .user_data = ...,
1586+ }
1587+ });
1588+ ...
1589+
1590+ If no overrides are provided, malloc and free will be used.
1591+
1592+ This only affects memory allocation calls done by sokol_gfx.h
1593+ itself though, not any allocations in OS libraries.
1594+
1595+
1596+ ERROR REPORTING AND LOGGING
1597+ ===========================
1598+ To get any logging information at all you need to provide a logging callback in the setup call
1599+ the easiest way is to use sokol_log.h:
1600+
1601+ #include "sokol_log.h"
1602+
1603+ sg_setup(&(sg_desc){ .logger.func = slog_func });
1604+
1605+ To override logging with your own callback, first write a logging function like this:
1606+
1607+ void my_log(const char* tag, // e.g. 'sg'
1608+ uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info
1609+ uint32_t log_item_id, // SG_LOGITEM_*
1610+ const char* message_or_null, // a message string, may be nullptr in release mode
1611+ uint32_t line_nr, // line number in sokol_gfx.h
1612+ const char* filename_or_null, // source filename, may be nullptr in release mode
1613+ void* user_data)
1614+ {
1615+ ...
1616+ }
1617+
1618+ ...and then setup sokol-gfx like this:
1619+
1620+ sg_setup(&(sg_desc){
1621+ .logger = {
1622+ .func = my_log,
1623+ .user_data = my_user_data,
1624+ }
1625+ });
1626+
1627+ The provided logging function must be reentrant (e.g. be callable from
1628+ different threads).
1629+
1630+ If you don't want to provide your own custom logger it is highly recommended to use
1631+ the standard logger in sokol_log.h instead, otherwise you won't see any warnings or
1632+ errors.
1633+
1634+
1635+ COMMIT LISTENERS
1636+ ================
1637+ It's possible to hook callback functions into sokol-gfx which are called from
1638+ inside sg_commit() in unspecified order. This is mainly useful for libraries
1639+ that build on top of sokol_gfx.h to be notified about the end/start of a frame.
1640+
1641+ To add a commit listener, call:
1642+
1643+ static void my_commit_listener(void* user_data) {
1644+ ...
1645+ }
1646+
1647+ bool success = sg_add_commit_listener((sg_commit_listener){
1648+ .func = my_commit_listener,
1649+ .user_data = ...,
1650+ });
1651+
1652+ The function returns false if the internal array of commit listeners is full,
1653+ or the same commit listener had already been added.
1654+
1655+ If the function returns true, my_commit_listener() will be called each frame
1656+ from inside sg_commit().
1657+
1658+ By default, 1024 distinct commit listeners can be added, but this number
1659+ can be tweaked in the sg_setup() call:
1660+
1661+ sg_setup(&(sg_desc){
1662+ .max_commit_listeners = 2048,
1663+ });
1664+
1665+ An sg_commit_listener item is equal to another if both the function
1666+ pointer and user_data field are equal.
1667+
1668+ To remove a commit listener:
1669+
1670+ bool success = sg_remove_commit_listener((sg_commit_listener){
1671+ .func = my_commit_listener,
1672+ .user_data = ...,
1673+ });
1674+
1675+ ...where the .func and .user_data field are equal to a previous
1676+ sg_add_commit_listener() call. The function returns true if the commit
1677+ listener item was found and removed, and false otherwise.
1678+
1679+
1680+ RESOURCE CREATION AND DESTRUCTION IN DETAIL
1681+ ===========================================
1682+ The 'vanilla' way to create resource objects is with the 'make functions':
1683+
1684+ sg_buffer sg_make_buffer(const sg_buffer_desc* desc)
1685+ sg_image sg_make_image(const sg_image_desc* desc)
1686+ sg_sampler sg_make_sampler(const sg_sampler_desc* desc)
1687+ sg_shader sg_make_shader(const sg_shader_desc* desc)
1688+ sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc)
1689+ sg_view sg_make_view(const sg_view_desc* desc)
1690+
1691+ This will result in one of three cases:
1692+
1693+ 1. The returned handle is invalid. This happens when there are no more
1694+ free slots in the resource pool for this resource type. An invalid
1695+ handle is associated with the INVALID resource state, for instance:
1696+
1697+ sg_buffer buf = sg_make_buffer(...)
1698+ if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_INVALID) {
1699+ // buffer pool is exhausted
1700+ }
1701+
1702+ 2. The returned handle is valid, but creating the underlying resource
1703+ has failed for some reason. This results in a resource object in the
1704+ FAILED state. The reason *why* resource creation has failed differ
1705+ by resource type. Look for log messages with more details. A failed
1706+ resource state can be checked with:
1707+
1708+ sg_buffer buf = sg_make_buffer(...)
1709+ if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED) {
1710+ // creating the resource has failed
1711+ }
1712+
1713+ 3. And finally, if everything goes right, the returned resource is
1714+ in resource state VALID and ready to use. This can be checked
1715+ with:
1716+
1717+ sg_buffer buf = sg_make_buffer(...)
1718+ if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID) {
1719+ // creating the resource has succeeded
1720+ }
1721+
1722+ When calling the 'make functions', the created resource goes through a number
1723+ of states:
1724+
1725+ - INITIAL: the resource slot associated with the new resource is currently
1726+ free (technically, there is no resource yet, just an empty pool slot)
1727+ - ALLOC: a handle for the new resource has been allocated, this just means
1728+ a pool slot has been reserved.
1729+ - VALID or FAILED: in VALID state any 3D API backend resource objects have
1730+ been successfully created, otherwise if anything went wrong, the resource
1731+ will be in FAILED state.
1732+
1733+ Sometimes it makes sense to first grab a handle, but initialize the
1734+ underlying resource at a later time. For instance when loading data
1735+ asynchronously from a slow data source, you may know what buffers and
1736+ textures are needed at an early stage of the loading process, but actually
1737+ loading the buffer or texture content can only be completed at a later time.
1738+
1739+ For such situations, sokol-gfx resource objects can be created in two steps.
1740+ You can allocate a handle upfront with one of the 'alloc functions':
1741+
1742+ sg_buffer sg_alloc_buffer(void)
1743+ sg_image sg_alloc_image(void)
1744+ sg_sampler sg_alloc_sampler(void)
1745+ sg_shader sg_alloc_shader(void)
1746+ sg_pipeline sg_alloc_pipeline(void)
1747+ sg_view sg_alloc_view(void)
1748+
1749+ This will return a handle with the underlying resource object in the
1750+ ALLOC state:
1751+
1752+ sg_image img = sg_alloc_image();
1753+ if (sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC) {
1754+ // allocating an image handle has succeeded, otherwise
1755+ // the image pool is full
1756+ }
1757+
1758+ Such an 'incomplete' handle can be used in most sokol-gfx rendering functions
1759+ without doing any harm, sokol-gfx will simply skip any rendering operation
1760+ that involve resources which are not in VALID state.
1761+
1762+ At a later time (for instance once the texture has completed loading
1763+ asynchronously), the resource creation can be completed by calling one of
1764+ the 'init functions', those functions take an existing resource handle and
1765+ 'desc struct':
1766+
1767+ void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc)
1768+ void sg_init_image(sg_image img, const sg_image_desc* desc)
1769+ void sg_init_sampler(sg_sampler smp, const sg_sampler_desc* desc)
1770+ void sg_init_shader(sg_shader shd, const sg_shader_desc* desc)
1771+ void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc)
1772+ void sg_init_view(sg_view view, const sg_view_desc* desc)
1773+
1774+ The init functions expect a resource in ALLOC state, and after the function
1775+ returns, the resource will be either in VALID or FAILED state. Calling
1776+ an 'alloc function' followed by the matching 'init function' is fully
1777+ equivalent with calling the 'make function' alone.
1778+
1779+ Destruction can also happen as a two-step process. The 'uninit functions'
1780+ will put a resource object from the VALID or FAILED state back into the
1781+ ALLOC state:
1782+
1783+ void sg_uninit_buffer(sg_buffer buf)
1784+ void sg_uninit_image(sg_image img)
1785+ void sg_uninit_sampler(sg_sampler smp)
1786+ void sg_uninit_shader(sg_shader shd)
1787+ void sg_uninit_pipeline(sg_pipeline pip)
1788+ void sg_uninit_view(sg_view view)
1789+
1790+ Calling the 'uninit functions' with a resource that is not in the VALID or
1791+ FAILED state is a no-op.
1792+
1793+ To finally free the pool slot for recycling call the 'dealloc functions':
1794+
1795+ void sg_dealloc_buffer(sg_buffer buf)
1796+ void sg_dealloc_image(sg_image img)
1797+ void sg_dealloc_sampler(sg_sampler smp)
1798+ void sg_dealloc_shader(sg_shader shd)
1799+ void sg_dealloc_pipeline(sg_pipeline pip)
1800+ void sg_dealloc_view(sg_view view)
1801+
1802+ Calling the 'dealloc functions' on a resource that's not in ALLOC state is
1803+ a no-op, but will generate a warning log message.
1804+
1805+ Calling an 'uninit function' and 'dealloc function' in sequence is equivalent
1806+ with calling the associated 'destroy function':
1807+
1808+ void sg_destroy_buffer(sg_buffer buf)
1809+ void sg_destroy_image(sg_image img)
1810+ void sg_destroy_sampler(sg_sampler smp)
1811+ void sg_destroy_shader(sg_shader shd)
1812+ void sg_destroy_pipeline(sg_pipeline pip)
1813+ void sg_destroy_view(sg_view view)
1814+
1815+ The 'destroy functions' can be called on resources in any state and generally
1816+ do the right thing (for instance if the resource is in ALLOC state, the destroy
1817+ function will be equivalent to the 'dealloc function' and skip the 'uninit part').
1818+
1819+ And finally to close the circle, the 'fail functions' can be called to manually
1820+ put a resource in ALLOC state into the FAILED state:
1821+
1822+ sg_fail_buffer(sg_buffer buf)
1823+ sg_fail_image(sg_image img)
1824+ sg_fail_sampler(sg_sampler smp)
1825+ sg_fail_shader(sg_shader shd)
1826+ sg_fail_pipeline(sg_pipeline pip)
1827+ sg_fail_view(sg_view view)
1828+
1829+ This is recommended if anything went wrong outside of sokol-gfx during asynchronous
1830+ resource setup (for instance a file loading operation failed). In this case,
1831+ the 'fail function' should be called instead of the 'init function'.
1832+
1833+ Calling a 'fail function' on a resource that's not in ALLOC state is a no-op,
1834+ but will generate a warning log message.
1835+
1836+ NOTE: that two-step resource creation usually only makes sense for buffers,
1837+ images and views, but not for samplers, shaders or pipelines. Most notably, trying
1838+ to create a pipeline object with a shader that's not in VALID state will
1839+ trigger a validation layer error, or if the validation layer is disabled,
1840+ result in a pipeline object in FAILED state.
1841+
1842+
1843+ WEBGPU CAVEATS
1844+ ==============
1845+ For a general overview and design notes of the WebGPU backend see:
1846+
1847+ https://floooh.github.io/2023/10/16/sokol-webgpu.html
1848+
1849+ In general, don't expect an automatic speedup when switching from the WebGL2
1850+ backend to the WebGPU backend. Some WebGPU functions currently actually
1851+ have a higher CPU overhead than similar WebGL2 functions, leading to the
1852+ paradoxical situation that some WebGPU code may be slower than similar WebGL2
1853+ code.
1854+
1855+ - when writing WGSL shader code by hand, a specific bind-slot convention
1856+ must be used:
1857+
1858+ All uniform block structs must use `@group(0)` and bindings in the
1859+ range 0..15
1860+
1861+ @group(0) @binding(0..15)
1862+
1863+ All textures, samplers, storage-buffers and storage-images must use `@group(1)`
1864+ and bindings must be in the range 0..127:
1865+
1866+ @group(1) @binding(0..127)
1867+
1868+ Note that the number of texture, sampler, storage-buffer storage-image bindings
1869+ is still limited despite the large bind range:
1870+
1871+ - up to 16 textures and sampler across all shader stages
1872+ - up to 8 storage buffers across all shader stages
1873+ - up to 4 storage images on the compute shader stage
1874+
1875+ If you use sokol-shdc to generate WGSL shader code, you don't need to worry
1876+ about the above binding conventions since sokol-shdc will allocate
1877+ the WGSL bindslots).
1878+
1879+ - The sokol-gfx WebGPU backend uses the sg_desc.uniform_buffer_size item
1880+ to allocate a single per-frame uniform buffer which must be big enough
1881+ to hold all data written by sg_apply_uniforms() during a single frame,
1882+ including a worst-case 256-byte alignment (e.g. each sg_apply_uniform
1883+ call will cost at least 256 bytes of uniform buffer size). The default size
1884+ is 4 MB, which is enough for 16384 sg_apply_uniform() calls per
1885+ frame (assuming the uniform data 'payload' is less than 256 bytes
1886+ per call). These rules are the same as for the Metal backend, so if
1887+ you are already using the Metal backend you'll be fine.
1888+
1889+ - sg_apply_bindings(): the sokol-gfx WebGPU backend implements a bindgroup
1890+ cache to prevent excessive creation and destruction of BindGroup objects
1891+ when calling sg_apply_bindings(). The number of slots in the bindgroups
1892+ cache is defined in sg_desc.wgpu.bindgroups_cache_size when calling
1893+ sg_setup. The cache size must be a power-of-2 number, with the default being
1894+ 1024. The bindgroups cache behaviour can be observed by calling the new
1895+ function sg_query_stats(), where the following struct items are
1896+ of interest:
1897+
1898+ .wgpu.num_bindgroup_cache_hits
1899+ .wgpu.num_bindgroup_cache_misses
1900+ .wgpu.num_bindgroup_cache_collisions
1901+ .wgpu_num_bindgroup_cache_invalidates
1902+ .wgpu.num_bindgroup_cache_vs_hash_key_mismatch
1903+
1904+ The value to pay attention to is `.wgpu.num_bindgroup_cache_collisions`,
1905+ if this number is consistently higher than a few percent of the
1906+ .wgpu.num_set_bindgroup value, it might be a good idea to bump the
1907+ bindgroups cache size to the next power-of-2.
1908+
1909+ - sg_apply_viewport(): WebGPU currently has a unique restriction that viewport
1910+ rectangles must be contained entirely within the framebuffer. As a shitty
1911+ workaround sokol_gfx.h will clip incoming viewport rectangles against
1912+ the framebuffer, but this will distort the clipspace-to-screenspace mapping.
1913+ There's no proper way to handle this inside sokol_gfx.h, this must be fixed
1914+ in a future WebGPU update (see: https://github.com/gpuweb/gpuweb/issues/373
1915+ and https://github.com/gpuweb/gpuweb/pull/5025)
1916+
1917+ - The sokol shader compiler generally adds `diagnostic(off, derivative_uniformity);`
1918+ into the WGSL output. Currently only the Chrome WebGPU implementation seems
1919+ to recognize this.
1920+
1921+ - Likewise, the following sokol-gfx pixel formats are not supported in WebGPU:
1922+ R16, R16SN, RG16, RG16SN, RGBA16, RGBA16SN.
1923+ Unlike unsupported vertex formats, unsupported pixel formats can be queried
1924+ in cross-backend code via sg_query_pixelformat() though.
1925+
1926+ - The Emscripten WebGPU shim currently doesn't support the Closure minification
1927+ post-link-step (e.g. currently the emcc argument '--closure 1' or '--closure 2'
1928+ will generate broken Javascript code.
1929+
1930+ - sokol-gfx requires the WebGPU device feature `depth32float-stencil8` to be enabled
1931+ (this should be widely supported)
1932+
1933+ - sokol-gfx expects that the WebGPU device feature `float32-filterable` to *not* be
1934+ enabled (since this would exclude all iOS devices)
1935+
1936+
1937+ LICENSE
1938+ =======
1939+ zlib/libpng license
1940+
1941+ Copyright (c) 2018 Andre Weissflog
1942+
1943+ This software is provided 'as-is', without any express or implied warranty.
1944+ In no event will the authors be held liable for any damages arising from the
1945+ use of this software.
1946+
1947+ Permission is granted to anyone to use this software for any purpose,
1948+ including commercial applications, and to alter it and redistribute it
1949+ freely, subject to the following restrictions:
1950+
1951+ 1. The origin of this software must not be misrepresented; you must not
1952+ claim that you wrote the original software. If you use this software in a
1953+ product, an acknowledgment in the product documentation would be
1954+ appreciated but is not required.
1955+
1956+ 2. Altered source versions must be plainly marked as such, and must not
1957+ be misrepresented as being the original software.
1958+
1959+ 3. This notice may not be removed or altered from any source
1960+ distribution.
1961+*/
1962+#define SOKOL_GFX_INCLUDED (1)
1963+#include <stddef.h> // size_t
1964+#include <stdint.h>
1965+#include <stdbool.h>
1966+
1967+#if defined(SOKOL_API_DECL) && !defined(SOKOL_GFX_API_DECL)
1968+#define SOKOL_GFX_API_DECL SOKOL_API_DECL
1969+#endif
1970+#ifndef SOKOL_GFX_API_DECL
1971+#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GFX_IMPL)
1972+#define SOKOL_GFX_API_DECL __declspec(dllexport)
1973+#elif defined(_WIN32) && defined(SOKOL_DLL)
1974+#define SOKOL_GFX_API_DECL __declspec(dllimport)
1975+#else
1976+#define SOKOL_GFX_API_DECL extern
1977+#endif
1978+#endif
1979+
1980+#ifdef __cplusplus
1981+extern "C" {
1982+#endif
1983+
1984+/*
1985+ Resource id typedefs:
1986+
1987+ sg_buffer: vertex- and index-buffers
1988+ sg_image: images used as textures and render-pass attachments
1989+ sg_sampler sampler objects describing how a texture is sampled in a shader
1990+ sg_shader: vertex- and fragment-shaders and shader interface information
1991+ sg_pipeline: associated shader and vertex-layouts, and render states
1992+ sg_view: a resource view object used for bindings and render-pass attachments
1993+
1994+ Instead of pointers, resource creation functions return a 32-bit
1995+ handle which uniquely identifies the resource object.
1996+
1997+ The 32-bit resource id is split into a 16-bit pool index in the lower bits,
1998+ and a 16-bit 'generation counter' in the upper bits. The index allows fast
1999+ pool lookups, and combined with the generation-counter it allows to detect
2000+ 'dangling accesses' (trying to use an object which no longer exists, and
2001+ its pool slot has been reused for a new object)
2002+
2003+ The resource ids are wrapped into a strongly-typed struct so that
2004+ trying to pass an incompatible resource id is a compile error.
2005+*/
2006+typedef struct sg_buffer { uint32_t id; } sg_buffer;
2007+typedef struct sg_image { uint32_t id; } sg_image;
2008+typedef struct sg_sampler { uint32_t id; } sg_sampler;
2009+typedef struct sg_shader { uint32_t id; } sg_shader;
2010+typedef struct sg_pipeline { uint32_t id; } sg_pipeline;
2011+typedef struct sg_view { uint32_t id; } sg_view;
2012+
2013+/*
2014+ sg_range is a pointer-size-pair struct used to pass memory blobs into
2015+ sokol-gfx. When initialized from a value type (array or struct), you can
2016+ use the SG_RANGE() macro to build an sg_range struct. For functions which
2017+ take either a sg_range pointer, or a (C++) sg_range reference, use the
2018+ SG_RANGE_REF macro as a solution which compiles both in C and C++.
2019+*/
2020+typedef struct sg_range {
2021+ const void* ptr;
2022+ size_t size;
2023+} sg_range;
2024+
2025+// disabling this for every includer isn't great, but the warnings are also quite pointless
2026+#if defined(_MSC_VER)
2027+#pragma warning(disable:4221) // /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y'
2028+#pragma warning(disable:4204) // VS2015: nonstandard extension used: non-constant aggregate initializer
2029+#endif
2030+#if defined(__cplusplus)
2031+#define SG_RANGE(x) sg_range{ &x, sizeof(x) }
2032+#define SG_RANGE_REF(x) sg_range{ &x, sizeof(x) }
2033+#else
2034+#define SG_RANGE(x) (sg_range){ &x, sizeof(x) }
2035+#define SG_RANGE_REF(x) &(sg_range){ &x, sizeof(x) }
2036+#endif
2037+
2038+// various compile-time constants in the public API
2039+enum {
2040+ SG_INVALID_ID = 0,
2041+ SG_NUM_INFLIGHT_FRAMES = 2,
2042+ SG_MAX_COLOR_ATTACHMENTS = 8,
2043+ SG_MAX_UNIFORMBLOCK_MEMBERS = 16,
2044+ SG_MAX_VERTEX_ATTRIBUTES = 16,
2045+ SG_MAX_MIPMAPS = 16,
2046+ SG_MAX_VERTEXBUFFER_BINDSLOTS = 8,
2047+ SG_MAX_UNIFORMBLOCK_BINDSLOTS = 8,
2048+ SG_MAX_VIEW_BINDSLOTS = 32,
2049+ SG_MAX_SAMPLER_BINDSLOTS = 12,
2050+ SG_MAX_TEXTURE_SAMPLER_PAIRS = 32, // same as SG_MAX_VIEW_BINDSLOTS
2051+ SG_MAX_PORTABLE_COLOR_ATTACHMENTS = 4,
2052+ SG_MAX_PORTABLE_TEXTURE_BINDINGS_PER_STAGE = 16,
2053+ SG_MAX_PORTABLE_STORAGEBUFFER_BINDINGS_PER_STAGE = 8, // assuming sg_features.compute = true
2054+ SG_MAX_PORTABLE_STORAGEIMAGE_BINDINGS_PER_STAGE = 4, // assuming sg_features.compute = true
2055+};
2056+
2057+/*
2058+ sg_color
2059+
2060+ An RGBA color value.
2061+*/
2062+typedef struct sg_color { float r, g, b, a; } sg_color;
2063+
2064+/*
2065+ sg_backend
2066+
2067+ The active 3D-API backend, use the function sg_query_backend()
2068+ to get the currently active backend.
2069+*/
2070+typedef enum sg_backend {
2071+ SG_BACKEND_GLCORE,
2072+ SG_BACKEND_GLES3,
2073+ SG_BACKEND_D3D11,
2074+ SG_BACKEND_METAL_IOS,
2075+ SG_BACKEND_METAL_MACOS,
2076+ SG_BACKEND_METAL_SIMULATOR,
2077+ SG_BACKEND_WGPU,
2078+ SG_BACKEND_VULKAN,
2079+ SG_BACKEND_DUMMY,
2080+} sg_backend;
2081+
2082+/*
2083+ sg_pixel_format
2084+
2085+ sokol_gfx.h basically uses the same pixel formats as WebGPU, since these
2086+ are supported on most newer GPUs.
2087+
2088+ A pixelformat name consist of three parts:
2089+
2090+ - components (R, RG, RGB or RGBA)
2091+ - bit width per component (8, 16 or 32)
2092+ - component data type:
2093+ - unsigned normalized (no postfix)
2094+ - signed normalized (SN postfix)
2095+ - unsigned integer (UI postfix)
2096+ - signed integer (SI postfix)
2097+ - float (F postfix)
2098+
2099+ Not all pixel formats can be used for everything, call sg_query_pixelformat()
2100+ to inspect the capabilities of a given pixelformat. The function returns
2101+ an sg_pixelformat_info struct with the following members:
2102+
2103+ - sample: the pixelformat can be sampled as texture at least with
2104+ nearest filtering
2105+ - filter: the pixelformat can be sampled as texture with linear
2106+ filtering
2107+ - render: the pixelformat can be used as render-pass attachment
2108+ - blend: blending is supported when used as render-pass attachment
2109+ - msaa: multisample-antialiasing is supported when used
2110+ as render-pass attachment
2111+ - depth: the pixelformat can be used for depth-stencil attachments
2112+ - compressed: this is a block-compressed format
2113+ - bytes_per_pixel: the numbers of bytes in a pixel (0 for compressed formats)
2114+
2115+ The default pixel format for texture images is SG_PIXELFORMAT_RGBA8.
2116+
2117+ The default pixel format for render target images is platform-dependent
2118+ and taken from the sg_environment struct passed into sg_setup(). Typically
2119+ the default formats are:
2120+
2121+ - for the Metal, D3D11 and WebGPU backends: SG_PIXELFORMAT_BGRA8
2122+ - for GL backends: SG_PIXELFORMAT_RGBA8
2123+*/
2124+typedef enum sg_pixel_format {
2125+ _SG_PIXELFORMAT_DEFAULT, // value 0 reserved for default-init
2126+ SG_PIXELFORMAT_NONE,
2127+
2128+ SG_PIXELFORMAT_R8,
2129+ SG_PIXELFORMAT_R8SN,
2130+ SG_PIXELFORMAT_R8UI,
2131+ SG_PIXELFORMAT_R8SI,
2132+
2133+ SG_PIXELFORMAT_R16,
2134+ SG_PIXELFORMAT_R16SN,
2135+ SG_PIXELFORMAT_R16UI,
2136+ SG_PIXELFORMAT_R16SI,
2137+ SG_PIXELFORMAT_R16F,
2138+ SG_PIXELFORMAT_RG8,
2139+ SG_PIXELFORMAT_RG8SN,
2140+ SG_PIXELFORMAT_RG8UI,
2141+ SG_PIXELFORMAT_RG8SI,
2142+
2143+ SG_PIXELFORMAT_R32UI,
2144+ SG_PIXELFORMAT_R32SI,
2145+ SG_PIXELFORMAT_R32F,
2146+ SG_PIXELFORMAT_RG16,
2147+ SG_PIXELFORMAT_RG16SN,
2148+ SG_PIXELFORMAT_RG16UI,
2149+ SG_PIXELFORMAT_RG16SI,
2150+ SG_PIXELFORMAT_RG16F,
2151+ SG_PIXELFORMAT_RGBA8,
2152+ SG_PIXELFORMAT_SRGB8A8,
2153+ SG_PIXELFORMAT_RGBA8SN,
2154+ SG_PIXELFORMAT_RGBA8UI,
2155+ SG_PIXELFORMAT_RGBA8SI,
2156+ SG_PIXELFORMAT_BGRA8,
2157+ SG_PIXELFORMAT_SBGR8A8,
2158+ SG_PIXELFORMAT_RGB10A2,
2159+ SG_PIXELFORMAT_RG11B10F,
2160+ SG_PIXELFORMAT_RGB9E5,
2161+
2162+ SG_PIXELFORMAT_RG32UI,
2163+ SG_PIXELFORMAT_RG32SI,
2164+ SG_PIXELFORMAT_RG32F,
2165+ SG_PIXELFORMAT_RGBA16,
2166+ SG_PIXELFORMAT_RGBA16SN,
2167+ SG_PIXELFORMAT_RGBA16UI,
2168+ SG_PIXELFORMAT_RGBA16SI,
2169+ SG_PIXELFORMAT_RGBA16F,
2170+
2171+ SG_PIXELFORMAT_RGBA32UI,
2172+ SG_PIXELFORMAT_RGBA32SI,
2173+ SG_PIXELFORMAT_RGBA32F,
2174+
2175+ SG_PIXELFORMAT_DEPTH,
2176+ SG_PIXELFORMAT_DEPTH_STENCIL,
2177+
2178+ // NOTE: don't put any new compressed format in front of here
2179+ SG_PIXELFORMAT_BC1_RGBA,
2180+ SG_PIXELFORMAT_BC2_RGBA,
2181+ SG_PIXELFORMAT_BC3_RGBA,
2182+ SG_PIXELFORMAT_BC3_SRGBA,
2183+ SG_PIXELFORMAT_BC4_R,
2184+ SG_PIXELFORMAT_BC4_RSN,
2185+ SG_PIXELFORMAT_BC5_RG,
2186+ SG_PIXELFORMAT_BC5_RGSN,
2187+ SG_PIXELFORMAT_BC6H_RGBF,
2188+ SG_PIXELFORMAT_BC6H_RGBUF,
2189+ SG_PIXELFORMAT_BC7_RGBA,
2190+ SG_PIXELFORMAT_BC7_SRGBA,
2191+ SG_PIXELFORMAT_ETC2_RGB8,
2192+ SG_PIXELFORMAT_ETC2_SRGB8,
2193+ SG_PIXELFORMAT_ETC2_RGB8A1,
2194+ SG_PIXELFORMAT_ETC2_RGBA8,
2195+ SG_PIXELFORMAT_ETC2_SRGB8A8,
2196+ SG_PIXELFORMAT_EAC_R11,
2197+ SG_PIXELFORMAT_EAC_R11SN,
2198+ SG_PIXELFORMAT_EAC_RG11,
2199+ SG_PIXELFORMAT_EAC_RG11SN,
2200+
2201+ SG_PIXELFORMAT_ASTC_4x4_RGBA,
2202+ SG_PIXELFORMAT_ASTC_4x4_SRGBA,
2203+
2204+ _SG_PIXELFORMAT_NUM,
2205+ _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF
2206+} sg_pixel_format;
2207+
2208+/*
2209+ Runtime information about a pixel format, returned by sg_query_pixelformat().
2210+*/
2211+typedef struct sg_pixelformat_info {
2212+ bool sample; // pixel format can be sampled in shaders at least with nearest filtering
2213+ bool filter; // pixel format can be sampled with linear filtering
2214+ bool render; // pixel format can be used as render-pass attachment
2215+ bool blend; // pixel format supports alpha-blending when used as render-pass attachment
2216+ bool msaa; // pixel format supports MSAA when used as render-pass attachment
2217+ bool depth; // pixel format is a depth format
2218+ bool compressed; // true if this is a hardware-compressed format
2219+ bool read; // true if format supports compute shader read access
2220+ bool write; // true if format supports compute shader write access
2221+ int bytes_per_pixel; // NOTE: this is 0 for compressed formats, use sg_query_row_pitch() / sg_query_surface_pitch() as alternative
2222+} sg_pixelformat_info;
2223+
2224+/*
2225+ Runtime information about available optional features, returned by sg_query_features()
2226+*/
2227+typedef struct sg_features {
2228+ bool origin_top_left; // framebuffer- and texture-origin is in top left corner
2229+ bool image_clamp_to_border; // border color and clamp-to-border uv-wrap mode is supported
2230+ bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state
2231+ bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks
2232+ bool compute; // storage buffers and compute shaders are supported
2233+ bool msaa_texture_bindings; // if true, multisampled images can be bound as textures
2234+ bool separate_buffer_types; // cannot use the same buffer for vertex and indices (only WebGL2)
2235+ bool draw_base_vertex; // draw with (base vertex > 0) && (base_instance == 0) supported
2236+ bool draw_base_instance; // draw with (base instance > 0) supported
2237+ bool dual_source_blending; // dual-source-blending supported
2238+ bool vertexformat_int10_n2; // SG_VERTEXFORMAT_INT10_N2 is supported
2239+ bool gl_texture_views; // supports 'proper' texture views (GL 4.3+)
2240+} sg_features;
2241+
2242+/*
2243+ Runtime information about resource limits, returned by sg_query_limit()
2244+*/
2245+typedef struct sg_limits {
2246+ int max_image_size_2d; // max width/height of SG_IMAGETYPE_2D images
2247+ int max_image_size_cube; // max width/height of SG_IMAGETYPE_CUBE images
2248+ int max_image_size_3d; // max width/height/depth of SG_IMAGETYPE_3D images
2249+ int max_image_size_array; // max width/height of SG_IMAGETYPE_ARRAY images
2250+ int max_image_array_layers; // max number of layers in SG_IMAGETYPE_ARRAY images
2251+ int max_vertex_attrs; // max number of vertex attributes, clamped to SG_MAX_VERTEX_ATTRIBUTES
2252+ int max_color_attachments; // max number of render pass color attachments, clamped to SG_MAX_COLOR_ATTACHMENTS
2253+ int max_texture_bindings_per_stage; // max number of texture bindings per shader stage, clamped to SG_MAX_VIEW_BINDSLOTS
2254+ int max_storage_buffer_bindings_per_stage; // max number of storage buffer bindings per shader stage, clamped to SG_MAX_VIEW_BINDSLOTS
2255+ int max_storage_image_bindings_per_stage; // max number of storage image bindings per shader stage, clamped to SG_MAX_VIEW_BINDSLOTS
2256+ int gl_max_vertex_uniform_components; // GL_MAX_VERTEX_UNIFORM_COMPONENTS (only on GL backends)
2257+ int gl_max_combined_texture_image_units; // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS (only on GL backends)
2258+ int d3d11_max_unordered_access_views; // 8 on feature level 11.0, otherwise 32 (clamped to SG_MAX_VIEW_BINDSLOTS)
2259+ int vk_min_uniform_buffer_offset_alignment;
2260+} sg_limits;
2261+
2262+/*
2263+ sg_resource_state
2264+
2265+ The current state of a resource in its resource pool.
2266+ Resources start in the INITIAL state, which means the
2267+ pool slot is unoccupied and can be allocated. When a resource is
2268+ created, first an id is allocated, and the resource pool slot
2269+ is set to state ALLOC. After allocation, the resource is
2270+ initialized, which may result in the VALID or FAILED state. The
2271+ reason why allocation and initialization are separate is because
2272+ some resource types (e.g. buffers and images) might be asynchronously
2273+ initialized by the user application. If a resource which is not
2274+ in the VALID state is attempted to be used for rendering, rendering
2275+ operations will silently be dropped.
2276+
2277+ The special INVALID state is returned in sg_query_xxx_state() if no
2278+ resource object exists for the provided resource id.
2279+*/
2280+typedef enum sg_resource_state {
2281+ SG_RESOURCESTATE_INITIAL,
2282+ SG_RESOURCESTATE_ALLOC,
2283+ SG_RESOURCESTATE_VALID,
2284+ SG_RESOURCESTATE_FAILED,
2285+ SG_RESOURCESTATE_INVALID,
2286+ _SG_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF
2287+} sg_resource_state;
2288+
2289+/*
2290+ sg_index_type
2291+
2292+ Indicates whether indexed rendering (fetching vertex-indices from an
2293+ index buffer) is used, and if yes, the index data type (16- or 32-bits).
2294+
2295+ This is used in the sg_pipeline_desc.index_type member when creating a
2296+ pipeline object.
2297+
2298+ The default index type is SG_INDEXTYPE_NONE.
2299+*/
2300+typedef enum sg_index_type {
2301+ _SG_INDEXTYPE_DEFAULT, // value 0 reserved for default-init
2302+ SG_INDEXTYPE_NONE,
2303+ SG_INDEXTYPE_UINT16,
2304+ SG_INDEXTYPE_UINT32,
2305+ _SG_INDEXTYPE_NUM,
2306+ _SG_INDEXTYPE_FORCE_U32 = 0x7FFFFFFF
2307+} sg_index_type;
2308+
2309+/*
2310+ sg_image_type
2311+
2312+ Indicates the basic type of an image object (2D-texture, cubemap,
2313+ 3D-texture or 2D-array-texture). Used in the sg_image_desc.type member when
2314+ creating an image, and in sg_shader_image_desc to describe a sampled texture
2315+ in the shader (both must match and will be checked in the validation layer
2316+ when calling sg_apply_bindings).
2317+
2318+ The default image type when creating an image is SG_IMAGETYPE_2D.
2319+*/
2320+typedef enum sg_image_type {
2321+ _SG_IMAGETYPE_DEFAULT, // value 0 reserved for default-init
2322+ SG_IMAGETYPE_2D,
2323+ SG_IMAGETYPE_CUBE,
2324+ SG_IMAGETYPE_3D,
2325+ SG_IMAGETYPE_ARRAY,
2326+ _SG_IMAGETYPE_NUM,
2327+ _SG_IMAGETYPE_FORCE_U32 = 0x7FFFFFFF
2328+} sg_image_type;
2329+
2330+/*
2331+ sg_image_sample_type
2332+
2333+ The basic data type of a texture sample as expected by a shader.
2334+ Must be provided in sg_shader_image and used by the validation
2335+ layer in sg_apply_bindings() to check if the provided image object
2336+ is compatible with what the shader expects. Apart from the sokol-gfx
2337+ validation layer, WebGPU is the only backend API which actually requires
2338+ matching texture and sampler type to be provided upfront for validation
2339+ (other 3D APIs treat texture/sampler type mismatches as undefined behaviour).
2340+
2341+ NOTE that the following texture pixel formats require the use
2342+ of SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT, combined with a sampler
2343+ of type SG_SAMPLERTYPE_NONFILTERING:
2344+
2345+ - SG_PIXELFORMAT_R32F
2346+ - SG_PIXELFORMAT_RG32F
2347+ - SG_PIXELFORMAT_RGBA32F
2348+
2349+ (when using sokol-shdc, also check out the meta tags `@image_sample_type`
2350+ and `@sampler_type`)
2351+*/
2352+typedef enum sg_image_sample_type {
2353+ _SG_IMAGESAMPLETYPE_DEFAULT, // value 0 reserved for default-init
2354+ SG_IMAGESAMPLETYPE_FLOAT,
2355+ SG_IMAGESAMPLETYPE_DEPTH,
2356+ SG_IMAGESAMPLETYPE_SINT,
2357+ SG_IMAGESAMPLETYPE_UINT,
2358+ SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT,
2359+ _SG_IMAGESAMPLETYPE_NUM,
2360+ _SG_IMAGESAMPLETYPE_FORCE_U32 = 0x7FFFFFFF
2361+} sg_image_sample_type;
2362+
2363+/*
2364+ sg_sampler_type
2365+
2366+ The basic type of a texture sampler (sampling vs comparison) as
2367+ defined in a shader. Must be provided in sg_shader_sampler_desc.
2368+
2369+ sg_image_sample_type and sg_sampler_type for a texture/sampler
2370+ pair must be compatible with each other, specifically only
2371+ the following pairs are allowed:
2372+
2373+ - SG_IMAGESAMPLETYPE_FLOAT => (SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING)
2374+ - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT => SG_SAMPLERTYPE_NONFILTERING
2375+ - SG_IMAGESAMPLETYPE_SINT => SG_SAMPLERTYPE_NONFILTERING
2376+ - SG_IMAGESAMPLETYPE_UINT => SG_SAMPLERTYPE_NONFILTERING
2377+ - SG_IMAGESAMPLETYPE_DEPTH => SG_SAMPLERTYPE_COMPARISON
2378+*/
2379+typedef enum sg_sampler_type {
2380+ _SG_SAMPLERTYPE_DEFAULT,
2381+ SG_SAMPLERTYPE_FILTERING,
2382+ SG_SAMPLERTYPE_NONFILTERING,
2383+ SG_SAMPLERTYPE_COMPARISON,
2384+ _SG_SAMPLERTYPE_NUM,
2385+ _SG_SAMPLERTYPE_FORCE_U32,
2386+} sg_sampler_type;
2387+
2388+/*
2389+ sg_primitive_type
2390+
2391+ This is the common subset of 3D primitive types supported across all 3D
2392+ APIs. This is used in the sg_pipeline_desc.primitive_type member when
2393+ creating a pipeline object.
2394+
2395+ The default primitive type is SG_PRIMITIVETYPE_TRIANGLES.
2396+*/
2397+typedef enum sg_primitive_type {
2398+ _SG_PRIMITIVETYPE_DEFAULT, // value 0 reserved for default-init
2399+ SG_PRIMITIVETYPE_POINTS,
2400+ SG_PRIMITIVETYPE_LINES,
2401+ SG_PRIMITIVETYPE_LINE_STRIP,
2402+ SG_PRIMITIVETYPE_TRIANGLES,
2403+ SG_PRIMITIVETYPE_TRIANGLE_STRIP,
2404+ _SG_PRIMITIVETYPE_NUM,
2405+ _SG_PRIMITIVETYPE_FORCE_U32 = 0x7FFFFFFF
2406+} sg_primitive_type;
2407+
2408+/*
2409+ sg_filter
2410+
2411+ The filtering mode when sampling a texture image. This is
2412+ used in the sg_sampler_desc.min_filter, sg_sampler_desc.mag_filter
2413+ and sg_sampler_desc.mipmap_filter members when creating a sampler object.
2414+
2415+ For the default is SG_FILTER_NEAREST.
2416+*/
2417+typedef enum sg_filter {
2418+ _SG_FILTER_DEFAULT, // value 0 reserved for default-init
2419+ SG_FILTER_NEAREST,
2420+ SG_FILTER_LINEAR,
2421+ _SG_FILTER_NUM,
2422+ _SG_FILTER_FORCE_U32 = 0x7FFFFFFF
2423+} sg_filter;
2424+
2425+/*
2426+ sg_wrap
2427+
2428+ The texture coordinates wrapping mode when sampling a texture
2429+ image. This is used in the sg_image_desc.wrap_u, .wrap_v
2430+ and .wrap_w members when creating an image.
2431+
2432+ The default wrap mode is SG_WRAP_REPEAT.
2433+
2434+ NOTE: SG_WRAP_CLAMP_TO_BORDER is not supported on all backends
2435+ and platforms. To check for support, call sg_query_features()
2436+ and check the "clamp_to_border" boolean in the returned
2437+ sg_features struct.
2438+
2439+ Platforms which don't support SG_WRAP_CLAMP_TO_BORDER will silently fall back
2440+ to SG_WRAP_CLAMP_TO_EDGE without a validation error.
2441+*/
2442+typedef enum sg_wrap {
2443+ _SG_WRAP_DEFAULT, // value 0 reserved for default-init
2444+ SG_WRAP_REPEAT,
2445+ SG_WRAP_CLAMP_TO_EDGE,
2446+ SG_WRAP_CLAMP_TO_BORDER,
2447+ SG_WRAP_MIRRORED_REPEAT,
2448+ _SG_WRAP_NUM,
2449+ _SG_WRAP_FORCE_U32 = 0x7FFFFFFF
2450+} sg_wrap;
2451+
2452+/*
2453+ sg_border_color
2454+
2455+ The border color to use when sampling a texture, and the UV wrap
2456+ mode is SG_WRAP_CLAMP_TO_BORDER.
2457+
2458+ The default border color is SG_BORDERCOLOR_OPAQUE_BLACK
2459+*/
2460+typedef enum sg_border_color {
2461+ _SG_BORDERCOLOR_DEFAULT, // value 0 reserved for default-init
2462+ SG_BORDERCOLOR_TRANSPARENT_BLACK,
2463+ SG_BORDERCOLOR_OPAQUE_BLACK,
2464+ SG_BORDERCOLOR_OPAQUE_WHITE,
2465+ _SG_BORDERCOLOR_NUM,
2466+ _SG_BORDERCOLOR_FORCE_U32 = 0x7FFFFFFF
2467+} sg_border_color;
2468+
2469+/*
2470+ sg_vertex_format
2471+
2472+ The data type of a vertex component. This is used to describe
2473+ the layout of input vertex data when creating a pipeline object.
2474+
2475+ NOTE that specific mapping rules exist from the CPU-side vertex
2476+ formats to the vertex attribute base type in the vertex shader code
2477+ (see doc header section 'ON VERTEX FORMATS').
2478+*/
2479+typedef enum sg_vertex_format {
2480+ SG_VERTEXFORMAT_INVALID,
2481+ SG_VERTEXFORMAT_FLOAT,
2482+ SG_VERTEXFORMAT_FLOAT2,
2483+ SG_VERTEXFORMAT_FLOAT3,
2484+ SG_VERTEXFORMAT_FLOAT4,
2485+ SG_VERTEXFORMAT_INT,
2486+ SG_VERTEXFORMAT_INT2,
2487+ SG_VERTEXFORMAT_INT3,
2488+ SG_VERTEXFORMAT_INT4,
2489+ SG_VERTEXFORMAT_UINT,
2490+ SG_VERTEXFORMAT_UINT2,
2491+ SG_VERTEXFORMAT_UINT3,
2492+ SG_VERTEXFORMAT_UINT4,
2493+ SG_VERTEXFORMAT_BYTE4,
2494+ SG_VERTEXFORMAT_BYTE4N,
2495+ SG_VERTEXFORMAT_UBYTE4,
2496+ SG_VERTEXFORMAT_UBYTE4N,
2497+ SG_VERTEXFORMAT_SHORT2,
2498+ SG_VERTEXFORMAT_SHORT2N,
2499+ SG_VERTEXFORMAT_USHORT2,
2500+ SG_VERTEXFORMAT_USHORT2N,
2501+ SG_VERTEXFORMAT_SHORT4,
2502+ SG_VERTEXFORMAT_SHORT4N,
2503+ SG_VERTEXFORMAT_USHORT4,
2504+ SG_VERTEXFORMAT_USHORT4N,
2505+ SG_VERTEXFORMAT_INT10_N2,
2506+ SG_VERTEXFORMAT_UINT10_N2,
2507+ SG_VERTEXFORMAT_HALF2,
2508+ SG_VERTEXFORMAT_HALF4,
2509+ _SG_VERTEXFORMAT_NUM,
2510+ _SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF
2511+} sg_vertex_format;
2512+
2513+/*
2514+ sg_vertex_step
2515+
2516+ Defines whether the input pointer of a vertex input stream is advanced
2517+ 'per vertex' or 'per instance'. The default step-func is
2518+ SG_VERTEXSTEP_PER_VERTEX. SG_VERTEXSTEP_PER_INSTANCE is used with
2519+ instanced-rendering.
2520+
2521+ The vertex-step is part of the vertex-layout definition
2522+ when creating pipeline objects.
2523+*/
2524+typedef enum sg_vertex_step {
2525+ _SG_VERTEXSTEP_DEFAULT, // value 0 reserved for default-init
2526+ SG_VERTEXSTEP_PER_VERTEX,
2527+ SG_VERTEXSTEP_PER_INSTANCE,
2528+ _SG_VERTEXSTEP_NUM,
2529+ _SG_VERTEXSTEP_FORCE_U32 = 0x7FFFFFFF
2530+} sg_vertex_step;
2531+
2532+/*
2533+ sg_uniform_type
2534+
2535+ The data type of a uniform block member. This is used to
2536+ describe the internal layout of uniform blocks when creating
2537+ a shader object. This is only required for the GL backend, all
2538+ other backends will ignore the interior layout of uniform blocks.
2539+*/
2540+typedef enum sg_uniform_type {
2541+ SG_UNIFORMTYPE_INVALID,
2542+ SG_UNIFORMTYPE_FLOAT,
2543+ SG_UNIFORMTYPE_FLOAT2,
2544+ SG_UNIFORMTYPE_FLOAT3,
2545+ SG_UNIFORMTYPE_FLOAT4,
2546+ SG_UNIFORMTYPE_INT,
2547+ SG_UNIFORMTYPE_INT2,
2548+ SG_UNIFORMTYPE_INT3,
2549+ SG_UNIFORMTYPE_INT4,
2550+ SG_UNIFORMTYPE_MAT4,
2551+ _SG_UNIFORMTYPE_NUM,
2552+ _SG_UNIFORMTYPE_FORCE_U32 = 0x7FFFFFFF
2553+} sg_uniform_type;
2554+
2555+/*
2556+ sg_uniform_layout
2557+
2558+ A hint for the interior memory layout of uniform blocks. This is
2559+ only relevant for the GL backend where the internal layout
2560+ of uniform blocks must be known to sokol-gfx. For all other backends the
2561+ internal memory layout of uniform blocks doesn't matter, sokol-gfx
2562+ will just pass uniform data as an opaque memory blob to the
2563+ 3D backend.
2564+
2565+ SG_UNIFORMLAYOUT_NATIVE (default)
2566+ Native layout means that a 'backend-native' memory layout
2567+ is used. For the GL backend this means that uniforms
2568+ are packed tightly in memory (e.g. there are no padding
2569+ bytes).
2570+
2571+ SG_UNIFORMLAYOUT_STD140
2572+ The memory layout is a subset of std140. Arrays are only
2573+ allowed for the FLOAT4, INT4 and MAT4. Alignment is as
2574+ is as follows:
2575+
2576+ FLOAT, INT: 4 byte alignment
2577+ FLOAT2, INT2: 8 byte alignment
2578+ FLOAT3, INT3: 16 byte alignment(!)
2579+ FLOAT4, INT4: 16 byte alignment
2580+ MAT4: 16 byte alignment
2581+ FLOAT4[], INT4[]: 16 byte alignment
2582+
2583+ The overall size of the uniform block must be a multiple
2584+ of 16.
2585+
2586+ For more information search for 'UNIFORM DATA LAYOUT' in the documentation block
2587+ at the start of the header.
2588+*/
2589+typedef enum sg_uniform_layout {
2590+ _SG_UNIFORMLAYOUT_DEFAULT, // value 0 reserved for default-init
2591+ SG_UNIFORMLAYOUT_NATIVE, // default: layout depends on currently active backend
2592+ SG_UNIFORMLAYOUT_STD140, // std140: memory layout according to std140
2593+ _SG_UNIFORMLAYOUT_NUM,
2594+ _SG_UNIFORMLAYOUT_FORCE_U32 = 0x7FFFFFFF
2595+} sg_uniform_layout;
2596+
2597+/*
2598+ sg_cull_mode
2599+
2600+ The face-culling mode, this is used in the
2601+ sg_pipeline_desc.cull_mode member when creating a
2602+ pipeline object.
2603+
2604+ The default cull mode is SG_CULLMODE_NONE
2605+*/
2606+typedef enum sg_cull_mode {
2607+ _SG_CULLMODE_DEFAULT, // value 0 reserved for default-init
2608+ SG_CULLMODE_NONE,
2609+ SG_CULLMODE_FRONT,
2610+ SG_CULLMODE_BACK,
2611+ _SG_CULLMODE_NUM,
2612+ _SG_CULLMODE_FORCE_U32 = 0x7FFFFFFF
2613+} sg_cull_mode;
2614+
2615+/*
2616+ sg_face_winding
2617+
2618+ The vertex-winding rule that determines a front-facing primitive. This
2619+ is used in the member sg_pipeline_desc.face_winding
2620+ when creating a pipeline object.
2621+
2622+ The default winding is SG_FACEWINDING_CW (clockwise)
2623+*/
2624+typedef enum sg_face_winding {
2625+ _SG_FACEWINDING_DEFAULT, // value 0 reserved for default-init
2626+ SG_FACEWINDING_CCW,
2627+ SG_FACEWINDING_CW,
2628+ _SG_FACEWINDING_NUM,
2629+ _SG_FACEWINDING_FORCE_U32 = 0x7FFFFFFF
2630+} sg_face_winding;
2631+
2632+/*
2633+ sg_compare_func
2634+
2635+ The compare-function for configuring depth- and stencil-ref tests
2636+ in pipeline objects, and for texture samplers which perform a comparison
2637+ instead of regular sampling operation.
2638+
2639+ Used in the following structs:
2640+
2641+ sg_pipeline_desc
2642+ .depth
2643+ .compare
2644+ .stencil
2645+ .front.compare
2646+ .back.compare
2647+
2648+ sg_sampler_desc
2649+ .compare
2650+
2651+ The default compare func for depth- and stencil-tests is
2652+ SG_COMPAREFUNC_ALWAYS.
2653+
2654+ The default compare func for samplers is SG_COMPAREFUNC_NEVER.
2655+*/
2656+typedef enum sg_compare_func {
2657+ _SG_COMPAREFUNC_DEFAULT, // value 0 reserved for default-init
2658+ SG_COMPAREFUNC_NEVER,
2659+ SG_COMPAREFUNC_LESS,
2660+ SG_COMPAREFUNC_EQUAL,
2661+ SG_COMPAREFUNC_LESS_EQUAL,
2662+ SG_COMPAREFUNC_GREATER,
2663+ SG_COMPAREFUNC_NOT_EQUAL,
2664+ SG_COMPAREFUNC_GREATER_EQUAL,
2665+ SG_COMPAREFUNC_ALWAYS,
2666+ _SG_COMPAREFUNC_NUM,
2667+ _SG_COMPAREFUNC_FORCE_U32 = 0x7FFFFFFF
2668+} sg_compare_func;
2669+
2670+/*
2671+ sg_stencil_op
2672+
2673+ The operation performed on a currently stored stencil-value when a
2674+ comparison test passes or fails. This is used when creating a pipeline
2675+ object in the following sg_pipeline_desc struct items:
2676+
2677+ sg_pipeline_desc
2678+ .stencil
2679+ .front
2680+ .fail_op
2681+ .depth_fail_op
2682+ .pass_op
2683+ .back
2684+ .fail_op
2685+ .depth_fail_op
2686+ .pass_op
2687+
2688+ The default value is SG_STENCILOP_KEEP.
2689+*/
2690+typedef enum sg_stencil_op {
2691+ _SG_STENCILOP_DEFAULT, // value 0 reserved for default-init
2692+ SG_STENCILOP_KEEP,
2693+ SG_STENCILOP_ZERO,
2694+ SG_STENCILOP_REPLACE,
2695+ SG_STENCILOP_INCR_CLAMP,
2696+ SG_STENCILOP_DECR_CLAMP,
2697+ SG_STENCILOP_INVERT,
2698+ SG_STENCILOP_INCR_WRAP,
2699+ SG_STENCILOP_DECR_WRAP,
2700+ _SG_STENCILOP_NUM,
2701+ _SG_STENCILOP_FORCE_U32 = 0x7FFFFFFF
2702+} sg_stencil_op;
2703+
2704+/*
2705+ sg_blend_factor
2706+
2707+ The source and destination factors in blending operations.
2708+ This is used in the following members when creating a pipeline object:
2709+
2710+ sg_pipeline_desc
2711+ .colors[i]
2712+ .blend
2713+ .src_factor_rgb
2714+ .dst_factor_rgb
2715+ .src_factor_alpha
2716+ .dst_factor_alpha
2717+
2718+ The default value is SG_BLENDFACTOR_ONE for source
2719+ factors, and for the destination SG_BLENDFACTOR_ZERO if the associated
2720+ blend-op is ADD, SUBTRACT or REVERSE_SUBTRACT or SG_BLENDFACTOR_ONE
2721+ if the associated blend-op is MIN or MAX.
2722+*/
2723+typedef enum sg_blend_factor {
2724+ _SG_BLENDFACTOR_DEFAULT, // value 0 reserved for default-init
2725+ SG_BLENDFACTOR_ZERO,
2726+ SG_BLENDFACTOR_ONE,
2727+ SG_BLENDFACTOR_SRC_COLOR,
2728+ SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
2729+ SG_BLENDFACTOR_SRC_ALPHA,
2730+ SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
2731+ SG_BLENDFACTOR_DST_COLOR,
2732+ SG_BLENDFACTOR_ONE_MINUS_DST_COLOR,
2733+ SG_BLENDFACTOR_DST_ALPHA,
2734+ SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA,
2735+ SG_BLENDFACTOR_SRC_ALPHA_SATURATED,
2736+ SG_BLENDFACTOR_BLEND_COLOR,
2737+ SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR,
2738+ SG_BLENDFACTOR_BLEND_ALPHA,
2739+ SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA,
2740+ SG_BLENDFACTOR_SRC1_COLOR,
2741+ SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR,
2742+ SG_BLENDFACTOR_SRC1_ALPHA,
2743+ SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA,
2744+ _SG_BLENDFACTOR_NUM,
2745+ _SG_BLENDFACTOR_FORCE_U32 = 0x7FFFFFFF
2746+} sg_blend_factor;
2747+
2748+/*
2749+ sg_blend_op
2750+
2751+ Describes how the source and destination values are combined in the
2752+ fragment blending operation. It is used in the following struct items
2753+ when creating a pipeline object:
2754+
2755+ sg_pipeline_desc
2756+ .colors[i]
2757+ .blend
2758+ .op_rgb
2759+ .op_alpha
2760+
2761+ The default value is SG_BLENDOP_ADD.
2762+*/
2763+typedef enum sg_blend_op {
2764+ _SG_BLENDOP_DEFAULT, // value 0 reserved for default-init
2765+ SG_BLENDOP_ADD,
2766+ SG_BLENDOP_SUBTRACT,
2767+ SG_BLENDOP_REVERSE_SUBTRACT,
2768+ SG_BLENDOP_MIN,
2769+ SG_BLENDOP_MAX,
2770+ _SG_BLENDOP_NUM,
2771+ _SG_BLENDOP_FORCE_U32 = 0x7FFFFFFF
2772+} sg_blend_op;
2773+
2774+/*
2775+ sg_color_mask
2776+
2777+ Selects the active color channels when writing a fragment color to the
2778+ framebuffer. This is used in the members
2779+ sg_pipeline_desc.colors[i].write_mask when creating a pipeline object.
2780+
2781+ The default colormask is SG_COLORMASK_RGBA (write all colors channels)
2782+
2783+ NOTE: since the color mask value 0 is reserved for the default value
2784+ (SG_COLORMASK_RGBA), use SG_COLORMASK_NONE if all color channels
2785+ should be disabled.
2786+*/
2787+typedef enum sg_color_mask {
2788+ _SG_COLORMASK_DEFAULT = 0, // value 0 reserved for default-init
2789+ SG_COLORMASK_NONE = 0x10, // special value for 'all channels disabled
2790+ SG_COLORMASK_R = 0x1,
2791+ SG_COLORMASK_G = 0x2,
2792+ SG_COLORMASK_RG = 0x3,
2793+ SG_COLORMASK_B = 0x4,
2794+ SG_COLORMASK_RB = 0x5,
2795+ SG_COLORMASK_GB = 0x6,
2796+ SG_COLORMASK_RGB = 0x7,
2797+ SG_COLORMASK_A = 0x8,
2798+ SG_COLORMASK_RA = 0x9,
2799+ SG_COLORMASK_GA = 0xA,
2800+ SG_COLORMASK_RGA = 0xB,
2801+ SG_COLORMASK_BA = 0xC,
2802+ SG_COLORMASK_RBA = 0xD,
2803+ SG_COLORMASK_GBA = 0xE,
2804+ SG_COLORMASK_RGBA = 0xF,
2805+ _SG_COLORMASK_FORCE_U32 = 0x7FFFFFFF
2806+} sg_color_mask;
2807+
2808+/*
2809+ sg_load_action
2810+
2811+ Defines the load action that should be performed at the start of a render pass:
2812+
2813+ SG_LOADACTION_CLEAR: clear the render target
2814+ SG_LOADACTION_LOAD: load the previous content of the render target
2815+ SG_LOADACTION_DONTCARE: leave the render target in an undefined state
2816+
2817+ This is used in the sg_pass_action structure.
2818+
2819+ The default load action for all pass attachments is SG_LOADACTION_CLEAR,
2820+ with the values rgba = { 0.5f, 0.5f, 0.5f, 1.0f }, depth=1.0f and stencil=0.
2821+
2822+ If you want to override the default behaviour, it is important to not
2823+ only set the clear color, but the 'action' field as well (as long as this
2824+ is _SG_LOADACTION_DEFAULT, the value fields will be ignored).
2825+*/
2826+typedef enum sg_load_action {
2827+ _SG_LOADACTION_DEFAULT,
2828+ SG_LOADACTION_CLEAR,
2829+ SG_LOADACTION_LOAD,
2830+ SG_LOADACTION_DONTCARE,
2831+ _SG_LOADACTION_FORCE_U32 = 0x7FFFFFFF
2832+} sg_load_action;
2833+
2834+/*
2835+ sg_store_action
2836+
2837+ Defines the store action that should be performed at the end of a render pass:
2838+
2839+ SG_STOREACTION_STORE: store the rendered content to the color attachment image
2840+ SG_STOREACTION_DONTCARE: allows the GPU to discard the rendered content
2841+*/
2842+typedef enum sg_store_action {
2843+ _SG_STOREACTION_DEFAULT,
2844+ SG_STOREACTION_STORE,
2845+ SG_STOREACTION_DONTCARE,
2846+ _SG_STOREACTION_FORCE_U32 = 0x7FFFFFFF
2847+} sg_store_action;
2848+
2849+
2850+/*
2851+ sg_pass_action
2852+
2853+ The sg_pass_action struct defines the actions to be performed
2854+ at the start and end of a render pass.
2855+
2856+ - at the start of the pass: whether the render attachments should be cleared,
2857+ loaded with their previous content, or start in an undefined state
2858+ - for clear operations: the clear value (color, depth, or stencil values)
2859+ - at the end of the pass: whether the rendering result should be
2860+ stored back into the render attachment or discarded
2861+*/
2862+typedef struct sg_color_attachment_action {
2863+ sg_load_action load_action; // default: SG_LOADACTION_CLEAR
2864+ sg_store_action store_action; // default: SG_STOREACTION_STORE
2865+ sg_color clear_value; // default: { 0.5f, 0.5f, 0.5f, 1.0f }
2866+} sg_color_attachment_action;
2867+
2868+typedef struct sg_depth_attachment_action {
2869+ sg_load_action load_action; // default: SG_LOADACTION_CLEAR
2870+ sg_store_action store_action; // default: SG_STOREACTION_DONTCARE
2871+ float clear_value; // default: 1.0
2872+} sg_depth_attachment_action;
2873+
2874+typedef struct sg_stencil_attachment_action {
2875+ sg_load_action load_action; // default: SG_LOADACTION_CLEAR
2876+ sg_store_action store_action; // default: SG_STOREACTION_DONTCARE
2877+ uint8_t clear_value; // default: 0
2878+} sg_stencil_attachment_action;
2879+
2880+typedef struct sg_pass_action {
2881+ sg_color_attachment_action colors[SG_MAX_COLOR_ATTACHMENTS];
2882+ sg_depth_attachment_action depth;
2883+ sg_stencil_attachment_action stencil;
2884+} sg_pass_action;
2885+
2886+/*
2887+ sg_swapchain
2888+
2889+ Used in sg_begin_pass() to provide details about an external swapchain
2890+ (pixel formats, sample count and backend-API specific render surface objects).
2891+
2892+ The following information must be provided:
2893+
2894+ - the width and height of the swapchain surfaces in number of pixels,
2895+ - the pixel format of the render- and optional msaa-resolve-surface
2896+ - the pixel format of the optional depth- or depth-stencil-surface
2897+ - the MSAA sample count for the render and depth-stencil surface
2898+
2899+ If the pixel formats and MSAA sample counts are left zero-initialized,
2900+ their defaults are taken from the sg_environment struct provided in the
2901+ sg_setup() call.
2902+
2903+ The width and height *must* be > 0.
2904+
2905+ The boolean `sg_swapchain.invalid` is used to communicate an invalid
2906+ swapchain state to sokol-gfx (for instance the swapchain code outside of
2907+ sokol-gfx not being able to create swapchain surfaces). When the .invalid
2908+ boolean is set to true, all other sg_swapchain struct items must be zeroed
2909+ (checked in the validation layer), and all rendering in this swapchain-pass
2910+ will be silently skipped.
2911+
2912+ For valid swapchains, the following backend API specific objects must be passed in
2913+ as 'type erased' void pointers:
2914+
2915+ GL:
2916+ - on all GL backends, a GL framebuffer object must be provided. This
2917+ can be zero for the default framebuffer.
2918+
2919+ D3D11:
2920+ - an ID3D11RenderTargetView for the rendering surface, without
2921+ MSAA rendering this surface will also be displayed
2922+ - an optional ID3D11DepthStencilView for the depth- or depth/stencil
2923+ buffer surface
2924+ - when MSAA rendering is used, another ID3D11RenderTargetView
2925+ which serves as MSAA resolve target and will be displayed
2926+
2927+ WebGPU (same as D3D11, except different types)
2928+ - a WGPUTextureView for the rendering surface, without
2929+ MSAA rendering this surface will also be displayed
2930+ - an optional WGPUTextureView for the depth- or depth/stencil
2931+ buffer surface
2932+ - when MSAA rendering is used, another WGPUTextureView
2933+ which serves as MSAA resolve target and will be displayed
2934+
2935+ Metal (NOTE that the roles of provided surfaces is slightly different
2936+ than on D3D11 or WebGPU in case of MSAA vs non-MSAA rendering):
2937+
2938+ - A current CAMetalDrawable (NOT an MTLDrawable!) which will be presented.
2939+ This will either be rendered to directly (if no MSAA is used), or serve
2940+ as MSAA-resolve target.
2941+ - an optional MTLTexture for the depth- or depth-stencil buffer
2942+ - an optional multisampled MTLTexture which serves as intermediate
2943+ rendering surface which will then be resolved into the
2944+ CAMetalDrawable.
2945+
2946+ NOTE that for Metal you must use an ObjC __bridge cast to
2947+ properly tunnel the ObjC object id through a C void*, e.g.:
2948+
2949+ swapchain.metal.current_drawable = (__bridge const void*) [mtkView currentDrawable];
2950+
2951+ On all other backends you shouldn't need to mess with the reference count.
2952+
2953+ It's a good practice to write a helper function which returns an initialized
2954+ sg_swapchain struct, which can then be plugged directly into
2955+ sg_pass.swapchain. Look at the function sglue_swapchain() in the sokol_glue.h
2956+ as an example.
2957+*/
2958+typedef struct sg_metal_swapchain {
2959+ const void* current_drawable; // CAMetalDrawable (NOT MTLDrawable!!!)
2960+ const void* depth_stencil_texture; // MTLTexture
2961+ const void* msaa_color_texture; // MTLTexture
2962+} sg_metal_swapchain;
2963+
2964+typedef struct sg_d3d11_swapchain {
2965+ const void* render_view; // ID3D11RenderTargetView
2966+ const void* resolve_view; // ID3D11RenderTargetView
2967+ const void* depth_stencil_view; // ID3D11DepthStencilView
2968+} sg_d3d11_swapchain;
2969+
2970+typedef struct sg_wgpu_swapchain {
2971+ const void* render_view; // WGPUTextureView
2972+ const void* resolve_view; // WGPUTextureView
2973+ const void* depth_stencil_view; // WGPUTextureView
2974+} sg_wgpu_swapchain;
2975+
2976+typedef struct sg_vulkan_swapchain {
2977+ const void* render_image; // vkImage
2978+ const void* render_view; // vkImageView
2979+ const void* resolve_image; // vkImage
2980+ const void* resolve_view; // vkImageView
2981+ const void* depth_stencil_image; // vkImage
2982+ const void* depth_stencil_view; // vkImageView
2983+ const void* render_finished_semaphore; // vkSemaphore
2984+ const void* present_complete_semaphore; // vkSemaphore
2985+} sg_vulkan_swapchain;
2986+
2987+typedef struct sg_gl_swapchain {
2988+ uint32_t framebuffer; // GL framebuffer object
2989+} sg_gl_swapchain;
2990+
2991+typedef struct sg_swapchain {
2992+ bool invalid;
2993+ int width;
2994+ int height;
2995+ int sample_count;
2996+ sg_pixel_format color_format;
2997+ sg_pixel_format depth_format;
2998+ sg_metal_swapchain metal;
2999+ sg_d3d11_swapchain d3d11;
3000+ sg_wgpu_swapchain wgpu;
3001+ sg_vulkan_swapchain vulkan;
3002+ sg_gl_swapchain gl;
3003+} sg_swapchain;
3004+
3005+/*
3006+ sg_attachments
3007+
3008+ Used in sg_pass to provide render pass attachment views. Each
3009+ type of pass attachment has it corresponding view type:
3010+
3011+ sg_attachments.colors[]:
3012+ populate with color-attachment views, e.g.:
3013+
3014+ sg_make_view(&(sg_view_desc){
3015+ .color_attachment = { ... },
3016+ });
3017+
3018+ sg_attachments.resolves[]:
3019+ populate with resolve-attachment views, e.g.:
3020+
3021+ sg_make_view(&(sg_view_desc){
3022+ .resolve_attachment = { ... },
3023+ });
3024+
3025+ sg_attachments.depth_stencil:
3026+ populate with depth-stencil-attachment views, e.g.:
3027+
3028+ sg_make_view(&(sg_view_desc){
3029+ .depth_stencil_attachment = { ... },
3030+ });
3031+*/
3032+typedef struct sg_attachments {
3033+ sg_view colors[SG_MAX_COLOR_ATTACHMENTS];
3034+ sg_view resolves[SG_MAX_COLOR_ATTACHMENTS];
3035+ sg_view depth_stencil;
3036+} sg_attachments;
3037+
3038+/*
3039+ sg_pass
3040+
3041+ The sg_pass structure is passed as argument into the sg_begin_pass()
3042+ function.
3043+
3044+ For a swapchain render pass, provide an sg_pass_action and sg_swapchain
3045+ struct (for instance via the sglue_swapchain() helper function from
3046+ sokol_glue.h):
3047+
3048+ sg_begin_pass(&(sg_pass){
3049+ .action = { ... },
3050+ .swapchain = sglue_swapchain(),
3051+ });
3052+
3053+ For an offscreen render pass, provide an sg_pass_action struct with
3054+ attachment view objects:
3055+
3056+ sg_begin_pass(&(sg_pass){
3057+ .action = { ... },
3058+ .attachments = {
3059+ .colors = { ... },
3060+ .resolves = { ... },
3061+ .depth_stencil = ...,
3062+ },
3063+ });
3064+
3065+ You can also omit the .action object to get default pass action behaviour
3066+ (clear to color=grey, depth=1 and stencil=0).
3067+
3068+ For a compute pass, just set the sg_pass.compute boolean to true:
3069+
3070+ sg_begin_pass(&(sg_pass){ .compute = true });
3071+*/
3072+typedef struct sg_pass {
3073+ uint32_t _start_canary;
3074+ bool compute;
3075+ sg_pass_action action;
3076+ sg_attachments attachments;
3077+ sg_swapchain swapchain;
3078+ const char* label;
3079+ uint32_t _end_canary;
3080+} sg_pass;
3081+
3082+/*
3083+ sg_bindings
3084+
3085+ The sg_bindings structure defines the resource bindings for
3086+ the next draw call.
3087+
3088+ To update the resource bindings, call sg_apply_bindings() with
3089+ a pointer to a populated sg_bindings struct. Note that
3090+ sg_apply_bindings() must be called after sg_apply_pipeline()
3091+ and that bindings are not preserved across sg_apply_pipeline()
3092+ calls, even when the new pipeline uses the same 'bindings layout'.
3093+
3094+ A resource binding struct contains:
3095+
3096+ - 1..N vertex buffers
3097+ - 1..N vertex buffer offsets
3098+ - 0..1 index buffer
3099+ - 0..1 index buffer offset
3100+ - 0..N resource views (texture-, storage-image, storage-buffer-views)
3101+ - 0..N samplers
3102+
3103+ Where 'N' is defined in the following constants:
3104+
3105+ - SG_MAX_VERTEXBUFFER_BINDSLOTS
3106+ - SG_MAX_VIEW_BINDSLOTS
3107+ - SG_MAX_SAMPLER_BINDSLOTS
3108+
3109+ Note that inside compute passes vertex- and index-buffer-bindings are
3110+ disallowed.
3111+
3112+ When using sokol-shdc for shader authoring, the `layout(binding=N)`
3113+ for texture-, storage-image- and storage-buffer-bindings directly
3114+ maps to the views-array index, for instance the following vertex-
3115+ and fragment-shader interface for sokol-shdc:
3116+
3117+ @vs vs
3118+ layout(binding=0) uniform vs_params { ... };
3119+ layout(binding=0) readonly buffer ssbo { ... };
3120+ layout(binding=1) uniform texture2D vs_tex;
3121+ layout(binding=0) uniform sampler vs_smp;
3122+ ...
3123+ @end
3124+
3125+ @fs fs
3126+ layout(binding=1) uniform fs_params { ... };
3127+ layout(binding=2) uniform texture2D fs_tex;
3128+ layout(binding=1) uniform sampler fs_smp;
3129+ ...
3130+ @end
3131+
3132+ ...would map to the following sg_bindings struct:
3133+
3134+ const sg_bindings bnd = {
3135+ .vertex_buffers[0] = ...,
3136+ .views[0] = ssbo_view,
3137+ .views[1] = vs_tex_view,
3138+ .views[2] = fs_tex_view,
3139+ .samplers[0] = vs_smp,
3140+ .samplers[1] = fs_smp,
3141+ };
3142+
3143+ ...alternatively you can use code-generated slot indices:
3144+
3145+ const sg_bindings bnd = {
3146+ .vertex_buffers[0] = ...,
3147+ .views[VIEW_ssbo] = ssbo_view,
3148+ .views[VIEW_vs_tex] = vs_tex_view,
3149+ .views[VIEW_fs_tex] = fs_tex_view,
3150+ .samplers[SMP_vs_smp] = vs_smp,
3151+ .samplers[SMP_fs_smp] = fs_smp,
3152+ };
3153+
3154+ Resource bindslots for a specific shader/pipeline may have gaps, and an
3155+ sg_bindings struct may have populated bind slots which are not used by a
3156+ specific shader. This allows to use the same sg_bindings struct across
3157+ different shader variants.
3158+
3159+ When not using sokol-shdc, the bindslot indices in the sg_bindings
3160+ struct need to match the per-binding reflection info slot indices
3161+ in the sg_shader_desc struct (for details about that see the
3162+ sg_shader_desc struct documentation).
3163+
3164+ The optional buffer offsets can be used to put different unrelated
3165+ chunks of vertex- and/or index-data into the same buffer objects.
3166+*/
3167+typedef struct sg_bindings {
3168+ uint32_t _start_canary;
3169+ sg_buffer vertex_buffers[SG_MAX_VERTEXBUFFER_BINDSLOTS];
3170+ int vertex_buffer_offsets[SG_MAX_VERTEXBUFFER_BINDSLOTS];
3171+ sg_buffer index_buffer;
3172+ int index_buffer_offset;
3173+ sg_view views[SG_MAX_VIEW_BINDSLOTS];
3174+ sg_sampler samplers[SG_MAX_SAMPLER_BINDSLOTS];
3175+ uint32_t _end_canary;
3176+} sg_bindings;
3177+
3178+/*
3179+ sg_buffer_usage
3180+
3181+ Describes how a buffer object is going to be used:
3182+
3183+ .vertex_buffer (default: true)
3184+ the buffer will be bound as vertex buffer via sg_bindings.vertex_buffers[]
3185+ .index_buffer (default: false)
3186+ the buffer will be bound as index buffer via sg_bindings.index_buffer
3187+ .storage_buffer (default: false)
3188+ the buffer will be bound as storage buffer via storage-buffer-view
3189+ in sg_bindings.views[]
3190+ .immutable (default: true)
3191+ the buffer content will never be updated from the CPU side (but
3192+ may be written to by a compute shader)
3193+ .dynamic_update (default: false)
3194+ the buffer content will be infrequently updated from the CPU side
3195+ .stream_upate (default: false)
3196+ the buffer content will be updated each frame from the CPU side
3197+*/
3198+typedef struct sg_buffer_usage {
3199+ bool vertex_buffer;
3200+ bool index_buffer;
3201+ bool storage_buffer;
3202+ bool immutable;
3203+ bool dynamic_update;
3204+ bool stream_update;
3205+} sg_buffer_usage;
3206+
3207+/*
3208+ sg_buffer_desc
3209+
3210+ Creation parameters for sg_buffer objects, used in the sg_make_buffer() call.
3211+
3212+ The default configuration is:
3213+
3214+ .size: 0 (*must* be >0 for buffers without data)
3215+ .usage { .vertex_buffer = true, .immutable = true }
3216+ .data.ptr 0 (*must* be valid for immutable buffers without storage buffer usage)
3217+ .data.size 0 (*must* be > 0 for immutable buffers without storage buffer usage)
3218+ .label 0 (optional string label)
3219+
3220+ For immutable buffers which are initialized with initial data,
3221+ keep the .size item zero-initialized, and set the size together with the
3222+ pointer to the initial data in the .data item.
3223+
3224+ For immutable or mutable buffers without initial data, keep the .data item
3225+ zero-initialized, and set the buffer size in the .size item instead.
3226+
3227+ You can also set both size values, but currently both size values must
3228+ be identical (this may change in the future when the dynamic resource
3229+ management may become more flexible).
3230+
3231+ NOTE: Immutable buffers without storage-buffer-usage *must* be created
3232+ with initial content, this restriction doesn't apply to storage buffer usage,
3233+ because storage buffers may also get their initial content by running
3234+ a compute shader on them.
3235+
3236+ NOTE: Buffers without initial data will have undefined content, e.g.
3237+ do *not* expect the buffer to be zero-initialized!
3238+
3239+ ADVANCED TOPIC: Injecting native 3D-API buffers:
3240+
3241+ The following struct members allow to inject your own GL, Metal
3242+ or D3D11 buffers into sokol_gfx:
3243+
3244+ .gl_buffers[SG_NUM_INFLIGHT_FRAMES]
3245+ .mtl_buffers[SG_NUM_INFLIGHT_FRAMES]
3246+ .d3d11_buffer
3247+
3248+ You must still provide all other struct items except the .data item, and
3249+ these must match the creation parameters of the native buffers you provide.
3250+ For sg_buffer_desc.usage.immutable buffers, only provide a single native
3251+ 3D-API buffer, otherwise you need to provide SG_NUM_INFLIGHT_FRAMES buffers
3252+ (only for GL and Metal, not D3D11). Providing multiple buffers for GL and
3253+ Metal is necessary because sokol_gfx will rotate through them when calling
3254+ sg_update_buffer() to prevent lock-stalls.
3255+
3256+ Note that it is expected that immutable injected buffer have already been
3257+ initialized with content, and the .content member must be 0!
3258+
3259+ Also you need to call sg_reset_state_cache() after calling native 3D-API
3260+ functions, and before calling any sokol_gfx function.
3261+*/
3262+typedef struct sg_buffer_desc {
3263+ uint32_t _start_canary;
3264+ size_t size;
3265+ sg_buffer_usage usage;
3266+ sg_range data;
3267+ const char* label;
3268+ // optionally inject backend-specific resources
3269+ uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES];
3270+ const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES];
3271+ const void* d3d11_buffer;
3272+ const void* wgpu_buffer;
3273+ uint32_t _end_canary;
3274+} sg_buffer_desc;
3275+
3276+/*
3277+ sg_image_usage
3278+
3279+ Describes the intended usage of an image object:
3280+
3281+ .storage_image (default: false)
3282+ the image can be used as parent resource of a storage-image-view,
3283+ which allows compute shaders to write to the image in a compute
3284+ pass (for read-only access in compute shaders bind the image
3285+ via a texture view instead
3286+ .color_attachment (default: false)
3287+ the image can be used as parent resource of a color-attachment-view,
3288+ which is then passed into sg_begin_pass via sg_pass.attachments.colors[]
3289+ so that fragment shaders can render into the image
3290+ .resolve_attachment (default: false)
3291+ the image can be used as parent resource of a resolve-attachment-view,
3292+ which is then passed into sg_begin_pass via sg_pass.attachments.resolves[]
3293+ as target for an MSAA-resolve operation in sg_end_pass()
3294+ .depth_stencil_attachment (default: false)
3295+ the image can be used as parent resource of a depth-stencil-attachmnet-view
3296+ which is then passes into sg_begin_pass via sg_pass.attachments.depth_stencil
3297+ as depth-stencil-buffer
3298+ .immutable (default: true)
3299+ the image content cannot be updated from the CPU side
3300+ (but may be updated by the GPU in a render- or compute-pass)
3301+ .dynamic_update (default: false)
3302+ the image content is updated infrequently by the CPU via sg_update_image()
3303+ .stream_update (default: false)
3304+ the image content is updated each frame by the CPU via sg_update_image()
3305+
3306+ Note that creating a texture view from the image to be used for
3307+ texture-sampling in vertex-, fragment- or compute-shaders
3308+ is always implicitly allowed.
3309+*/
3310+typedef struct sg_image_usage {
3311+ bool storage_image;
3312+ bool color_attachment;
3313+ bool resolve_attachment;
3314+ bool depth_stencil_attachment;
3315+ bool immutable;
3316+ bool dynamic_update;
3317+ bool stream_update;
3318+} sg_image_usage;
3319+
3320+/*
3321+ sg_view_type
3322+
3323+ Allows to query the type of a view object via the function sg_query_view_type()
3324+*/
3325+typedef enum sg_view_type {
3326+ SG_VIEWTYPE_INVALID,
3327+ SG_VIEWTYPE_STORAGEBUFFER,
3328+ SG_VIEWTYPE_STORAGEIMAGE,
3329+ SG_VIEWTYPE_TEXTURE,
3330+ SG_VIEWTYPE_COLORATTACHMENT,
3331+ SG_VIEWTYPE_RESOLVEATTACHMENT,
3332+ SG_VIEWTYPE_DEPTHSTENCILATTACHMENT,
3333+ _SG_VIEWTYPE_FORCE_U32 = 0x7FFFFFFF
3334+} sg_view_type;
3335+
3336+/*
3337+ sg_image_data
3338+
3339+ Defines the content of an image through an array of sg_range structs, each
3340+ range pointing to the pixel data for one mip-level. For array-, cubemap- and
3341+ 3D-images each mip-level contains all slice-surfaces for that mip-level in a
3342+ single tightly packed memory block.
3343+
3344+ The size of a single surface in a mip-level for a regular 2D texture
3345+ can be computed via:
3346+
3347+ sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1);
3348+
3349+ For array- and 3d-images the size of a single miplevel is:
3350+
3351+ num_slices * sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1);
3352+
3353+ For cubemap-images the size of a single mip-level is:
3354+
3355+ 6 * sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1);
3356+
3357+ The order of cubemap-faces is in a mip-level data chunk is:
3358+
3359+ [0] => +X
3360+ [1] => -X
3361+ [2] => +Y
3362+ [3] => -Y
3363+ [4] => +Z
3364+ [5] => -Z
3365+*/
3366+typedef struct sg_image_data {
3367+ sg_range mip_levels[SG_MAX_MIPMAPS];
3368+} sg_image_data;
3369+
3370+/*
3371+ sg_image_desc
3372+
3373+ Creation parameters for sg_image objects, used in the sg_make_image() call.
3374+
3375+ The default configuration is:
3376+
3377+ .type SG_IMAGETYPE_2D
3378+ .usage .immutable = true
3379+ .width 0 (must be set to >0)
3380+ .height 0 (must be set to >0)
3381+ .num_slices 1 (3D textures: depth; array textures: number of layers)
3382+ .num_mipmaps 1
3383+ .pixel_format SG_PIXELFORMAT_RGBA8 for textures, or sg_desc.environment.defaults.color_format for render targets
3384+ .sample_count 1 for textures, or sg_desc.environment.defaults.sample_count for render targets
3385+ .data an sg_image_data struct to define the initial content
3386+ .label 0 (optional string label for trace hooks)
3387+
3388+ Q: Why is the default sample_count for render targets identical with the
3389+ "default sample count" from sg_desc.environment.defaults.sample_count?
3390+
3391+ A: So that it matches the default sample count in pipeline objects. Even
3392+ though it is a bit strange/confusing that offscreen render targets by default
3393+ get the same sample count as 'default swapchains', but it's better that
3394+ an offscreen render target created with default parameters matches
3395+ a pipeline object created with default parameters.
3396+
3397+ NOTE:
3398+
3399+ Regular images used as texture binding with usage.immutable must be fully
3400+ initialized by providing a valid .data member which points to initialization
3401+ data.
3402+
3403+ Images with usage.*_attachment or usage.storage_image must
3404+ *not* be created with initial content. Be aware that the initial
3405+ content of pass attachment and storage images is undefined
3406+ (not guaranteed to be zeroed).
3407+
3408+ ADVANCED TOPIC: Injecting native 3D-API textures:
3409+
3410+ The following struct members allow to inject your own GL, Metal or D3D11
3411+ textures into sokol_gfx:
3412+
3413+ .gl_textures[SG_NUM_INFLIGHT_FRAMES]
3414+ .mtl_textures[SG_NUM_INFLIGHT_FRAMES]
3415+ .d3d11_texture
3416+ .wgpu_texture
3417+
3418+ For GL, you can also specify the texture target or leave it empty to use
3419+ the default texture target for the image type (GL_TEXTURE_2D for
3420+ SG_IMAGETYPE_2D etc)
3421+
3422+ The same rules apply as for injecting native buffers (see sg_buffer_desc
3423+ documentation for more details).
3424+*/
3425+typedef struct sg_image_desc {
3426+ uint32_t _start_canary;
3427+ sg_image_type type;
3428+ sg_image_usage usage;
3429+ int width;
3430+ int height;
3431+ int num_slices;
3432+ int num_mipmaps;
3433+ sg_pixel_format pixel_format;
3434+ int sample_count;
3435+ sg_image_data data;
3436+ const char* label;
3437+ // optionally inject backend-specific resources
3438+ uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES];
3439+ uint32_t gl_texture_target;
3440+ const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES];
3441+ const void* d3d11_texture;
3442+ const void* wgpu_texture;
3443+ uint32_t _end_canary;
3444+} sg_image_desc;
3445+
3446+/*
3447+ sg_sampler_desc
3448+
3449+ Creation parameters for sg_sampler objects, used in the sg_make_sampler() call
3450+
3451+ .min_filter: SG_FILTER_NEAREST
3452+ .mag_filter: SG_FILTER_NEAREST
3453+ .mipmap_filter SG_FILTER_NEAREST
3454+ .wrap_u: SG_WRAP_REPEAT
3455+ .wrap_v: SG_WRAP_REPEAT
3456+ .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D)
3457+ .min_lod 0.0f
3458+ .max_lod FLT_MAX
3459+ .border_color SG_BORDERCOLOR_OPAQUE_BLACK
3460+ .compare SG_COMPAREFUNC_NEVER
3461+ .max_anisotropy 1 (must be 1..16)
3462+*/
3463+typedef struct sg_sampler_desc {
3464+ uint32_t _start_canary;
3465+ sg_filter min_filter;
3466+ sg_filter mag_filter;
3467+ sg_filter mipmap_filter;
3468+ sg_wrap wrap_u;
3469+ sg_wrap wrap_v;
3470+ sg_wrap wrap_w;
3471+ float min_lod;
3472+ float max_lod;
3473+ sg_border_color border_color;
3474+ sg_compare_func compare;
3475+ uint32_t max_anisotropy;
3476+ const char* label;
3477+ // optionally inject backend-specific resources
3478+ uint32_t gl_sampler;
3479+ const void* mtl_sampler;
3480+ const void* d3d11_sampler;
3481+ const void* wgpu_sampler;
3482+ uint32_t _end_canary;
3483+} sg_sampler_desc;
3484+
3485+/*
3486+ sg_shader_desc
3487+
3488+ Used as parameter of sg_make_shader() to create a shader object which
3489+ communicates shader source or bytecode and shader interface
3490+ reflection information to sokol-gfx.
3491+
3492+ If you use sokol-shdc you can ignore the following information since
3493+ the sg_shader_desc struct will be code-generated.
3494+
3495+ Otherwise you need to provide the following information to the
3496+ sg_make_shader() call:
3497+
3498+ - a vertex- and fragment-shader function:
3499+ - the shader source or bytecode
3500+ - an optional entry point name
3501+ - for D3D11: an optional compile target when source code is provided
3502+ (the defaults are "vs_4_0" and "ps_4_0")
3503+
3504+ - ...or alternatively, a compute function:
3505+ - the shader source or bytecode
3506+ - an optional entry point name
3507+ - for D3D11: an optional compile target when source code is provided
3508+ (the default is "cs_5_0")
3509+
3510+ - vertex attributes required by some backends (not for compute shaders):
3511+ - the vertex attribute base type (undefined, float, signed int, unsigned int),
3512+ this information is only used in the validation layer to check that the
3513+ pipeline object vertex formats are compatible with the input vertex attribute
3514+ type used in the vertex shader. NOTE that the default base type
3515+ 'undefined' skips the validation layer check.
3516+ - for the GL backend: optional vertex attribute names used for name lookup
3517+ - for the D3D11 backend: semantic names and indices
3518+
3519+ - only for compute shaders on the Metal backend:
3520+ - the workgroup size aka 'threads per thread-group'
3521+
3522+ In other 3D APIs this is declared in the shader code:
3523+ - GLSL: `layout(local_size_x=x, local_size_y=y, local_size_y=z) in;`
3524+ - HLSL: `[numthreads(x, y, z)]`
3525+ - WGSL: `@workgroup_size(x, y, z)`
3526+ ...but in Metal the workgroup size is declared on the CPU side
3527+
3528+ - reflection information for each uniform block binding used by the shader:
3529+ - the shader stage the uniform block appears in (SG_SHADERSTAGE_*)
3530+ - the size in bytes of the uniform block
3531+ - backend-specific bindslots:
3532+ - HLSL: the constant buffer register `register(b0..7)`
3533+ - MSL: the buffer attribute `[[buffer(0..7)]]`
3534+ - WGSL: the binding in `@group(0) @binding(0..15)`
3535+ - GLSL only: a description of the uniform block interior
3536+ - the memory layout standard (SG_UNIFORMLAYOUT_*)
3537+ - for each member in the uniform block:
3538+ - the member type (SG_UNIFORM_*)
3539+ - if the member is an array, the array count
3540+ - the member name
3541+
3542+ - reflection information for each texture-, storage-buffer and
3543+ storage-image bindings by the shader, each with an associated
3544+ view type:
3545+ - texture bindings => texture views
3546+ - storage-buffer bindings => storage-buffer views
3547+ - storage-image bindings => storage-image views
3548+
3549+ - texture bindings must provide the following information:
3550+ - the shader stage the texture binding appears in (SG_SHADERSTAGE_*)
3551+ - the image type (SG_IMAGETYPE_*)
3552+ - the image-sample type (SG_IMAGESAMPLETYPE_*)
3553+ - whether the texture is multisampled
3554+ - backend specific bindslots:
3555+ - HLSL: the texture register `register(t0..31)`
3556+ - MSL: the texture attribute `[[texture(0..31)]]`
3557+ - WGSL: the binding in `@group(1) @binding(0..127)`
3558+
3559+ - storage-buffer bindings must provide the following information:
3560+ - the shader stage the storage buffer appears in (SG_SHADERSTAGE_*)
3561+ - whether the storage buffer is readonly
3562+ - backend specific bindslots:
3563+ - HLSL:
3564+ - for storage buffer bindings: `register(t0..31)`
3565+ - for read/write storage buffer bindings: `register(u0..31)`
3566+ - MSL: the buffer attribute `[[buffer(8..23)]]`
3567+ - WGSL: the binding in `@group(1) @binding(0..127)`
3568+ - GL: the binding in `layout(binding=0..sg_limits.max_storage_buffer_bindings_per_stage)`
3569+
3570+ - storage-image bindings must provide the following information:
3571+ - the shader stage (*must* be SG_SHADERSTAGE_COMPUTE)
3572+ - whether the storage image is writeonly or readwrite (for readonly
3573+ access use a regular texture binding instead)
3574+ - the image type expected by the shader (SG_IMAGETYPE_*)
3575+ - the access pixel format expected by the shader (SG_PIXELFORMAT_*),
3576+ note that only a subset of pixel formats is allowed for storage image
3577+ bindings
3578+ - backend specific bindslots:
3579+ - HLSL: the UAV register `register(u0..31)`
3580+ - MSL: the texture attribute `[[texture(0..31)]]`
3581+ - WGSL: the binding in `@group(1) @binding(0..127)`
3582+ - GLSL: the binding in `layout(binding=0..sg_imits.max_storage_buffer_bindings_per_stage, [access_format])`
3583+
3584+ - reflection information for each sampler used by the shader:
3585+ - the shader stage the sampler appears in (SG_SHADERSTAGE_*)
3586+ - the sampler type (SG_SAMPLERTYPE_*)
3587+ - backend specific bindslots:
3588+ - HLSL: the sampler register `register(s0..11)`
3589+ - MSL: the sampler attribute `[[sampler(0..11)]]`
3590+ - WGSL: the binding in `@group(0) @binding(0..127)`
3591+
3592+ - reflection information for each texture-sampler pair used by
3593+ the shader:
3594+ - the shader stage (SG_SHADERSTAGE_*)
3595+ - the texture's array index in the sg_shader_desc.views[] array
3596+ - the sampler's array index in the sg_shader_desc.samplers[] array
3597+ - GLSL only: the name of the combined image-sampler object
3598+
3599+ The number and order of items in the sg_shader_desc.attrs[]
3600+ array corresponds to the items in sg_pipeline_desc.layout.attrs.
3601+
3602+ - sg_shader_desc.attrs[N] => sg_pipeline_desc.layout.attrs[N]
3603+
3604+ NOTE that vertex attribute indices currently cannot have gaps.
3605+
3606+ The items index in the sg_shader_desc.uniform_blocks[] array corresponds
3607+ to the ub_slot arg in sg_apply_uniforms():
3608+
3609+ - sg_shader_desc.uniform_blocks[N] => sg_apply_uniforms(N, ...)
3610+
3611+ The items in the sg_shader_desc.views[] array directly map to
3612+ the views in the sg_bindings.views[] array!
3613+
3614+ For all GL backends, shader source-code must be provided. For D3D11 and Metal,
3615+ either shader source-code or byte-code can be provided.
3616+
3617+ NOTE that the uniform-block, view and sampler arrays may have gaps. This
3618+ allows to use the same sg_bindings struct for different but related
3619+ shader variations.
3620+
3621+ For D3D11, if source code is provided, the d3dcompiler_47.dll will be loaded
3622+ on demand. If this fails, shader creation will fail. When compiling HLSL
3623+ source code, you can provide an optional target string via
3624+ sg_shader_stage_desc.d3d11_target, the default target is "vs_4_0" for the
3625+ vertex shader stage and "ps_4_0" for the pixel shader stage.
3626+ You may optionally provide the file path to enable the default #include handler
3627+ behavior when compiling source code.
3628+*/
3629+typedef enum sg_shader_stage {
3630+ SG_SHADERSTAGE_NONE,
3631+ SG_SHADERSTAGE_VERTEX,
3632+ SG_SHADERSTAGE_FRAGMENT,
3633+ SG_SHADERSTAGE_COMPUTE,
3634+ _SG_SHADERSTAGE_FORCE_U32 = 0x7FFFFFFF,
3635+} sg_shader_stage;
3636+
3637+typedef struct sg_shader_function {
3638+ const char* source;
3639+ sg_range bytecode;
3640+ const char* entry;
3641+ const char* d3d11_target; // default: "vs_4_0" or "ps_4_0"
3642+ const char* d3d11_filepath;
3643+} sg_shader_function;
3644+
3645+typedef enum sg_shader_attr_base_type {
3646+ SG_SHADERATTRBASETYPE_UNDEFINED,
3647+ SG_SHADERATTRBASETYPE_FLOAT,
3648+ SG_SHADERATTRBASETYPE_SINT,
3649+ SG_SHADERATTRBASETYPE_UINT,
3650+ _SG_SHADERATTRBASETYPE_FORCE_U32 = 0x7FFFFFFF,
3651+} sg_shader_attr_base_type;
3652+
3653+typedef struct sg_shader_vertex_attr {
3654+ sg_shader_attr_base_type base_type; // default: UNDEFINED (disables validation)
3655+ const char* glsl_name; // [optional] GLSL attribute name
3656+ const char* hlsl_sem_name; // HLSL semantic name
3657+ uint8_t hlsl_sem_index; // HLSL semantic index
3658+} sg_shader_vertex_attr;
3659+
3660+typedef struct sg_glsl_shader_uniform {
3661+ sg_uniform_type type;
3662+ uint16_t array_count; // 0 or 1 for scalars, >1 for arrays
3663+ const char* glsl_name; // glsl name binding is required on GL 4.1 and WebGL2
3664+} sg_glsl_shader_uniform;
3665+
3666+typedef struct sg_shader_uniform_block {
3667+ sg_shader_stage stage;
3668+ uint32_t size;
3669+ uint8_t hlsl_register_b_n; // HLSL register(bn)
3670+ uint8_t msl_buffer_n; // MSL [[buffer(n)]]
3671+ uint8_t wgsl_group0_binding_n; // WGSL @group(0) @binding(n)
3672+ uint8_t spirv_set0_binding_n; // Vulkan GLSL layout(set=0, binding=n)
3673+ sg_uniform_layout layout;
3674+ sg_glsl_shader_uniform glsl_uniforms[SG_MAX_UNIFORMBLOCK_MEMBERS];
3675+} sg_shader_uniform_block;
3676+
3677+typedef struct sg_shader_texture_view {
3678+ sg_shader_stage stage;
3679+ sg_image_type image_type;
3680+ sg_image_sample_type sample_type;
3681+ bool multisampled;
3682+ uint8_t hlsl_register_t_n; // HLSL register(tn) bind slot
3683+ uint8_t msl_texture_n; // MSL [[texture(n)]] bind slot
3684+ uint8_t wgsl_group1_binding_n; // WGSL @group(1) @binding(n) bind slot
3685+ uint8_t spirv_set1_binding_n; // Vulkan GLSL layout(set=1, binding=0)
3686+} sg_shader_texture_view;
3687+
3688+typedef struct sg_shader_storage_buffer_view {
3689+ sg_shader_stage stage;
3690+ bool readonly;
3691+ uint8_t hlsl_register_t_n; // HLSL register(tn) bind slot (for readonly access)
3692+ uint8_t hlsl_register_u_n; // HLSL register(un) bind slot (for read/write access)
3693+ uint8_t msl_buffer_n; // MSL [[buffer(n)]] bind slot
3694+ uint8_t wgsl_group1_binding_n; // WGSL @group(1) @binding(n) bind slot
3695+ uint8_t spirv_set1_binding_n; // Vulkan GLSL layout(set=1, binding=0)
3696+ uint8_t glsl_binding_n; // GLSL layout(binding=n)
3697+} sg_shader_storage_buffer_view;
3698+
3699+typedef struct sg_shader_storage_image_view {
3700+ sg_shader_stage stage;
3701+ sg_image_type image_type;
3702+ sg_pixel_format access_format; // shader-access pixel format
3703+ bool writeonly; // false means read/write access
3704+ uint8_t hlsl_register_u_n; // HLSL register(un) bind slot
3705+ uint8_t msl_texture_n; // MSL [[texture(n)]] bind slot
3706+ uint8_t wgsl_group1_binding_n; // WGSL @group(2) @binding(n) bind slot
3707+ uint8_t spirv_set1_binding_n; // Vulkan GLSL layout(set=1, binding=0)
3708+ uint8_t glsl_binding_n; // GLSL layout(binding=n)
3709+} sg_shader_storage_image_view;
3710+
3711+typedef struct sg_shader_view {
3712+ sg_shader_texture_view texture;
3713+ sg_shader_storage_buffer_view storage_buffer;
3714+ sg_shader_storage_image_view storage_image;
3715+} sg_shader_view;
3716+
3717+typedef struct sg_shader_sampler {
3718+ sg_shader_stage stage;
3719+ sg_sampler_type sampler_type;
3720+ uint8_t hlsl_register_s_n; // HLSL register(sn) bind slot
3721+ uint8_t msl_sampler_n; // MSL [[sampler(n)]] bind slot
3722+ uint8_t wgsl_group1_binding_n; // WGSL @group(1) @binding(n) bind slot
3723+ uint8_t spirv_set1_binding_n; // Vulkan GLSL layout(set=1, binding=0)
3724+} sg_shader_sampler;
3725+
3726+typedef struct sg_shader_texture_sampler_pair {
3727+ sg_shader_stage stage;
3728+ uint8_t view_slot; // must be SG_VIEWTYPE_TEXTURE
3729+ uint8_t sampler_slot;
3730+ const char* glsl_name; // glsl name binding required because of GL 4.1 and WebGL2
3731+} sg_shader_texture_sampler_pair;
3732+
3733+typedef struct sg_mtl_shader_threads_per_threadgroup {
3734+ int x, y, z;
3735+} sg_mtl_shader_threads_per_threadgroup;
3736+
3737+typedef struct sg_shader_desc {
3738+ uint32_t _start_canary;
3739+ sg_shader_function vertex_func;
3740+ sg_shader_function fragment_func;
3741+ sg_shader_function compute_func;
3742+ sg_shader_vertex_attr attrs[SG_MAX_VERTEX_ATTRIBUTES];
3743+ sg_shader_uniform_block uniform_blocks[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
3744+ sg_shader_view views[SG_MAX_VIEW_BINDSLOTS];
3745+ sg_shader_sampler samplers[SG_MAX_SAMPLER_BINDSLOTS];
3746+ sg_shader_texture_sampler_pair texture_sampler_pairs[SG_MAX_TEXTURE_SAMPLER_PAIRS];
3747+ sg_mtl_shader_threads_per_threadgroup mtl_threads_per_threadgroup;
3748+ const char* label;
3749+ uint32_t _end_canary;
3750+} sg_shader_desc;
3751+
3752+/*
3753+ sg_pipeline_desc
3754+
3755+ The sg_pipeline_desc struct defines all creation parameters for an
3756+ sg_pipeline object, used as argument to the sg_make_pipeline() function:
3757+
3758+ Pipeline objects come in two flavours:
3759+
3760+ - render pipelines for use in render passes
3761+ - compute pipelines for use in compute passes
3762+
3763+ A compute pipeline only requires a compute shader object but no
3764+ 'render state', while a render pipeline requires a vertex/fragment shader
3765+ object and additional render state declarations:
3766+
3767+ - the vertex layout for all input vertex buffers
3768+ - a shader object
3769+ - the 3D primitive type (points, lines, triangles, ...)
3770+ - the index type (none, 16- or 32-bit)
3771+ - all the fixed-function-pipeline state (depth-, stencil-, blend-state, etc...)
3772+
3773+ If the vertex data has no gaps between vertex components, you can omit
3774+ the .layout.buffers[].stride and layout.attrs[].offset items (leave them
3775+ default-initialized to 0), sokol-gfx will then compute the offsets and
3776+ strides from the vertex component formats (.layout.attrs[].format).
3777+ Please note that ALL vertex attribute offsets must be 0 in order for the
3778+ automatic offset computation to kick in.
3779+
3780+ Note that if you use vertex-pulling from storage buffers instead of
3781+ fixed-function vertex input you can simply omit the entire nested .layout
3782+ struct.
3783+
3784+ The default configuration is as follows:
3785+
3786+ .compute: false (must be set to true for a compute pipeline)
3787+ .shader: 0 (must be initialized with a valid sg_shader id!)
3788+ .layout:
3789+ .buffers[]: vertex buffer layouts
3790+ .stride: 0 (if no stride is given it will be computed)
3791+ .step_func SG_VERTEXSTEP_PER_VERTEX
3792+ .step_rate 1
3793+ .attrs[]: vertex attribute declarations
3794+ .buffer_index 0 the vertex buffer bind slot
3795+ .offset 0 (offsets can be omitted if the vertex layout has no gaps)
3796+ .format SG_VERTEXFORMAT_INVALID (must be initialized!)
3797+ .depth:
3798+ .pixel_format: sg_desc.context.depth_format
3799+ .compare: SG_COMPAREFUNC_ALWAYS
3800+ .write_enabled: false
3801+ .bias: 0.0f
3802+ .bias_slope_scale: 0.0f
3803+ .bias_clamp: 0.0f
3804+ .stencil:
3805+ .enabled: false
3806+ .front/back:
3807+ .compare: SG_COMPAREFUNC_ALWAYS
3808+ .fail_op: SG_STENCILOP_KEEP
3809+ .depth_fail_op: SG_STENCILOP_KEEP
3810+ .pass_op: SG_STENCILOP_KEEP
3811+ .read_mask: 0
3812+ .write_mask: 0
3813+ .ref: 0
3814+ .color_count 1
3815+ .colors[0..color_count]
3816+ .pixel_format sg_desc.context.color_format
3817+ .write_mask: SG_COLORMASK_RGBA
3818+ .blend:
3819+ .enabled: false
3820+ .src_factor_rgb: SG_BLENDFACTOR_ONE
3821+ .dst_factor_rgb: SG_BLENDFACTOR_ZERO
3822+ .op_rgb: SG_BLENDOP_ADD
3823+ .src_factor_alpha: SG_BLENDFACTOR_ONE
3824+ .dst_factor_alpha: SG_BLENDFACTOR_ZERO
3825+ .op_alpha: SG_BLENDOP_ADD
3826+ .primitive_type: SG_PRIMITIVETYPE_TRIANGLES
3827+ .index_type: SG_INDEXTYPE_NONE
3828+ .cull_mode: SG_CULLMODE_NONE
3829+ .face_winding: SG_FACEWINDING_CW
3830+ .sample_count: sg_desc.context.sample_count
3831+ .blend_color: (sg_color) { 0.0f, 0.0f, 0.0f, 0.0f }
3832+ .alpha_to_coverage_enabled: false
3833+ .label 0 (optional string label for trace hooks)
3834+*/
3835+typedef struct sg_vertex_buffer_layout_state {
3836+ int stride;
3837+ sg_vertex_step step_func;
3838+ int step_rate;
3839+} sg_vertex_buffer_layout_state;
3840+
3841+typedef struct sg_vertex_attr_state {
3842+ int buffer_index;
3843+ int offset;
3844+ sg_vertex_format format;
3845+} sg_vertex_attr_state;
3846+
3847+typedef struct sg_vertex_layout_state {
3848+ sg_vertex_buffer_layout_state buffers[SG_MAX_VERTEXBUFFER_BINDSLOTS];
3849+ sg_vertex_attr_state attrs[SG_MAX_VERTEX_ATTRIBUTES];
3850+} sg_vertex_layout_state;
3851+
3852+typedef struct sg_stencil_face_state {
3853+ sg_compare_func compare;
3854+ sg_stencil_op fail_op;
3855+ sg_stencil_op depth_fail_op;
3856+ sg_stencil_op pass_op;
3857+} sg_stencil_face_state;
3858+
3859+typedef struct sg_stencil_state {
3860+ bool enabled;
3861+ sg_stencil_face_state front;
3862+ sg_stencil_face_state back;
3863+ uint8_t read_mask;
3864+ uint8_t write_mask;
3865+ uint8_t ref;
3866+} sg_stencil_state;
3867+
3868+typedef struct sg_depth_state {
3869+ sg_pixel_format pixel_format;
3870+ sg_compare_func compare;
3871+ bool write_enabled;
3872+ float bias;
3873+ float bias_slope_scale;
3874+ float bias_clamp;
3875+} sg_depth_state;
3876+
3877+typedef struct sg_blend_state {
3878+ bool enabled;
3879+ sg_blend_factor src_factor_rgb;
3880+ sg_blend_factor dst_factor_rgb;
3881+ sg_blend_op op_rgb;
3882+ sg_blend_factor src_factor_alpha;
3883+ sg_blend_factor dst_factor_alpha;
3884+ sg_blend_op op_alpha;
3885+} sg_blend_state;
3886+
3887+typedef struct sg_color_target_state {
3888+ sg_pixel_format pixel_format;
3889+ sg_color_mask write_mask;
3890+ sg_blend_state blend;
3891+} sg_color_target_state;
3892+
3893+typedef struct sg_pipeline_desc {
3894+ uint32_t _start_canary;
3895+ bool compute;
3896+ sg_shader shader;
3897+ sg_vertex_layout_state layout;
3898+ sg_depth_state depth;
3899+ sg_stencil_state stencil;
3900+ int color_count;
3901+ sg_color_target_state colors[SG_MAX_COLOR_ATTACHMENTS];
3902+ sg_primitive_type primitive_type;
3903+ sg_index_type index_type;
3904+ sg_cull_mode cull_mode;
3905+ sg_face_winding face_winding;
3906+ int sample_count;
3907+ sg_color blend_color;
3908+ bool alpha_to_coverage_enabled;
3909+ const char* label;
3910+ uint32_t _end_canary;
3911+} sg_pipeline_desc;
3912+
3913+/*
3914+ sg_view_desc
3915+
3916+ Creation params for sg_view objects, passed into sg_make_view() calls.
3917+
3918+ View objects are passed into sg_apply_bindings() (for texture-, storage-buffer-
3919+ and storage-image views), and sg_begin_pass() (for color-, resolve-
3920+ and depth-stencil-attachment views).
3921+
3922+ The view type is determined by initializing one of the sub-structs of
3923+ sg_view_desc:
3924+
3925+ .texture a texture-view object will be created
3926+ .image the sg_image parent resource
3927+ .mip_levels optional mip-level range, keep zero-initialized for the
3928+ entire mipmap chain
3929+ .base the first mip level
3930+ .count number of mip levels, keeping this zero-initialized means
3931+ 'all remaining mip levels'
3932+ .slices optional slice range, keep zero-initialized to include
3933+ all slices
3934+ .base the first slice
3935+ .count number of slices, keeping this zero-initializied means 'all remaining slices'
3936+
3937+ .storage_buffer a storage-buffer-view object will be created
3938+ .buffer the sg_buffer parent resource, must have been created
3939+ with `sg_buffer_desc.usage.storage_buffer = true`
3940+ .offset optional 256-byte aligned byte-offset into the buffer
3941+
3942+ .storage_image a storage-image-view object will be created
3943+ .image the sg_image parent resource, must have been created
3944+ with `sg_image_desc.usage.storage_image = true`
3945+ .mip_level selects the mip-level for the compute shader to write
3946+ .slice selects the slice for the compute shader to write
3947+
3948+ .color_attachment a color-attachment-view object will be created
3949+ .image the sg_image parent resource, must have been created
3950+ with `sg_image_desc.usage.color_attachment = true`
3951+ .mip_level selects the mip-level to render into
3952+ .slice selects the slice to render into
3953+
3954+ .resolve_attachment a resolve-attachment-view object will be created
3955+ .image the sg_image parent resource, must have been created
3956+ with `sg_image_desc.usage.resolve_attachment = true`
3957+ .mip_level selects the mip-level to msaa-resolve into
3958+ .slice selects the slice to msaa-resolve into
3959+
3960+ .depth_stencil_attachment a depth-stencil-attachment-view object will be created
3961+ .image the sg_image parent resource, must have been created
3962+ with `sg_image_desc.usage.depth_stencil_attachment = true`
3963+ .mip_level selects the mip-level to render into
3964+ .slice selects the slice to render into
3965+*/
3966+typedef struct sg_buffer_view_desc {
3967+ sg_buffer buffer;
3968+ int offset;
3969+} sg_buffer_view_desc;
3970+
3971+typedef struct sg_image_view_desc {
3972+ sg_image image;
3973+ int mip_level;
3974+ int slice; // cube texture: face; array texture: layer; 3D texture: depth-slice
3975+} sg_image_view_desc;
3976+
3977+typedef struct sg_texture_view_range {
3978+ int base;
3979+ int count;
3980+} sg_texture_view_range;
3981+
3982+typedef struct sg_texture_view_desc {
3983+ sg_image image;
3984+ sg_texture_view_range mip_levels;
3985+ sg_texture_view_range slices; // cube texture: face; array texture: layer; 3D texture: depth-slice
3986+} sg_texture_view_desc;
3987+
3988+typedef struct sg_view_desc {
3989+ uint32_t _start_canary;
3990+ sg_texture_view_desc texture;
3991+ sg_buffer_view_desc storage_buffer;
3992+ sg_image_view_desc storage_image;
3993+ sg_image_view_desc color_attachment;
3994+ sg_image_view_desc resolve_attachment;
3995+ sg_image_view_desc depth_stencil_attachment;
3996+ const char* label;
3997+ uint32_t _end_canary;
3998+} sg_view_desc;
3999+
4000+/*
4001+ sg_trace_hooks
4002+
4003+ Installable callback functions to keep track of the sokol-gfx calls,
4004+ this is useful for debugging, or keeping track of resource creation
4005+ and destruction.
4006+
4007+ Trace hooks are installed with sg_install_trace_hooks(), this returns
4008+ another sg_trace_hooks struct with the previous set of
4009+ trace hook function pointers. These should be invoked by the
4010+ new trace hooks to form a proper call chain.
4011+*/
4012+typedef struct sg_trace_hooks {
4013+ void* user_data;
4014+ void (*reset_state_cache)(void* user_data);
4015+ void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data);
4016+ void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data);
4017+ void (*make_sampler)(const sg_sampler_desc* desc, sg_sampler result, void* user_data);
4018+ void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data);
4019+ void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data);
4020+ void (*make_view)(const sg_view_desc* desc, sg_view result, void* user_data);
4021+ void (*destroy_buffer)(sg_buffer buf, void* user_data);
4022+ void (*destroy_image)(sg_image img, void* user_data);
4023+ void (*destroy_sampler)(sg_sampler smp, void* user_data);
4024+ void (*destroy_shader)(sg_shader shd, void* user_data);
4025+ void (*destroy_pipeline)(sg_pipeline pip, void* user_data);
4026+ void (*destroy_view)(sg_view view, void* user_data);
4027+ void (*update_buffer)(sg_buffer buf, const sg_range* data, void* user_data);
4028+ void (*update_image)(sg_image img, const sg_image_data* data, void* user_data);
4029+ void (*append_buffer)(sg_buffer buf, const sg_range* data, int result, void* user_data);
4030+ void (*begin_pass)(const sg_pass* pass, void* user_data);
4031+ void (*apply_viewport)(int x, int y, int width, int height, bool origin_top_left, void* user_data);
4032+ void (*apply_scissor_rect)(int x, int y, int width, int height, bool origin_top_left, void* user_data);
4033+ void (*apply_pipeline)(sg_pipeline pip, void* user_data);
4034+ void (*apply_bindings)(const sg_bindings* bindings, void* user_data);
4035+ void (*apply_uniforms)(int ub_index, const sg_range* data, void* user_data);
4036+ void (*draw)(int base_element, int num_elements, int num_instances, void* user_data);
4037+ void (*draw_ex)(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance, void* user_data);
4038+ void (*dispatch)(int num_groups_x, int num_groups_y, int num_groups_z, void* user_data);
4039+ void (*end_pass)(void* user_data);
4040+ void (*commit)(void* user_data);
4041+ void (*alloc_buffer)(sg_buffer result, void* user_data);
4042+ void (*alloc_image)(sg_image result, void* user_data);
4043+ void (*alloc_sampler)(sg_sampler result, void* user_data);
4044+ void (*alloc_shader)(sg_shader result, void* user_data);
4045+ void (*alloc_pipeline)(sg_pipeline result, void* user_data);
4046+ void (*alloc_view)(sg_view result, void* user_data);
4047+ void (*dealloc_buffer)(sg_buffer buf_id, void* user_data);
4048+ void (*dealloc_image)(sg_image img_id, void* user_data);
4049+ void (*dealloc_sampler)(sg_sampler smp_id, void* user_data);
4050+ void (*dealloc_shader)(sg_shader shd_id, void* user_data);
4051+ void (*dealloc_pipeline)(sg_pipeline pip_id, void* user_data);
4052+ void (*dealloc_view)(sg_view view_id, void* user_data);
4053+ void (*init_buffer)(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data);
4054+ void (*init_image)(sg_image img_id, const sg_image_desc* desc, void* user_data);
4055+ void (*init_sampler)(sg_sampler smp_id, const sg_sampler_desc* desc, void* user_data);
4056+ void (*init_shader)(sg_shader shd_id, const sg_shader_desc* desc, void* user_data);
4057+ void (*init_pipeline)(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data);
4058+ void (*init_view)(sg_view view_id, const sg_view_desc* desc, void* user_data);
4059+ void (*uninit_buffer)(sg_buffer buf_id, void* user_data);
4060+ void (*uninit_image)(sg_image img_id, void* user_data);
4061+ void (*uninit_sampler)(sg_sampler smp_id, void* user_data);
4062+ void (*uninit_shader)(sg_shader shd_id, void* user_data);
4063+ void (*uninit_pipeline)(sg_pipeline pip_id, void* user_data);
4064+ void (*uninit_view)(sg_view view_id, void* user_data);
4065+ void (*fail_buffer)(sg_buffer buf_id, void* user_data);
4066+ void (*fail_image)(sg_image img_id, void* user_data);
4067+ void (*fail_sampler)(sg_sampler smp_id, void* user_data);
4068+ void (*fail_shader)(sg_shader shd_id, void* user_data);
4069+ void (*fail_pipeline)(sg_pipeline pip_id, void* user_data);
4070+ void (*fail_view)(sg_view view_id, void* user_data);
4071+ void (*push_debug_group)(const char* name, void* user_data);
4072+ void (*pop_debug_group)(void* user_data);
4073+} sg_trace_hooks;
4074+
4075+/*
4076+ sg_buffer_info
4077+ sg_image_info
4078+ sg_sampler_info
4079+ sg_shader_info
4080+ sg_pipeline_info
4081+ sg_view_info
4082+
4083+ These structs contain various internal resource attributes which
4084+ might be useful for debug-inspection. Please don't rely on the
4085+ actual content of those structs too much, as they are quite closely
4086+ tied to sokol_gfx.h internals and may change more frequently than
4087+ the other public API elements.
4088+
4089+ The *_info structs are used as the return values of the following functions:
4090+
4091+ sg_query_buffer_info()
4092+ sg_query_image_info()
4093+ sg_query_sampler_info()
4094+ sg_query_shader_info()
4095+ sg_query_pipeline_info()
4096+ sg_query_view_info()
4097+*/
4098+typedef struct sg_slot_info {
4099+ sg_resource_state state; // the current state of this resource slot
4100+ uint32_t res_id; // type-neutral resource if (e.g. sg_buffer.id)
4101+ uint32_t uninit_count;
4102+} sg_slot_info;
4103+
4104+typedef struct sg_buffer_info {
4105+ sg_slot_info slot; // resource pool slot info
4106+ uint32_t update_frame_index; // frame index of last sg_update_buffer()
4107+ uint32_t append_frame_index; // frame index of last sg_append_buffer()
4108+ int append_pos; // current position in buffer for sg_append_buffer()
4109+ bool append_overflow; // is buffer in overflow state (due to sg_append_buffer)
4110+ int num_slots; // number of renaming-slots for dynamically updated buffers
4111+ int active_slot; // currently active write-slot for dynamically updated buffers
4112+} sg_buffer_info;
4113+
4114+typedef struct sg_image_info {
4115+ sg_slot_info slot; // resource pool slot info
4116+ uint32_t upd_frame_index; // frame index of last sg_update_image()
4117+ int num_slots; // number of renaming-slots for dynamically updated images
4118+ int active_slot; // currently active write-slot for dynamically updated images
4119+} sg_image_info;
4120+
4121+typedef struct sg_sampler_info {
4122+ sg_slot_info slot; // resource pool slot info
4123+} sg_sampler_info;
4124+
4125+typedef struct sg_shader_info {
4126+ sg_slot_info slot; // resource pool slot info
4127+} sg_shader_info;
4128+
4129+typedef struct sg_pipeline_info {
4130+ sg_slot_info slot; // resource pool slot info
4131+} sg_pipeline_info;
4132+
4133+typedef struct sg_view_info {
4134+ sg_slot_info slot; // resource pool slot info
4135+} sg_view_info;
4136+
4137+/*
4138+ sg_stats
4139+
4140+ Allows to track generic and backend-specific rendering stats,
4141+ obtained via sg_query_stats().
4142+*/
4143+typedef struct sg_frame_stats_gl {
4144+ uint32_t num_bind_buffer;
4145+ uint32_t num_active_texture;
4146+ uint32_t num_bind_texture;
4147+ uint32_t num_bind_sampler;
4148+ uint32_t num_bind_image_texture;
4149+ uint32_t num_use_program;
4150+ uint32_t num_render_state;
4151+ uint32_t num_vertex_attrib_pointer;
4152+ uint32_t num_vertex_attrib_divisor;
4153+ uint32_t num_enable_vertex_attrib_array;
4154+ uint32_t num_disable_vertex_attrib_array;
4155+ uint32_t num_uniform;
4156+ uint32_t num_memory_barriers;
4157+} sg_frame_stats_gl;
4158+
4159+typedef struct sg_frame_stats_d3d11_pass {
4160+ uint32_t num_om_set_render_targets;
4161+ uint32_t num_clear_render_target_view;
4162+ uint32_t num_clear_depth_stencil_view;
4163+ uint32_t num_resolve_subresource;
4164+} sg_frame_stats_d3d11_pass;
4165+
4166+typedef struct sg_frame_stats_d3d11_pipeline {
4167+ uint32_t num_rs_set_state;
4168+ uint32_t num_om_set_depth_stencil_state;
4169+ uint32_t num_om_set_blend_state;
4170+ uint32_t num_ia_set_primitive_topology;
4171+ uint32_t num_ia_set_input_layout;
4172+ uint32_t num_vs_set_shader;
4173+ uint32_t num_vs_set_constant_buffers;
4174+ uint32_t num_ps_set_shader;
4175+ uint32_t num_ps_set_constant_buffers;
4176+ uint32_t num_cs_set_shader;
4177+ uint32_t num_cs_set_constant_buffers;
4178+} sg_frame_stats_d3d11_pipeline;
4179+
4180+typedef struct sg_frame_stats_d3d11_bindings {
4181+ uint32_t num_ia_set_vertex_buffers;
4182+ uint32_t num_ia_set_index_buffer;
4183+ uint32_t num_vs_set_shader_resources;
4184+ uint32_t num_vs_set_samplers;
4185+ uint32_t num_ps_set_shader_resources;
4186+ uint32_t num_ps_set_samplers;
4187+ uint32_t num_cs_set_shader_resources;
4188+ uint32_t num_cs_set_samplers;
4189+ uint32_t num_cs_set_unordered_access_views;
4190+} sg_frame_stats_d3d11_bindings;
4191+
4192+typedef struct sg_frame_stats_d3d11_uniforms {
4193+ uint32_t num_update_subresource;
4194+} sg_frame_stats_d3d11_uniforms;
4195+
4196+typedef struct sg_frame_stats_d3d11_draw {
4197+ uint32_t num_draw_indexed_instanced;
4198+ uint32_t num_draw_indexed;
4199+ uint32_t num_draw_instanced;
4200+ uint32_t num_draw;
4201+} sg_frame_stats_d3d11_draw;
4202+
4203+typedef struct sg_frame_stats_d3d11 {
4204+ sg_frame_stats_d3d11_pass pass;
4205+ sg_frame_stats_d3d11_pipeline pipeline;
4206+ sg_frame_stats_d3d11_bindings bindings;
4207+ sg_frame_stats_d3d11_uniforms uniforms;
4208+ sg_frame_stats_d3d11_draw draw;
4209+ uint32_t num_map;
4210+ uint32_t num_unmap;
4211+} sg_frame_stats_d3d11;
4212+
4213+typedef struct sg_frame_stats_metal_idpool {
4214+ uint32_t num_added;
4215+ uint32_t num_released;
4216+ uint32_t num_garbage_collected;
4217+} sg_frame_stats_metal_idpool;
4218+
4219+typedef struct sg_frame_stats_metal_pipeline {
4220+ uint32_t num_set_blend_color;
4221+ uint32_t num_set_cull_mode;
4222+ uint32_t num_set_front_facing_winding;
4223+ uint32_t num_set_stencil_reference_value;
4224+ uint32_t num_set_depth_bias;
4225+ uint32_t num_set_render_pipeline_state;
4226+ uint32_t num_set_depth_stencil_state;
4227+} sg_frame_stats_metal_pipeline;
4228+
4229+typedef struct sg_frame_stats_metal_bindings {
4230+ uint32_t num_set_vertex_buffer;
4231+ uint32_t num_set_vertex_buffer_offset;
4232+ uint32_t num_skip_redundant_vertex_buffer;
4233+ uint32_t num_set_vertex_texture;
4234+ uint32_t num_skip_redundant_vertex_texture;
4235+ uint32_t num_set_vertex_sampler_state;
4236+ uint32_t num_skip_redundant_vertex_sampler_state;
4237+ uint32_t num_set_fragment_buffer;
4238+ uint32_t num_set_fragment_buffer_offset;
4239+ uint32_t num_skip_redundant_fragment_buffer;
4240+ uint32_t num_set_fragment_texture;
4241+ uint32_t num_skip_redundant_fragment_texture;
4242+ uint32_t num_set_fragment_sampler_state;
4243+ uint32_t num_skip_redundant_fragment_sampler_state;
4244+ uint32_t num_set_compute_buffer;
4245+ uint32_t num_set_compute_buffer_offset;
4246+ uint32_t num_skip_redundant_compute_buffer;
4247+ uint32_t num_set_compute_texture;
4248+ uint32_t num_skip_redundant_compute_texture;
4249+ uint32_t num_set_compute_sampler_state;
4250+ uint32_t num_skip_redundant_compute_sampler_state;
4251+} sg_frame_stats_metal_bindings;
4252+
4253+typedef struct sg_frame_stats_metal_uniforms {
4254+ uint32_t num_set_vertex_buffer_offset;
4255+ uint32_t num_set_fragment_buffer_offset;
4256+ uint32_t num_set_compute_buffer_offset;
4257+} sg_frame_stats_metal_uniforms;
4258+
4259+typedef struct sg_frame_stats_metal {
4260+ sg_frame_stats_metal_idpool idpool;
4261+ sg_frame_stats_metal_pipeline pipeline;
4262+ sg_frame_stats_metal_bindings bindings;
4263+ sg_frame_stats_metal_uniforms uniforms;
4264+} sg_frame_stats_metal;
4265+
4266+typedef struct sg_frame_stats_wgpu_uniforms {
4267+ uint32_t num_set_bindgroup;
4268+ uint32_t size_write_buffer;
4269+} sg_frame_stats_wgpu_uniforms;
4270+
4271+typedef struct sg_frame_stats_wgpu_bindings {
4272+ uint32_t num_set_vertex_buffer;
4273+ uint32_t num_skip_redundant_vertex_buffer;
4274+ uint32_t num_set_index_buffer;
4275+ uint32_t num_skip_redundant_index_buffer;
4276+ uint32_t num_create_bindgroup;
4277+ uint32_t num_discard_bindgroup;
4278+ uint32_t num_set_bindgroup;
4279+ uint32_t num_skip_redundant_bindgroup;
4280+ uint32_t num_bindgroup_cache_hits;
4281+ uint32_t num_bindgroup_cache_misses;
4282+ uint32_t num_bindgroup_cache_collisions;
4283+ uint32_t num_bindgroup_cache_invalidates;
4284+ uint32_t num_bindgroup_cache_hash_vs_key_mismatch;
4285+} sg_frame_stats_wgpu_bindings;
4286+
4287+typedef struct sg_frame_stats_wgpu {
4288+ sg_frame_stats_wgpu_uniforms uniforms;
4289+ sg_frame_stats_wgpu_bindings bindings;
4290+} sg_frame_stats_wgpu;
4291+
4292+typedef struct sg_frame_stats_vk {
4293+ uint32_t num_cmd_pipeline_barrier;
4294+ uint32_t num_allocate_memory;
4295+ uint32_t num_free_memory;
4296+ uint32_t size_allocate_memory;
4297+ uint32_t num_delete_queue_added;
4298+ uint32_t num_delete_queue_collected;
4299+ uint32_t num_cmd_copy_buffer;
4300+ uint32_t num_cmd_copy_buffer_to_image;
4301+ uint32_t num_cmd_set_descriptor_buffer_offsets;
4302+ uint32_t size_descriptor_buffer_writes;
4303+} sg_frame_stats_vk;
4304+
4305+typedef struct sg_frame_resource_stats {
4306+ uint32_t allocated; // number of allocated objects in current frame
4307+ uint32_t deallocated; // number of deallocated object in current frame
4308+ uint32_t inited; // number of initialized objects in current frame
4309+ uint32_t uninited; // number of deinitialized objects in current frame
4310+} sg_frame_resource_stats;
4311+
4312+typedef struct sg_total_resource_stats {
4313+ uint32_t alive; // number of live objects in pool
4314+ uint32_t free; // number of free objects in pool
4315+ uint32_t allocated; // total number of object allocations
4316+ uint32_t deallocated; // total number of object deallocations
4317+ uint32_t inited; // total number of object initializations
4318+ uint32_t uninited; // total number of object deinitializations
4319+} sg_total_resource_stats;
4320+
4321+typedef struct sg_total_stats {
4322+ sg_total_resource_stats buffers;
4323+ sg_total_resource_stats images;
4324+ sg_total_resource_stats samplers;
4325+ sg_total_resource_stats views;
4326+ sg_total_resource_stats shaders;
4327+ sg_total_resource_stats pipelines;
4328+} sg_total_stats;
4329+
4330+typedef struct sg_frame_stats {
4331+ uint32_t frame_index; // current frame counter, starts at 0
4332+
4333+ uint32_t num_passes;
4334+ uint32_t num_apply_viewport;
4335+ uint32_t num_apply_scissor_rect;
4336+ uint32_t num_apply_pipeline;
4337+ uint32_t num_apply_bindings;
4338+ uint32_t num_apply_uniforms;
4339+ uint32_t num_draw;
4340+ uint32_t num_draw_ex;
4341+ uint32_t num_dispatch;
4342+ uint32_t num_update_buffer;
4343+ uint32_t num_append_buffer;
4344+ uint32_t num_update_image;
4345+
4346+ uint32_t size_apply_uniforms;
4347+ uint32_t size_update_buffer;
4348+ uint32_t size_append_buffer;
4349+ uint32_t size_update_image;
4350+
4351+ sg_frame_resource_stats buffers;
4352+ sg_frame_resource_stats images;
4353+ sg_frame_resource_stats samplers;
4354+ sg_frame_resource_stats views;
4355+ sg_frame_resource_stats shaders;
4356+ sg_frame_resource_stats pipelines;
4357+
4358+ sg_frame_stats_gl gl;
4359+ sg_frame_stats_d3d11 d3d11;
4360+ sg_frame_stats_metal metal;
4361+ sg_frame_stats_wgpu wgpu;
4362+ sg_frame_stats_vk vk;
4363+} sg_frame_stats;
4364+
4365+typedef struct sg_stats {
4366+ sg_frame_stats prev_frame;
4367+ sg_frame_stats cur_frame;
4368+ sg_total_stats total;
4369+} sg_stats;
4370+
4371+/*
4372+ sg_log_item
4373+
4374+ An enum with a unique item for each log message, warning, error
4375+ and validation layer message. Note that these messages are only
4376+ visible when a logger function is installed in the sg_setup() call.
4377+*/
4378+#define _SG_LOG_ITEMS \
4379+ _SG_LOGITEM_XMACRO(OK, "Ok") \
4380+ _SG_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \
4381+ _SG_LOGITEM_XMACRO(GL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (gl)") \
4382+ _SG_LOGITEM_XMACRO(GL_3D_TEXTURES_NOT_SUPPORTED, "3d textures not supported (gl)") \
4383+ _SG_LOGITEM_XMACRO(GL_ARRAY_TEXTURES_NOT_SUPPORTED, "array textures not supported (gl)") \
4384+ _SG_LOGITEM_XMACRO(GL_STORAGEBUFFER_GLSL_BINDING_OUT_OF_RANGE, "GLSL storage buffer bindslot is out of range (sg_limits.max_storage_buffer_bindings_per_stage) (gl)") \
4385+ _SG_LOGITEM_XMACRO(GL_STORAGEIMAGE_GLSL_BINDING_OUT_OF_RANGE, "GLSL storage image bindslot is out of range (sg.limits.max_storage_image_bindings_per_stage) (gl)") \
4386+ _SG_LOGITEM_XMACRO(GL_SHADER_COMPILATION_FAILED, "shader compilation failed (gl)") \
4387+ _SG_LOGITEM_XMACRO(GL_SHADER_LINKING_FAILED, "shader linking failed (gl)") \
4388+ _SG_LOGITEM_XMACRO(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, "vertex attribute not found in shader; NOTE: may be caused by GL driver's GLSL compiler removing unused globals") \
4389+ _SG_LOGITEM_XMACRO(GL_UNIFORMBLOCK_NAME_NOT_FOUND_IN_SHADER, "uniform block name not found in shader; NOTE: may be caused by GL driver's GLSL compiler removing unused globals") \
4390+ _SG_LOGITEM_XMACRO(GL_IMAGE_SAMPLER_NAME_NOT_FOUND_IN_SHADER, "image-sampler name not found in shader; NOTE: may be caused by GL driver's GLSL compiler removing unused globals") \
4391+ _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNDEFINED, "framebuffer completeness check failed with GL_FRAMEBUFFER_UNDEFINED (gl)") \
4392+ _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT (gl)") \
4393+ _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (gl)") \
4394+ _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNSUPPORTED, "framebuffer completeness check failed with GL_FRAMEBUFFER_UNSUPPORTED (gl)") \
4395+ _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE (gl)") \
4396+ _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNKNOWN, "framebuffer completeness check failed (unknown reason) (gl)") \
4397+ _SG_LOGITEM_XMACRO(D3D11_FEATURE_LEVEL_0_DETECTED, "D3D11 Feature Level 0 device detected, this restricts the number of UAV slots to 8! (d3d11)") \
4398+ _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_FAILED, "CreateBuffer() failed (d3d11)") \
4399+ _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_SRV_FAILED, "CreateShaderResourceView() failed for storage buffer (d3d11)") \
4400+ _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_UAV_FAILED, "CreateUnorderedAccessView() failed for storage buffer (d3d11)") \
4401+ _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for depth-stencil texture (d3d11)") \
4402+ _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_FAILED, "CreateTexture2D() failed for depth-stencil texture (d3d11)") \
4403+ _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 2d-, cube- or array-texture (d3d11)") \
4404+ _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_FAILED, "CreateTexture2D() failed for 2d-, cube- or array-texture (d3d11)") \
4405+ _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_SRV_FAILED, "CreateShaderResourceView() failed for 2d-, cube- or array-texture (d3d11)") \
4406+ _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 3D texture (d3d11)") \
4407+ _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_FAILED, "CreateTexture3D() failed (d3d11)") \
4408+ _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_SRV_FAILED, "CreateShaderResourceView() failed for 3d texture (d3d11)") \
4409+ _SG_LOGITEM_XMACRO(D3D11_CREATE_MSAA_TEXTURE_FAILED, "CreateTexture2D() failed for MSAA render target texture (d3d11)") \
4410+ _SG_LOGITEM_XMACRO(D3D11_CREATE_SAMPLER_STATE_FAILED, "CreateSamplerState() failed (d3d11)") \
4411+ _SG_LOGITEM_XMACRO(D3D11_UNIFORMBLOCK_HLSL_REGISTER_B_OUT_OF_RANGE, "sg_shader_desc.uniform_blocks[].hlsl_register_b_n is out of range (must be 0..7)") \
4412+ _SG_LOGITEM_XMACRO(D3D11_STORAGEBUFFER_HLSL_REGISTER_T_OUT_OF_RANGE, "sg_shader_desc.views[].storage_buffer.hlsl_register_t_n is out of range (must be 0..31)") \
4413+ _SG_LOGITEM_XMACRO(D3D11_STORAGEBUFFER_HLSL_REGISTER_U_OUT_OF_RANGE, "sg_shader_desc.views[].storage_buffer.hlsl_register_u_n is out of range (must be 0..31)") \
4414+ _SG_LOGITEM_XMACRO(D3D11_IMAGE_HLSL_REGISTER_T_OUT_OF_RANGE, "sg_shader_desc.views[].texture.hlsl_register_t_n is out of range (must be 0..31)") \
4415+ _SG_LOGITEM_XMACRO(D3D11_STORAGEIMAGE_HLSL_REGISTER_U_OUT_OF_RANGE, "sg_shader_desc.views[].storage_image.hlsl_register_u_n is out of range (must be 0..31)") \
4416+ _SG_LOGITEM_XMACRO(D3D11_SAMPLER_HLSL_REGISTER_S_OUT_OF_RANGE, "sampler 'hlsl_register_s_n' is out of rang (must be 0..11)") \
4417+ _SG_LOGITEM_XMACRO(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED, "loading d3dcompiler_47.dll failed (d3d11)") \
4418+ _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_FAILED, "shader compilation failed (d3d11)") \
4419+ _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_OUTPUT, "") \
4420+ _SG_LOGITEM_XMACRO(D3D11_CREATE_CONSTANT_BUFFER_FAILED, "CreateBuffer() failed for uniform constant buffer (d3d11)") \
4421+ _SG_LOGITEM_XMACRO(D3D11_CREATE_INPUT_LAYOUT_FAILED, "CreateInputLayout() failed (d3d11)") \
4422+ _SG_LOGITEM_XMACRO(D3D11_CREATE_RASTERIZER_STATE_FAILED, "CreateRasterizerState() failed (d3d11)") \
4423+ _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED, "CreateDepthStencilState() failed (d3d11)") \
4424+ _SG_LOGITEM_XMACRO(D3D11_CREATE_BLEND_STATE_FAILED, "CreateBlendState() failed (d3d11)") \
4425+ _SG_LOGITEM_XMACRO(D3D11_CREATE_RTV_FAILED, "CreateRenderTargetView() failed (d3d11)") \
4426+ _SG_LOGITEM_XMACRO(D3D11_CREATE_DSV_FAILED, "CreateDepthStencilView() failed (d3d11)") \
4427+ _SG_LOGITEM_XMACRO(D3D11_CREATE_UAV_FAILED, "CreateUnorderedAccessView() failed (d3d11)") \
4428+ _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED, "Map() failed when updating buffer (d3d11)") \
4429+ _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_APPEND_BUFFER_FAILED, "Map() failed when appending to buffer (d3d11)") \
4430+ _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED, "Map() failed when updating image (d3d11)") \
4431+ _SG_LOGITEM_XMACRO(METAL_CREATE_BUFFER_FAILED, "failed to create buffer object (metal)") \
4432+ _SG_LOGITEM_XMACRO(METAL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (metal)") \
4433+ _SG_LOGITEM_XMACRO(METAL_CREATE_TEXTURE_FAILED, "failed to create texture object (metal)") \
4434+ _SG_LOGITEM_XMACRO(METAL_CREATE_SAMPLER_FAILED, "failed to create sampler object (metal)") \
4435+ _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_FAILED, "shader compilation failed (metal)") \
4436+ _SG_LOGITEM_XMACRO(METAL_SHADER_CREATION_FAILED, "shader creation failed (metal)") \
4437+ _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_OUTPUT, "") \
4438+ _SG_LOGITEM_XMACRO(METAL_SHADER_ENTRY_NOT_FOUND, "shader entry function not found (metal)") \
4439+ _SG_LOGITEM_XMACRO(METAL_UNIFORMBLOCK_MSL_BUFFER_SLOT_OUT_OF_RANGE, "uniform block 'msl_buffer_n' is out of range (must be 0..7)") \
4440+ _SG_LOGITEM_XMACRO(METAL_STORAGEBUFFER_MSL_BUFFER_SLOT_OUT_OF_RANGE, "storage buffer 'msl_buffer_n' is out of range (must be 8..23)") \
4441+ _SG_LOGITEM_XMACRO(METAL_STORAGEIMAGE_MSL_TEXTURE_SLOT_OUT_OF_RANGE, "storage image 'msl_texture_n' is out of range (must be 0..31)") \
4442+ _SG_LOGITEM_XMACRO(METAL_IMAGE_MSL_TEXTURE_SLOT_OUT_OF_RANGE, "image 'msl_texture_n' is out of range (must be 0..31)") \
4443+ _SG_LOGITEM_XMACRO(METAL_SAMPLER_MSL_SAMPLER_SLOT_OUT_OF_RANGE, "sampler 'msl_sampler_n' is out of range (must be 0..11)") \
4444+ _SG_LOGITEM_XMACRO(METAL_CREATE_CPS_FAILED, "failed to create compute pipeline state (metal)") \
4445+ _SG_LOGITEM_XMACRO(METAL_CREATE_CPS_OUTPUT, "") \
4446+ _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_FAILED, "failed to create render pipeline state (metal)") \
4447+ _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_OUTPUT, "") \
4448+ _SG_LOGITEM_XMACRO(METAL_CREATE_DSS_FAILED, "failed to create depth stencil state (metal)") \
4449+ _SG_LOGITEM_XMACRO(WGPU_BINDGROUPS_POOL_EXHAUSTED, "bindgroups pool exhausted (increase sg_desc.bindgroups_cache_size) (wgpu)") \
4450+ _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE, "sg_desc.wgpu.bindgroups_cache_size must be > 1 (wgpu)") \
4451+ _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_POW2, "sg_desc.wgpu.bindgroups_cache_size must be a power of 2 (wgpu)") \
4452+ _SG_LOGITEM_XMACRO(WGPU_CREATEBINDGROUP_FAILED, "wgpuDeviceCreateBindGroup failed") \
4453+ _SG_LOGITEM_XMACRO(WGPU_CREATE_BUFFER_FAILED, "wgpuDeviceCreateBuffer() failed") \
4454+ _SG_LOGITEM_XMACRO(WGPU_CREATE_TEXTURE_FAILED, "wgpuDeviceCreateTexture() failed") \
4455+ _SG_LOGITEM_XMACRO(WGPU_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed") \
4456+ _SG_LOGITEM_XMACRO(WGPU_CREATE_SAMPLER_FAILED, "wgpuDeviceCreateSampler() failed") \
4457+ _SG_LOGITEM_XMACRO(WGPU_CREATE_SHADER_MODULE_FAILED, "wgpuDeviceCreateShaderModule() failed") \
4458+ _SG_LOGITEM_XMACRO(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED, "wgpuDeviceCreateBindGroupLayout() for shader stage failed") \
4459+ _SG_LOGITEM_XMACRO(WGPU_UNIFORMBLOCK_WGSL_GROUP0_BINDING_OUT_OF_RANGE, "uniform block 'wgsl_group0_binding_n' is out of range (must be 0..15)") \
4460+ _SG_LOGITEM_XMACRO(WGPU_TEXTURE_WGSL_GROUP1_BINDING_OUT_OF_RANGE, "texture 'wgsl_group1_binding_n' is out of range (must be 0..127)") \
4461+ _SG_LOGITEM_XMACRO(WGPU_STORAGEBUFFER_WGSL_GROUP1_BINDING_OUT_OF_RANGE, "storage buffer 'wgsl_group1_binding_n' is out of range (must be 0..127)") \
4462+ _SG_LOGITEM_XMACRO(WGPU_STORAGEIMAGE_WGSL_GROUP1_BINDING_OUT_OF_RANGE, "storage image 'wgsl_group1_binding_n' is out of range (must be 0..127)") \
4463+ _SG_LOGITEM_XMACRO(WGPU_SAMPLER_WGSL_GROUP1_BINDING_OUT_OF_RANGE, "sampler 'wgsl_group1_binding_n' is out of range (must be 0..127)") \
4464+ _SG_LOGITEM_XMACRO(WGPU_CREATE_PIPELINE_LAYOUT_FAILED, "wgpuDeviceCreatePipelineLayout() failed") \
4465+ _SG_LOGITEM_XMACRO(WGPU_CREATE_RENDER_PIPELINE_FAILED, "wgpuDeviceCreateRenderPipeline() failed") \
4466+ _SG_LOGITEM_XMACRO(WGPU_CREATE_COMPUTE_PIPELINE_FAILED, "wgpuDeviceCreateComputePipeline() failed") \
4467+ _SG_LOGITEM_XMACRO(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING, "vulkan: could not look up a required extension function pointer") \
4468+ _SG_LOGITEM_XMACRO(VULKAN_ALLOC_DEVICE_MEMORY_NO_SUITABLE_MEMORY_TYPE, "vulkan: could not find suitable memory type") \
4469+ _SG_LOGITEM_XMACRO(VULKAN_ALLOCATE_MEMORY_FAILED, "vulkan: vkAllocateMemory() failed!") \
4470+ _SG_LOGITEM_XMACRO(VULKAN_ALLOC_BUFFER_DEVICE_MEMORY_FAILED, "vulkan: allocating buffer device memory failed") \
4471+ _SG_LOGITEM_XMACRO(VULKAN_ALLOC_IMAGE_DEVICE_MEMORY_FAILED, "vulkan: allocating image device memory failed") \
4472+ _SG_LOGITEM_XMACRO(VULKAN_DELETE_QUEUE_EXHAUSTED, "vulkan: internal delete queue exhausted (too many objects destroyed per frame)") \
4473+ _SG_LOGITEM_XMACRO(VULKAN_STAGING_CREATE_BUFFER_FAILED, "vulkan: vkCreateBuffer() failed for staging buffer") \
4474+ _SG_LOGITEM_XMACRO(VULKAN_STAGING_ALLOCATE_MEMORY_FAILED, "vulkan: allocating device memory for staging buffer failed") \
4475+ _SG_LOGITEM_XMACRO(VULKAN_STAGING_BIND_BUFFER_MEMORY_FAILED, "vulkan: vkBindBufferMemory() failed for staging buffer") \
4476+ _SG_LOGITEM_XMACRO(VULKAN_STAGING_STREAM_BUFFER_OVERFLOW, "vulkan: per-frame stream staging buffer has overflown (sg_desc.vulkan.stream_staging_buffer_size)") \
4477+ _SG_LOGITEM_XMACRO(VULKAN_CREATE_SHARED_BUFFER_FAILED, "vulkan: vkCreateBuffer() failed for cpu/gpu-shared buffer") \
4478+ _SG_LOGITEM_XMACRO(VULKAN_ALLOCATE_SHARED_BUFFER_MEMORY_FAILED, "vulkan: allocating device memory for cpu/gpu-shared buffer failed") \
4479+ _SG_LOGITEM_XMACRO(VULKAN_BIND_SHARED_BUFFER_MEMORY_FAILED, "vulkan: vkBindBufferMemory() failed for cpu/gpu-shared buffer") \
4480+ _SG_LOGITEM_XMACRO(VULKAN_MAP_SHARED_BUFFER_MEMORY_FAILED, "vulkan: vkMapMemory() failed on cpu/gpu-shared buffer") \
4481+ _SG_LOGITEM_XMACRO(VULKAN_CREATE_BUFFER_FAILED, "vulkan: vkCreateBuffer() failed!") \
4482+ _SG_LOGITEM_XMACRO(VULKAN_BIND_BUFFER_MEMORY_FAILED, "vulkan: vkBindBufferMemory() failed!") \
4483+ _SG_LOGITEM_XMACRO(VULKAN_CREATE_IMAGE_FAILED, "vulkan: vkCreateImage() failed!") \
4484+ _SG_LOGITEM_XMACRO(VULKAN_BIND_IMAGE_MEMORY_FAILED, "vulkan: vkBindImageMemory() failed!") \
4485+ _SG_LOGITEM_XMACRO(VULKAN_CREATE_SHADER_MODULE_FAILED, "vukan: vkCreateShaderModule() failed!") \
4486+ _SG_LOGITEM_XMACRO(VULKAN_UNIFORMBLOCK_SPIRV_SET0_BINDING_OUT_OF_RANGE, "vulkan: uniform block 'spirv_set0_binding_n' is out of range (must be 0..15)") \
4487+ _SG_LOGITEM_XMACRO(VULKAN_TEXTURE_SPIRV_SET1_BINDING_OUT_OF_RANGE, "vulkan: texture 'spirv_set1_binding_n' is out of range (must be 0..127)") \
4488+ _SG_LOGITEM_XMACRO(VULKAN_STORAGEBUFFER_SPIRV_SET1_BINDING_OUT_OF_RANGE, "vulkan: storage buffer 'spirv_set1_binding_n' is out of range (must be 0..127)") \
4489+ _SG_LOGITEM_XMACRO(VULKAN_STORAGEIMAGE_SPIRV_SET1_BINDING_OUT_OF_RANGE, "vulkan: storage image 'spirv_set1_binding_n' is out of range (must be 0..127)") \
4490+ _SG_LOGITEM_XMACRO(VULKAN_SAMPLER_SPIRV_SET1_BINDING_OUT_OF_RANGE, "vulkan: sampler 'spirv_set1_binding_n' is out of range (must be 0..127)") \
4491+ _SG_LOGITEM_XMACRO(VULKAN_CREATE_DESCRIPTOR_SET_LAYOUT_FAILED, "vulkan: vkCreateDescriptorSetLayout() failed!") \
4492+ _SG_LOGITEM_XMACRO(VULKAN_SHADER_UNIFORM_DESCRIPTOR_SET_SIZE_VS_CACHE_SIZE, "vulkan: shader uniform descriptor set is too big for the descriptor set cache (please write a Github issue)") \
4493+ _SG_LOGITEM_XMACRO(VULKAN_CREATE_PIPELINE_LAYOUT_FAILED, "vulkan: vkCreatePipelineLayout() failed!") \
4494+ _SG_LOGITEM_XMACRO(VULKAN_CREATE_GRAPHICS_PIPELINE_FAILED, "vulkan: vkCreateGraphicsPipelines() failed!") \
4495+ _SG_LOGITEM_XMACRO(VULKAN_CREATE_COMPUTE_PIPELINE_FAILED, "vulkan: vkCreateComputePipelines() failed!") \
4496+ _SG_LOGITEM_XMACRO(VULKAN_CREATE_IMAGE_VIEW_FAILED, "vulkan: vkCreateImageView() failed!") \
4497+ _SG_LOGITEM_XMACRO(VULKAN_VIEW_MAX_DESCRIPTOR_SIZE, "vulkan: required view descriptor size is greater than _SG_VK_MAX_DESCRIPTOR_DATA_SIZE") \
4498+ _SG_LOGITEM_XMACRO(VULKAN_CREATE_SAMPLER_FAILED, "vulkan: vkCreateSampler() failed!") \
4499+ _SG_LOGITEM_XMACRO(VULKAN_SAMPLER_MAX_DESCRIPTOR_SIZE, "vulkan: required sampler descriptor size is greater than _SG_VK_MAX_DESCRIPTOR_DATA_SIZE") \
4500+ _SG_LOGITEM_XMACRO(VULKAN_WAIT_FOR_FENCE_FAILED, "vulkan: vkWaitForFence() failed!") \
4501+ _SG_LOGITEM_XMACRO(VULKAN_UNIFORM_BUFFER_OVERFLOW, "vulkan: uniform buffer has overflown (increase sg_desc.uniform_buffer_size)") \
4502+ _SG_LOGITEM_XMACRO(VULKAN_DESCRIPTOR_BUFFER_OVERFLOW, "vulkan: desccriptor buffer has overflown (increase sg_desc.vulkan.descriptor_buffer_size)") \
4503+ _SG_LOGITEM_XMACRO(IDENTICAL_COMMIT_LISTENER, "attempting to add identical commit listener") \
4504+ _SG_LOGITEM_XMACRO(COMMIT_LISTENER_ARRAY_FULL, "commit listener array full") \
4505+ _SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SOKOL_TRACE_HOOKS is not defined") \
4506+ _SG_LOGITEM_XMACRO(DEALLOC_BUFFER_INVALID_STATE, "sg_dealloc_buffer(): buffer must be in ALLOC state") \
4507+ _SG_LOGITEM_XMACRO(DEALLOC_IMAGE_INVALID_STATE, "sg_dealloc_image(): image must be in alloc state") \
4508+ _SG_LOGITEM_XMACRO(DEALLOC_SAMPLER_INVALID_STATE, "sg_dealloc_sampler(): sampler must be in alloc state") \
4509+ _SG_LOGITEM_XMACRO(DEALLOC_SHADER_INVALID_STATE, "sg_dealloc_shader(): shader must be in ALLOC state") \
4510+ _SG_LOGITEM_XMACRO(DEALLOC_PIPELINE_INVALID_STATE, "sg_dealloc_pipeline(): pipeline must be in ALLOC state") \
4511+ _SG_LOGITEM_XMACRO(DEALLOC_VIEW_INVALID_STATE, "sg_dealloc_view(): view must be in ALLOC state") \
4512+ _SG_LOGITEM_XMACRO(INIT_BUFFER_INVALID_STATE, "sg_init_buffer(): buffer must be in ALLOC state") \
4513+ _SG_LOGITEM_XMACRO(INIT_IMAGE_INVALID_STATE, "sg_init_image(): image must be in ALLOC state") \
4514+ _SG_LOGITEM_XMACRO(INIT_SAMPLER_INVALID_STATE, "sg_init_sampler(): sampler must be in ALLOC state") \
4515+ _SG_LOGITEM_XMACRO(INIT_SHADER_INVALID_STATE, "sg_init_shader(): shader must be in ALLOC state") \
4516+ _SG_LOGITEM_XMACRO(INIT_PIPELINE_INVALID_STATE, "sg_init_pipeline(): pipeline must be in ALLOC state") \
4517+ _SG_LOGITEM_XMACRO(INIT_VIEW_INVALID_STATE, "sg_init_view(): view must be in ALLOC state") \
4518+ _SG_LOGITEM_XMACRO(UNINIT_BUFFER_INVALID_STATE, "sg_uninit_buffer(): buffer must be in VALID, FAILED or ALLOC state") \
4519+ _SG_LOGITEM_XMACRO(UNINIT_IMAGE_INVALID_STATE, "sg_uninit_image(): image must be in VALID, FAILED or ALLOC state") \
4520+ _SG_LOGITEM_XMACRO(UNINIT_SAMPLER_INVALID_STATE, "sg_uninit_sampler(): sampler must be in VALID, FAILED or ALLOC state") \
4521+ _SG_LOGITEM_XMACRO(UNINIT_SHADER_INVALID_STATE, "sg_uninit_shader(): shader must be in VALID, FAILED or ALLOC state") \
4522+ _SG_LOGITEM_XMACRO(UNINIT_PIPELINE_INVALID_STATE, "sg_uninit_pipeline(): pipeline must be in VALID, FAILED or ALLOC state") \
4523+ _SG_LOGITEM_XMACRO(UNINIT_VIEW_INVALID_STATE, "sg_uninit_view(): view must be in VALID, FAILED or ALLOC state") \
4524+ _SG_LOGITEM_XMACRO(FAIL_BUFFER_INVALID_STATE, "sg_fail_buffer(): buffer must be in ALLOC state") \
4525+ _SG_LOGITEM_XMACRO(FAIL_IMAGE_INVALID_STATE, "sg_fail_image(): image must be in ALLOC state") \
4526+ _SG_LOGITEM_XMACRO(FAIL_SAMPLER_INVALID_STATE, "sg_fail_sampler(): sampler must be in ALLOC state") \
4527+ _SG_LOGITEM_XMACRO(FAIL_SHADER_INVALID_STATE, "sg_fail_shader(): shader must be in ALLOC state") \
4528+ _SG_LOGITEM_XMACRO(FAIL_PIPELINE_INVALID_STATE, "sg_fail_pipeline(): pipeline must be in ALLOC state") \
4529+ _SG_LOGITEM_XMACRO(FAIL_VIEW_INVALID_STATE, "sg_fail_view(): view must be in ALLOC state") \
4530+ _SG_LOGITEM_XMACRO(BUFFER_POOL_EXHAUSTED, "buffer pool exhausted") \
4531+ _SG_LOGITEM_XMACRO(IMAGE_POOL_EXHAUSTED, "image pool exhausted") \
4532+ _SG_LOGITEM_XMACRO(SAMPLER_POOL_EXHAUSTED, "sampler pool exhausted") \
4533+ _SG_LOGITEM_XMACRO(SHADER_POOL_EXHAUSTED, "shader pool exhausted") \
4534+ _SG_LOGITEM_XMACRO(PIPELINE_POOL_EXHAUSTED, "pipeline pool exhausted") \
4535+ _SG_LOGITEM_XMACRO(VIEW_POOL_EXHAUSTED, "view pool exhausted") \
4536+ _SG_LOGITEM_XMACRO(BEGINPASS_TOO_MANY_COLOR_ATTACHMENTS, "sg_begin_pass: too many color attachments (sg_limits.max_color_attachments)") \
4537+ _SG_LOGITEM_XMACRO(BEGINPASS_TOO_MANY_RESOLVE_ATTACHMENTS, "sg_begin_pass: too many resolve attachments (sg_limits.max_color_attachments)") \
4538+ _SG_LOGITEM_XMACRO(BEGINPASS_ATTACHMENTS_ALIVE, "sg_begin_pass: an attachment was provided that no longer exists") \
4539+ _SG_LOGITEM_XMACRO(DRAW_WITHOUT_BINDINGS, "attempting to draw without resource bindings") \
4540+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_VERTEXSTAGE_TEXTURES, "sg_shader_desc: too many texture bindings on vertex shader stage (sg_limits.max_texture_bindings_per_stage)") \
4541+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_TEXTURES, "sg_shader_desc: too many texture bindings on fragment shader stage (sg_limits.max_texture_bindings_per_stage)") \
4542+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_COMPUTESTAGE_TEXTURES, "sg_shader_desc: too many texture bindings on compute shader stage (sg_limits.max_texture_bindings_per_stage)") \
4543+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_VERTEXSTAGE_STORAGEBUFFERS, "sg_shader_desc: too many storage buffer bindings on vertex shader stage (sg_limits.max_storage_buffer_bindings_per_stage)") \
4544+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_STORAGEBUFFERS, "sg_shader_desc: too many storage buffer bindings on fragment shader stage (sg_limits.max_storage_buffer_bindings_per_stage)") \
4545+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_COMPUTESTAGE_STORAGEBUFFERS, "sg_shader_desc: too many storage buffer bindings on compute shader stage (sg_limits.max_storage_buffer_bindings_per_stage)") \
4546+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_VERTEXSTAGE_STORAGEIMAGES, "sg_shader_desc: too many storage image bindings on vertex shader stage (sg_limits.max_storage_image_bindings_per_stage)") \
4547+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_STORAGEIMAGES, "sg_shader_desc: too many storage image bindings on fragment shader stage (sg_limits.max_storage_image_bindings_per_stage)") \
4548+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_COMPUTESTAGE_STORAGEIMAGES, "sg_shader_desc: too many storage image bindings on compute shader stage (sg_limits.max_storage_image_bindings_per_stage)") \
4549+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_VERTEXSTAGE_TEXTURESAMPLERPAIRS, "sg_shader_desc: too many texture-sampler-pairs on vertex shader stage (sg_limits.max_texture_bindings_per_stage)") \
4550+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_TEXTURESAMPLERPAIRS, "sg_shader_desc: too many texture-sampler-pairs on fragment shader stage (sg_limits.max_texture_bindings_per_stage)") \
4551+ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_COMPUTESTAGE_TEXTURESAMPLERPAIRS, "sg_shader_desc: too many texture-sampler-pairs on compute shader stage (sg_limits.max_texture_bindings_per_stage)") \
4552+ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_CANARY, "sg_buffer_desc not initialized") \
4553+ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_IMMUTABLE_DYNAMIC_STREAM, "sg_buffer_desc.usage: only one of .immutable, .dynamic_update, .stream_update can be true") \
4554+ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_SEPARATE_BUFFER_TYPES, "sg_buffer_desc.usage: on WebGL2, only one of .vertex_buffer or .index_buffer can be true (check sg_features.separate_buffer_types)") \
4555+ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_EXPECT_NONZERO_SIZE, "sg_buffer_desc.size must be greater zero") \
4556+ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_EXPECT_MATCHING_DATA_SIZE, "sg_buffer_desc.size and .data.size must be equal") \
4557+ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_EXPECT_ZERO_DATA_SIZE, "sg_buffer_desc.data.size expected to be zero") \
4558+ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_EXPECT_NO_DATA, "sg_buffer_desc.data.ptr must be null for dynamic/stream buffers") \
4559+ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_EXPECT_DATA, "sg_buffer_desc: initial content data must be provided for immutable buffers without storage buffer usage") \
4560+ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED, "storage buffers not supported by the backend 3D API (requires OpenGL >= 4.3)") \
4561+ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4, "size of storage buffers must be a multiple of 4") \
4562+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_NODATA, "sg_image_data: no data (.ptr and/or .size is zero)") \
4563+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_DATA_SIZE, "sg_image_data: data size doesn't match expected surface size") \
4564+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_CANARY, "sg_image_desc not initialized") \
4565+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMMUTABLE_DYNAMIC_STREAM, "sg_image_desc.usage: only one of .immutable, .dynamic_update, .stream_update can be true") \
4566+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_COLOR_DEPTH_STENCIL, "sg_image_desc.usage: only one of .color_attachment and .depth_stencil_attachment can be true") \
4567+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_2D_NUMSLICES, "sg_image_desc.num_slices must be exactly 1 for SG_IMAGETYPE_2D") \
4568+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_CUBE_NUMSLICES, "sg_image_desc.num_slices must be exactly 6 for SG_IMAGETYPE_CUBE") \
4569+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_ARRAY_NUMSLICES, "sg_image_desc.num_slices must be ((>= 1) && (<= sg_limits.max_image_array_layers)) for SG_IMAGETYPE_ARRAY") \
4570+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_3D_NUMSLICES, "sg_image_desc.num_slices must be ((>= 1) && (<= sg_limits.max_image_size_3d)) for SG_IMAGETYPE_ARRAY") \
4571+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NUMSLICES, "sg_image_desc.num_slices must be > 0") \
4572+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WIDTH, "sg_image_desc.width must be > 0") \
4573+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_HEIGHT, "sg_image_desc.height must be > 0") \
4574+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, "invalid pixel format for non-render-target image") \
4575+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_BUT_NO_ATTACHMENT, "non-attachment images cannot be multisampled") \
4576+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE, "3D images cannot have a depth/stencil image format") \
4577+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_IMMUTABLE, "attachment and storage images must be sg_image_usage.immutable") \
4578+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_NO_DATA, "render/storage attachment images cannot be initialized with data") \
4579+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_PIXELFORMAT, "invalid pixel format for render attachment image") \
4580+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_RESOLVE_EXPECT_NO_MSAA, "resolve attachment images cannot be multisampled") \
4581+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_NO_MSAA_SUPPORT, "multisampling not supported for this pixel format") \
4582+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_NUM_MIPMAPS, "multisample images must have num_mipmaps == 1") \
4583+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_3D_IMAGE, "3D images cannot have a sample_count > 1") \
4584+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_CUBE_IMAGE, "cube images cannot have sample_count > 1") \
4585+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_ARRAY_IMAGE, "array images cannot have sample_count > 1") \
4586+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_STORAGEIMAGE_PIXELFORMAT, "invalid pixel format for storage image") \
4587+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_STORAGEIMAGE_EXPECT_NO_MSAA, "storage images cannot be multisampled") \
4588+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_INJECTED_NO_DATA, "images with injected textures cannot be initialized with data") \
4589+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, "dynamic/stream-update images cannot be initialized with data") \
4590+ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, "compressed images must be immutable") \
4591+ _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_CANARY, "sg_sampler_desc not initialized") \
4592+ _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING, "sg_sampler_desc.max_anisotropy > 1 requires min/mag/mipmap_filter to be SG_FILTER_LINEAR") \
4593+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_CANARY, "sg_shader_desc not initialized") \
4594+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VERTEX_SOURCE, "vertex shader source code expected") \
4595+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_FRAGMENT_SOURCE, "fragment shader source code expected") \
4596+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_COMPUTE_SOURCE, "compute shader source code expected") \
4597+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VERTEX_SOURCE_OR_BYTECODE, "vertex shader source or byte code expected") \
4598+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_FRAGMENT_SOURCE_OR_BYTECODE, "fragment shader source or byte code expected") \
4599+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_COMPUTE_SOURCE_OR_BYTECODE, "compute shader source or byte code expected") \
4600+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_INVALID_SHADER_COMBO, "cannot combine compute shaders with vertex or fragment shaders") \
4601+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, "shader byte code length (in bytes) required") \
4602+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_METAL_THREADS_PER_THREADGROUP_INITIALIZED, "sg_shader_desc.mtl_threads_per_threadgroup must be initialized for compute shaders (metal)") \
4603+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_METAL_THREADS_PER_THREADGROUP_MULTIPLE_32, "sg_shader_desc.mtl_threads_per_threadgroup (x * y * z) must be a multiple of 32 (metal)") \
4604+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_NO_CONT_MEMBERS, "sg_shader_desc.uniform_blocks[].glsl_uniforms[]: items must occupy continuous slots") \
4605+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_SIZE_IS_ZERO, "sg_shader_desc.uniform_blocks[].size cannot be zero") \
4606+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_METAL_BUFFER_SLOT_COLLISION, "sg_shader_desc.uniform_blocks[].msl_buffer_n must be unique across uniform blocks and storage buffers in same shader stage") \
4607+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_HLSL_REGISTER_B_COLLISION, "sg_shader_desc.uniform_blocks[].hlsl_register_b_n must be unique across uniform blocks in same shader stage") \
4608+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_WGSL_GROUP0_BINDING_COLLISION, "sg_shader_desc.uniform_blocks[].wgsl_group0_binding_n must be unique across all uniform blocks") \
4609+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_SPIRV_SET0_BINDING_COLLISION, "sg_shader_desc.unifrom_blocks[].spirv_set0_binding_n must be unique across all uniform blocks") \
4610+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_NO_MEMBERS, "sg_shader_desc.uniform_blocks[].glsl_uniforms[]: GL backend requires uniform block member declarations") \
4611+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_UNIFORM_GLSL_NAME, "sg_shader_desc.uniform_blocks[].glsl_uniforms[].glsl_name missing") \
4612+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_SIZE_MISMATCH, "sg_shader_desc.uniform_blocks[].glsl_uniforms[]: size of uniform block members doesn't match uniform block size") \
4613+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_ARRAY_COUNT, "sg_shader_desc.uniform_blocks[].glsl_uniforms[].array_count must be >= 1") \
4614+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UNIFORMBLOCK_STD140_ARRAY_TYPE, "sg_shader_desc.uniform_blocks[].glsl_uniforms[].type: uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout") \
4615+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_METAL_BUFFER_SLOT_COLLISION, "sg_shader_desc.views[].storage_buffer.storagemsl_buffer_n must be unique across uniform blocks and storage buffer in same shader stage") \
4616+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_HLSL_REGISTER_T_COLLISION, "sg_shader_desc.views[].storage_buffer.hlsl_register_t_n must be unique across read-only storage buffers and images in same shader stage") \
4617+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_HLSL_REGISTER_U_COLLISION, "sg_shader_desc.views[].storage_buffer.hlsl_register_u_n must be unique across read/write storage buffers and storage images in same shader stage") \
4618+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_GLSL_BINDING_COLLISION, "sg_shader_desc.views[].storage_buffer.glsl_binding_n must be unique across shader stages") \
4619+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_WGSL_GROUP1_BINDING_COLLISION, "sg_shader_desc.views[].storage_buffer.wgsl_group1_binding_n must be unique across all view and sampler bindings") \
4620+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_SPIRV_SET1_BINDING_COLLISION, "sg_shader_desc.views[].storage_buffer.spirv_set1_binding_n must be unique across all view and sampler bindings") \
4621+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_EXPECT_COMPUTE_STAGE, "sg_shader_desc.views[].storage_image: storage images are allowed on the compute stage") \
4622+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_METAL_TEXTURE_SLOT_COLLISION, "sg_shader_desc.views[].storage_image.msl_texture_n must be unique across images and storage images in same shader stage") \
4623+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_HLSL_REGISTER_U_COLLISION, "sg_shader_desc.views[].storage_image.hlsl_register_u_n must be unique across storage images and read/write storage buffers in same shader stage") \
4624+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_GLSL_BINDING_COLLISION, "sg_shader_desc.views[].storage_image.glsl_binding_n must be unique across shader stages") \
4625+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_WGSL_GROUP1_BINDING_COLLISION, "sg_shader_desc.views[].storage_image.wgsl_group1_binding_n must be unique across all view and sampler bindings") \
4626+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_SPIRV_SET1_BINDING_COLLISION, "sg_shader_desc.views[].storage_image.spirv_set1_binding_n must be unique across all view and sampler bindings") \
4627+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_TEXTURE_METAL_TEXTURE_SLOT_COLLISION, "sg_shader_desc.views[].texture.msl_texture_n must be unique across textures and storage images in same shader stage") \
4628+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_TEXTURE_HLSL_REGISTER_T_COLLISION, "sg_shader_desc.views[].texture.hlsl_register_t_n must be unique across textures and storage buffers in same shader stage") \
4629+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_TEXTURE_WGSL_GROUP1_BINDING_COLLISION, "sg_shader_desc.views[].texture.wgsl_group1_binding_n must be unique across all view and sampler bindings") \
4630+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_VIEW_TEXTURE_SPIRV_SET1_BINDING_COLLISION, "sg_shader_desc.views[].texture.spirv_set1_binding_n must be unique across all view and sampler bindings") \
4631+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_METAL_SAMPLER_SLOT_COLLISION, "sg_shader_desc.samplers[].msl_sampler_n must be unique in same shader stage") \
4632+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_HLSL_REGISTER_S_COLLISION, "sg_shader_desc.samplers[].hlsl_register_s_n must be unique in same shader stage") \
4633+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_WGSL_GROUP1_BINDING_COLLISION, "sg_shader_desc.samplers[].wgsl_group1_binding_n must be unique across all view and sampler bindings") \
4634+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_SPIRV_SET1_BINDING_COLLISION, "sg_shader_desc.samplers[].spirv_set1_binding_n must be unique across all view and sampler bindings") \
4635+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_VIEW_SLOT_OUT_OF_RANGE, "texture-sampler-pair view slot index is out of range (sg_shader_desc.texture_sampler_pairs[].view_slot)") \
4636+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_SAMPLER_SLOT_OUT_OF_RANGE, "texture-sampler-pair sampler slot index is out of range (sg_shader_desc.texture_sampler_pairs[].sampler_slot)") \
4637+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_TEXTURE_STAGE_MISMATCH, "texture-sampler-pair stage doesn't match referenced texture stage") \
4638+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_EXPECT_TEXTURE_VIEW, "texture-sampler-pair view must be a texture view (sg_shader_desc.texture_sampler_pairs[].view_slot => sg_shaders_desc.views[i].texture)") \
4639+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_SAMPLER_STAGE_MISMATCH, "texture-sampler-pair stage doesn't match referenced sampler stage") \
4640+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_GLSL_NAME, "texture-sampler-pair 'glsl_name' missing") \
4641+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NONFILTERING_SAMPLER_REQUIRED, "image sample type UNFILTERABLE_FLOAT, UINT, SINT can only be used with NONFILTERING sampler") \
4642+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_COMPARISON_SAMPLER_REQUIRED, "image sample type DEPTH can only be used with COMPARISON sampler") \
4643+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_TEXVIEW_NOT_REFERENCED_BY_TEXTURE_SAMPLER_PAIRS, "one or more texture views are not referenced by by texture-sampler-pairs (sg_shader_desc.texture_sampler_pairs[].view_slot)") \
4644+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_TEXTURE_SAMPLER_PAIRS, "one or more samplers are not referenced by texture-sampler-pairs (sg_shader_desc.texture_sampler_pairs[].sampler_slot)") \
4645+ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, "vertex attribute name/semantic string too long (max len 16)") \
4646+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_CANARY, "sg_pipeline_desc not initialized") \
4647+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_SHADER, "sg_pipeline_desc.shader missing or invalid") \
4648+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_COMPUTE_SHADER_EXPECTED, "sg_pipeline_desc.shader must be a compute shader") \
4649+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_COMPUTE_SHADER_EXPECTED, "sg_pipeline_desc.compute is false, but shader is a compute shader") \
4650+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_CONT_ATTRS, "sg_pipeline_desc.layout.attrs is not continuous") \
4651+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH, "sg_pipeline_desc.layout.attrs[].format is incompatible with sg_shader_desc.attrs[].base_type") \
4652+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_VERTEXFORMAT_INT10_N2_NOT_SUPPORTED, "sg_pipeline_desc.layout.attrs[].format: SG_VERTEXFORMAT_INT10_N2 not supported on this platform") \
4653+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4") \
4654+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, "D3D11 missing vertex attribute semantics in shader") \
4655+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_SHADER_READONLY_STORAGEBUFFERS, "sg_pipeline_desc.shader: only readonly storage buffer bindings allowed in render pipelines") \
4656+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_BLENDOP_MINMAX_REQUIRES_BLENDFACTOR_ONE, "SG_BLENDOP_MIN/MAX requires all blend factors to be SG_BLENDFACTOR_ONE") \
4657+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_DUAL_SOURCE_BLENDING_NOT_SUPPORTED, "dual source blending not supported (sg_features.dual_source_blending)") \
4658+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_DEPTH_FORMAT_NONE_BUT_DEPTH_WRITE_ENABLED, "sg_pipeline_desc.depth.write_enabled cannot be true when sg_pipeline_desc.depth.pixel_format is SG_PIXELFORMAT_NONE") \
4659+ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_DEPTH_FORMAT_NONE_COMPARE_FUNC_MISMATCH, "sg_pipeline_desc.depth.compare must be SG_COMPAREFUNC_ALWAYS or SG_COMPAREFUNC_NEVER when sg_pipeline_desc.pixel_format is SG_PIXELFORMAT_NONE") \
4660+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_CANARY, "sg_view_desc not initialized") \
4661+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE, "sg_view_desc: only one view type can be active") \
4662+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_ANY_VIEWTYPE, "sg_view_desc: exactly one view type must be active") \
4663+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_RESOURCE_ALIVE, "sg_view_desc: resource object is no longer alive (.buffer or .image)") \
4664+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_RESOURCE_FAILED, "sg_view_desc: resource object cannot be in FAILED state (.buffer or .image)") \
4665+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_STORAGEBUFFER_OFFSET_VS_BUFFER_SIZE, "sg_view_desc.storage_buffer.offset is >= buffer size") \
4666+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_STORAGEBUFFER_OFFSET_MULTIPLE_256, "sg_view_desc.storage_buffer.offset must be a multiple of 256") \
4667+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_STORAGEBUFFER_USAGE, "sg_view_desc.storage_buffer.buffer must have been created with sg_buffer_desc.usage.storage_buffer = true") \
4668+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_STORAGEIMAGE_USAGE, "sg_view_desc.storage_image.image must have been created with sg_image_desc.usage.storage_image = true") \
4669+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_COLORATTACHMENT_USAGE, "sg_view_desc.color_attachment.image must have been created with sg_image_desc.usage.color_attachment = true") \
4670+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_RESOLVEATTACHMENT_USAGE, "sg_view_desc.resolve_attachment.image must have been created with sg_image_desc.usage.resolve_attachment = true") \
4671+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_DEPTHSTENCILATTACHMENT_USAGE, "sg_view_desc.depth_stencil_attachment.image must have been created with sg_image_desc.usage.depth_stencil_attachment = true") \
4672+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_IMAGE_MIPLEVEL, "sg_view_desc: image/attachment view mip level is out of range (must be >=0 and <image.num_miplevels)") \
4673+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_IMAGE_2D_SLICE, "sg_view_desc: image/attachment view slice is out of range for 2D image (must be 0)") \
4674+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_IMAGE_CUBEMAP_SLICE, "sg_view_desc: image/attachment view slice is out of range for cubemap image (must be >=0 and <6)") \
4675+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_IMAGE_ARRAY_SLICE, "sg_view_desc: image/attachment view slice is out of range for 2D array image (must be >=0 and <image.num_slices") \
4676+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_IMAGE_3D_SLICE, "sg_view_desc: image/attachment view slice is out of range for 3D image (must be 0)") \
4677+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_TEXTURE_EXPECT_NO_MSAA, "sg_view_desc: MSAA texture bindings not allowed on this backend (sg_features.msaa_texture_bindings)") \
4678+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_TEXTURE_MIPLEVELS, "sg_view_desc: texture view mip levels are out of range (must be >=0 and <image.num_miplevels)") \
4679+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_TEXTURE_2D_SLICES, "sg_view_desc: texture view slices are out of range for 2D image (must be 0)") \
4680+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_TEXTURE_CUBEMAP_SLICES, "sg_view_desc: texture view slices are out of range for cubemap image (must be 0)") \
4681+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_TEXTURE_ARRAY_SLICES, "sg_view_desc: texture view slices are out of range for 2D array image (must be >=0 and <image.num_slices") \
4682+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_TEXTURE_3D_SLICES, "sg_view_desc: texture view slices are out of range for 3D image (must be 0)") \
4683+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_STORAGEIMAGE_PIXELFORMAT, "sg_view_desc.storage_image_binding: image pixel format must be GPU readable or writable (sg_pixelformat_info.read/write)") \
4684+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_COLORATTACHMENT_PIXELFORMAT, "sg_view_desc.color_attachment: pixel format of image must be renderable (sg_pixelformat_info.render)") \
4685+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_DEPTHSTENCILATTACHMENT_PIXELFORMAT, "sg_view_desc.depth_stencil_attachment: pixel format of image must be a depth or depth-stencil format (sg_pixelformat_info.depth)") \
4686+ _SG_LOGITEM_XMACRO(VALIDATE_VIEWDESC_RESOLVEATTACHMENT_SAMPLECOUNT, "sg_view_desc.resolve_attachment: image cannot be multisampled") \
4687+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_CANARY, "sg_begin_pass: pass struct not initialized") \
4688+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COMPUTEPASS_EXPECT_NO_ATTACHMENTS, "sg_begin_pass: compute passes cannot have attachments") \
4689+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH, "sg_begin_pass: expected pass.swapchain.width > 0") \
4690+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH_NOTSET, "sg_begin_pass: expected pass.swapchain.width == 0") \
4691+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT, "sg_begin_pass: expected pass.swapchain.height > 0") \
4692+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT_NOTSET, "sg_begin_pass: expected pass.swapchain.height == 0") \
4693+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT, "sg_begin_pass: expected pass.swapchain.sample_count > 0") \
4694+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT_NOTSET, "sg_begin_pass: expected pass.swapchain.sample_count == 0") \
4695+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT, "sg_begin_pass: expected pass.swapchain.color_format to be valid") \
4696+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT_NOTSET, "sg_begin_pass: expected pass.swapchain.color_format to be unset") \
4697+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_DEPTHFORMAT_NOTSET, "sg_begin_pass: expected pass.swapchain.depth_format to be unset") \
4698+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE, "sg_begin_pass: expected pass.swapchain.metal.current_drawable != 0") \
4699+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.current_drawable == 0") \
4700+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE, "sg_begin_pass: expected pass.swapchain.metal.depth_stencil_texture != 0") \
4701+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.depth_stencil_texture == 0") \
4702+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE, "sg_begin_pass: expected pass.swapchain.metal.msaa_color_texture != 0") \
4703+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.msaa_color_texture == 0") \
4704+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.render_view != 0") \
4705+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.render_view == 0") \
4706+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.resolve_view != 0") \
4707+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.resolve_view == 0") \
4708+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.depth_stencil_view != 0") \
4709+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.depth_stencil_view == 0") \
4710+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.render_view != 0") \
4711+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.render_view == 0") \
4712+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.resolve_view != 0") \
4713+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.resolve_view == 0") \
4714+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.depth_stencil_view != 0") \
4715+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.depth_stencil_view == 0") \
4716+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_GL_EXPECT_FRAMEBUFFER_NOTSET, "sg_begin_pass: expected pass.swapchain.gl.framebuffer == 0") \
4717+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERIMAGE, "sg_begin_pass: expected pass.swapchain.vk.render_image != 0") \
4718+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERIMAGE_NOTSET, "sg_begin_pass: expected pass.swapchain.vk.render_image == 0") \
4719+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERVIEW, "sg_begin_pass: expected pass.swapchain.vk.render_view != 0") \
4720+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.vk.render_view == 0") \
4721+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_DEPTHSTENCILIMAGE, "sg_begin_pass: expected pass.swapchain.vk.depth_stencil_image != 0") \
4722+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_DEPTHSTENCILIMAGE_NOTSET, "sg_begin_pass: expected pass.swapchain.vk.depth_stencil_image == 0") \
4723+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_DEPTHSTENCILVIEW, "sg_begin_pass: expected pass.swapchain.vk.depth_stencil_view != 0") \
4724+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_DEPTHSTENCILVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.vk.depth_stencil_view == 0") \
4725+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RESOLVEIMAGE, "sg_begin_pass: expected pass.swapchain.vk.resolve_image != 0") \
4726+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RESOLVEIMAGE_NOTSET, "sg_begin_pass: expected pass.swapchain.vk.resolve_image == 0") \
4727+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RESOLVEVIEW, "sg_begin_pass: expected pass.swapchain.vk.resolve_view != 0") \
4728+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RESOLVEVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.vk.resolve_view == 0") \
4729+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERFINISHEDSEMAPHORE, "sg_begin_pass: expected pass.swapchain.vk.render_finished_semaphore != 0") \
4730+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERFINISHEDSEMAPHORE_NOTSET, "sg_begin_pass: expected pass.swapchain.vk.render_finished_semaphore == 0") \
4731+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_PRESENTCOMPLETESEMAPHORE, "sg_begin_pass: expected pass.swapchain.vk.present_complete_semaphore != 0") \
4732+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_PRESENTCOMPLETESEMAPHORE_NOTSET, "sg_begin_pass: expected pass.swapchain.vk.present_complete_semaphore == 0") \
4733+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEWS_CONTINUOUS, "sg_begin_pass: color attachment view array must be continuous") \
4734+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_ALIVE, "sg_begin_pass: color attachment view no longer alive") \
4735+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_VALID, "sg_begin_pass: color attachment view not in valid state (SG_RESOURCESTATE_VALID)") \
4736+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_TYPE, "sg_begin_pass: color attachment view has wrong type (must be sg_view_desc.color_attachment)") \
4737+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_IMAGE_ALIVE, "sg_begin_pass: color attachment view's image object is uninitialized or no longer alive") \
4738+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_IMAGE_VALID, "sg_begin_pass: color attachment view's image is not in valid state (SG_RESOURCESTATE_VALID)") \
4739+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SIZES, "sg_begin_pass: all color attachments must have the same width and height") \
4740+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SAMPLECOUNT, "sg_begin_pass: when resolve attachments are provided, the color attachment sample count must be > 1") \
4741+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SAMPLECOUNTS_EQUAL, "sg_begin_pass: all color attachments must have the same sample count") \
4742+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_NO_COLORATTACHMENTVIEW, "sg_begin_pass: a resolve attachment view must have an associated color attachment view at the same index") \
4743+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_ALIVE, "sg_begin_pass: resolve attachment view no longer alive") \
4744+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_VALID, "sg_begin_pass: resolve attachment view not in valid state (SG_RESOURCESTATE_VALID)") \
4745+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_TYPE, "sg_begin_pass: resolve attachment view has wrong type (must be sg_view_desc.resolve_attachment)") \
4746+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_IMAGE_ALIVE, "sg_begin_pass: resolve attachment view's image object is uninitialized or no longer alive") \
4747+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_IMAGE_VALID, "sg_begin_pass: resolve attachment view's image is not in valid state (SG_RESOURCESTATE_VALID)") \
4748+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_SIZES, "sg_begin_pass: all attachments must have the same width and height") \
4749+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEWS_CONTINUOUS, "sg_begin_pass: color attachment view array must be continuous") \
4750+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_ALIVE, "sg_begin_pass: depth-stencil attachment view no longer alive") \
4751+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_VALID, "sg_begin_pass: depth-stencil attachment view not in valid state (SG_RESOURCESTATE_VALID)") \
4752+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_TYPE, "sg_begin_pass: depth-stencil attachment view has wrong type (must be sg_view_desc.depth_stencil_attachment)") \
4753+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_IMAGE_ALIVE, "sg_begin_pass: depth-stencil attachment view's image object is uninitialized or no longer alive") \
4754+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_IMAGE_VALID, "sg_begin_pass: depth-stencil attachment view's image is not in valid state (SG_RESOURCESTATE_VALID)") \
4755+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_SIZES, "sg_begin_pass: attachments must have the same width and height") \
4756+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_SAMPLECOUNT, "sg_begin_pass: all color attachments must have the same sample count") \
4757+ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_ATTACHMENTS_EXPECTED, "sg_begin_pass: offscreen render passes must have at least one color- or depth-stencil attachment") \
4758+ _SG_LOGITEM_XMACRO(VALIDATE_AVP_RENDERPASS_EXPECTED, "sg_apply_viewport: must be called in a render pass") \
4759+ _SG_LOGITEM_XMACRO(VALIDATE_ASR_RENDERPASS_EXPECTED, "sg_apply_scissor_rect: must be called in a render pass") \
4760+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID_ID, "sg_apply_pipeline: invalid pipeline id provided") \
4761+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_EXISTS, "sg_apply_pipeline: pipeline object no longer alive") \
4762+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID, "sg_apply_pipeline: pipeline object not in valid state (SG_RESOURCESTATE_VALID)") \
4763+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_PASS_EXPECTED, "sg_apply_pipeline: must be called in a pass") \
4764+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_SHADER_ALIVE, "sg_apply_pipeline: shader object associated with pipeline no longer alive") \
4765+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_SHADER_VALID, "sg_apply_pipeline: shader object associated with pipeline not in valid state") \
4766+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_COMPUTEPASS_EXPECTED, "sg_apply_pipeline: trying to apply compute pipeline in render pass") \
4767+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_RENDERPASS_EXPECTED, "sg_apply_pipeline: trying to apply render pipeline in compute pass") \
4768+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_SWAPCHAIN_COLOR_COUNT, "sg_apply_pipeline: the pipeline .color_count must be 1 in swapchain render passes") \
4769+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_SWAPCHAIN_COLOR_FORMAT, "sg_apply_pipeline: the pipeline .colors[0].pixel_format doesn't match the sg_pass.swapchain.color_format") \
4770+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_SWAPCHAIN_DEPTH_FORMAT, "sg_apply_pipeline: the pipeline .depth.pixel_format doesn't match the sg_pass.swapchain.depth_format") \
4771+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_SWAPCHAIN_SAMPLE_COUNT, "sg_apply_pipeline: the pipeline .sample_count doesn't match the sg_pass.swapchain.sample_count") \
4772+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_ATTACHMENTS_ALIVE, "sg_apply_pipeline: at least one pass attachment view or base image object is no longer alive") \
4773+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLORATTACHMENTS_COUNT, "sg_apply_pipeline: the pipeline .color_count doesn't match the number of render pass color attachments") \
4774+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLORATTACHMENTS_VIEW_VALID, "sg_apply_pipeline: a pass color attachment view is not in valid state (SG_RESOURCESTATE_VALID)") \
4775+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLORATTACHMENTS_IMAGE_VALID, "sg_apply_pipeline: a pass color attachment view's image object is not in valid state (SG_RESOURCESTATE_VALID)") \
4776+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLORATTACHMENTS_FORMAT, "sg_apply_pipeline: a pipeline .colors[n].pixel_format doesn't match sg_pass.attachments.colors[n] image pixel format") \
4777+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTHSTENCILATTACHMENT_VIEW_VALID, "sg_apply_pipeline: the pass depth-stencil attachment view is not in valid state (SG_RESOURCESTATE_VALID)") \
4778+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTHSTENCILATTACHMENT_IMAGE_VALID, "sg_apply_pipeline: the pass depth-stencil attachment view's image object is not in valid state (SG_RESOURCESTATE_VALID)") \
4779+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTHSTENCILATTACHMENT_FORMAT, "sg_apply_pipeline: pipeline .depth.pixel_format doesn't match sg_pass.attachments.depth_stencil image pixel format") \
4780+ _SG_LOGITEM_XMACRO(VALIDATE_APIP_ATTACHMENT_SAMPLE_COUNT, "sg_apply_pipeline: pipeline MSAA sample count doesn't match pass attachment sample count") \
4781+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_PASS_EXPECTED, "sg_apply_bindings: must be called in a pass") \
4782+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EMPTY_BINDINGS, "sg_apply_bindings: the provided sg_bindings struct is empty") \
4783+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_NO_PIPELINE, "sg_apply_bindings: must be called after sg_apply_pipeline") \
4784+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_ALIVE, "sg_apply_bindings: currently applied pipeline object no longer alive") \
4785+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_VALID, "sg_apply_bindings: currently applied pipeline object not in valid state") \
4786+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_SHADER_ALIVE, "sg_apply_bindings: shader associated with currently applied pipeline is no longer alive") \
4787+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_SHADER_VALID, "sg_apply_bindings: shader associated with currently applied pipeline is not in valid state") \
4788+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_COMPUTE_EXPECTED_NO_VBUFS, "sg_apply_bindings: vertex buffer bindings not allowed in a compute pass") \
4789+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_COMPUTE_EXPECTED_NO_IBUF, "sg_apply_bindings: index buffer binding not allowed in compute pass") \
4790+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_VBUF, "sg_apply_bindings: vertex buffer binding is missing or buffer handle is invalid") \
4791+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VBUF_ALIVE, "sg_apply_bindings: vertex buffer no longer alive") \
4792+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VBUF_USAGE, "sg_apply_bindings: buffer in vertex buffer bind slot must have usage.vertex_buffer") \
4793+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VBUF_OVERFLOW, "sg_apply_bindings: buffer in vertex buffer bind slot is overflown") \
4794+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_NO_IBUF, "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer binding provided") \
4795+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_IBUF, "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer binding provided") \
4796+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_IBUF_ALIVE, "sg_apply_bindings: index buffer no longer alive") \
4797+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_IBUF_USAGE, "sg_apply_bindings: buffer in index buffer bind slot must have usage.index_buffer") \
4798+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_IBUF_OVERFLOW, "sg_apply_bindings: buffer in index buffer slot is overflown") \
4799+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_VIEW_BINDING, "sg_apply_bindings: view binding is missing or the view handle is invalid") \
4800+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VIEW_ALIVE, "sg_apply_bindings: view no longer alive") \
4801+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECT_TEXVIEW, "sg_apply_bindings: view type mismatch in bindslot (shader expects a texture view)") \
4802+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECT_SBVIEW, "sg_apply_bindings: view type mismatch in bindslot (shader expects a storage buffer view)") \
4803+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECT_SIMGVIEW, "sg_apply_bindings: view type mismatch in bindslot (shader expects a storage image view)") \
4804+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXVIEW_IMAGETYPE_MISMATCH, "sg_apply_bindings: image type of bound texture doesn't match shader desc") \
4805+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXVIEW_EXPECTED_MULTISAMPLED_IMAGE, "sg_apply_bindings: texture bindings expects image with sample_count > 1") \
4806+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXVIEW_EXPECTED_NON_MULTISAMPLED_IMAGE, "sg_apply_bindings: texture bindings expects image with sample_count == 1") \
4807+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXVIEW_EXPECTED_FILTERABLE_IMAGE, "sg_apply_bindings: filterable image expected") \
4808+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXVIEW_EXPECTED_DEPTH_IMAGE, "sg_apply_bindings: depth image expected") \
4809+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_SBVIEW_READWRITE_IMMUTABLE, "sg_apply_bindings: storage buffers bound as read/write must have usage immutable") \
4810+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_SIMGVIEW_COMPUTE_PASS_EXPECTED, "sg_apply_bindings: storage image bindings can only appear on compute passes") \
4811+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_SIMGVIEW_IMAGETYPE_MISMATCH, "sg_apply_bindings: image type of bound storage image doesn't match shader desc") \
4812+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_SIMGVIEW_ACCESSFORMAT, "sg_apply_bindings: pixel format of storage image view doesn't match access format in shader desc") \
4813+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: sampler binding is missing or the sampler handle is invalid") \
4814+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARISON but sampler has SG_COMPAREFUNC_NEVER") \
4815+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING but sampler doesn't have SG_COMPAREFUNC_NEVER") \
4816+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING, but sampler has SG_FILTER_LINEAR filters") \
4817+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_SAMPLER_ALIVE, "sg_apply_bindings: bound sampler no longer alive") \
4818+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_SAMPLER_VALID, "sg_apply_bindings: bound sampler not in valid state") \
4819+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXTURE_BINDING_VS_DEPTHSTENCIL_ATTACHMENT, "sg_apply_bindings: cannot bind texture in the same pass it is used as depth-stencil attachment") \
4820+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXTURE_BINDING_VS_COLOR_ATTACHMENT, "sg_apply_bindings: cannot bind texture in the same pass it is used as color attachment") \
4821+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXTURE_BINDING_VS_RESOLVE_ATTACHMENT, "sg_apply_bindings: cannot bind texture in the same pass it is used as resolve attachment") \
4822+ _SG_LOGITEM_XMACRO(VALIDATE_ABND_TEXTURE_VS_STORAGEIMAGE_BINDING, "sg_apply_bindings: an image cannot be bound as a texture and storage image at the same time") \
4823+ _SG_LOGITEM_XMACRO(VALIDATE_AU_PASS_EXPECTED, "sg_apply_uniforms: must be called in a pass") \
4824+ _SG_LOGITEM_XMACRO(VALIDATE_AU_NO_PIPELINE, "sg_apply_uniforms: must be called after sg_apply_pipeline()") \
4825+ _SG_LOGITEM_XMACRO(VALIDATE_AU_PIPELINE_ALIVE, "sg_apply_uniforms: currently applied pipeline object no longer alive") \
4826+ _SG_LOGITEM_XMACRO(VALIDATE_AU_PIPELINE_VALID, "sg_apply_uniforms: currently applied pipeline object not in valid state") \
4827+ _SG_LOGITEM_XMACRO(VALIDATE_AU_PIPELINE_SHADER_ALIVE, "sg_apply_uniforms: shader associated with currently applied pipeline is no longer alive") \
4828+ _SG_LOGITEM_XMACRO(VALIDATE_AU_PIPELINE_SHADER_VALID, "sg_apply_uniforms: shader associated with currently applied pipeline is not in valid state") \
4829+ _SG_LOGITEM_XMACRO(VALIDATE_AU_NO_UNIFORMBLOCK_AT_SLOT, "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot") \
4830+ _SG_LOGITEM_XMACRO(VALIDATE_AU_SIZE, "sg_apply_uniforms: data size doesn't match declared uniform block size") \
4831+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_RENDERPASS_EXPECTED, "sg_draw: must be called in a render pass") \
4832+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_BASEELEMENT_GE_ZERO, "sg_draw: base_element cannot be < 0") \
4833+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_NUMELEMENTS_GE_ZERO, "sg_draw: num_elements cannot be < 0") \
4834+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_NUMINSTANCES_GE_ZERO, "sg_draw: num_instances cannot be < 0") \
4835+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_RENDERPASS_EXPECTED, "sg_draw: must be called in a render pass") \
4836+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEELEMENT_GE_ZERO, "sg_draw_ex: base_element cannot be < 0") \
4837+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_NUMELEMENTS_GE_ZERO, "sg_draw_ex: num_elements cannot be < 0") \
4838+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_NUMINSTANCES_GE_ZERO, "sg_draw_ex: num_instances cannot be < 0") \
4839+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEINSTANCE_GE_ZERO, "sg_draw_ex: base_instance cannot be < 0") \
4840+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEVERTEX_VS_INDEXED, "sg_draw_ex(): base_vertex must be == 0 for non-indexed rendering") \
4841+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEINSTANCE_VS_INSTANCED, "sg_draw_ex(): base_instance must be == 0 for non-instanced rendering") \
4842+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEVERTEX_NOT_SUPPORTED, "sg_draw_ex(): base_vertex != 0 not supported on this backend (sg_features.draw_base_vertex)") \
4843+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_EX_BASEINSTANCE_NOT_SUPPORTED, "sg_draw_ex(): base_instance > 0 not supported on this backend (sg_features.draw_base_instance)") \
4844+ _SG_LOGITEM_XMACRO(VALIDATE_DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING, "sg_draw: call to sg_apply_bindings() and/or sg_apply_uniforms() missing after sg_apply_pipeline()") \
4845+ _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_COMPUTEPASS_EXPECTED, "sg_dispatch: must be called in a compute pass") \
4846+ _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_NUMGROUPSX, "sg_dispatch: num_groups_x must be >=0 and <65536") \
4847+ _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_NUMGROUPSY, "sg_dispatch: num_groups_y must be >=0 and <65536") \
4848+ _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_NUMGROUPSZ, "sg_dispatch: num_groups_z must be >=0 and <65536") \
4849+ _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING, "sg_dispatch: call to sg_apply_bindings() and/or sg_apply_uniforms() missing after sg_apply_pipeline()") \
4850+ _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_USAGE, "sg_update_buffer: cannot update immutable buffer") \
4851+ _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_SIZE, "sg_update_buffer: update size is bigger than buffer size") \
4852+ _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_ONCE, "sg_update_buffer: only one update allowed per buffer and frame") \
4853+ _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_APPEND, "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame") \
4854+ _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_USAGE, "sg_append_buffer: cannot append to immutable buffer") \
4855+ _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_SIZE, "sg_append_buffer: overall appended size is bigger than buffer size") \
4856+ _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_UPDATE, "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame") \
4857+ _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_USAGE, "sg_update_image: cannot update immutable image") \
4858+ _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_ONCE, "sg_update_image: only one update allowed per image and frame") \
4859+ _SG_LOGITEM_XMACRO(VALIDATION_FAILED, "validation layer checks failed") \
4860+
4861+#define _SG_LOGITEM_XMACRO(item,msg) SG_LOGITEM_##item,
4862+typedef enum sg_log_item {
4863+ _SG_LOG_ITEMS
4864+} sg_log_item;
4865+#undef _SG_LOGITEM_XMACRO
4866+
4867+/*
4868+ sg_desc
4869+
4870+ The sg_desc struct contains configuration values for sokol_gfx,
4871+ it is used as parameter to the sg_setup() call.
4872+
4873+ The default configuration is:
4874+
4875+ .buffer_pool_size 128
4876+ .image_pool_size 128
4877+ .sampler_pool_size 64
4878+ .shader_pool_size 32
4879+ .pipeline_pool_size 64
4880+ .view_pool_size 256
4881+ .uniform_buffer_size 4 MB (4*1024*1024)
4882+ .max_commit_listeners 1024
4883+ .disable_validation false
4884+ .metal.force_managed_storage_mode false
4885+ .metal.use_command_buffer_with_retained_references false
4886+ .wgpu.disable_bindgroups_cache false
4887+ .wgpu.bindgroups_cache_size 1024
4888+ .vulkan.copy_staging_buffer_size 4 MB
4889+ .vulkan.stream_staging_buffer_size 16 MB
4890+ .vulkan.descriptor_buffer_size 16 MB
4891+
4892+ .allocator.alloc_fn 0 (in this case, malloc() will be called)
4893+ .allocator.free_fn 0 (in this case, free() will be called)
4894+ .allocator.user_data 0
4895+
4896+ .environment.defaults.color_format: default value depends on selected backend:
4897+ all GL backends: SG_PIXELFORMAT_RGBA8
4898+ Metal and D3D11: SG_PIXELFORMAT_BGRA8
4899+ WebGPU: *no default* (must be queried from WebGPU swapchain object)
4900+ .environment.defaults.depth_format: SG_PIXELFORMAT_DEPTH_STENCIL
4901+ .environment.defaults.sample_count: 1
4902+
4903+ Metal specific:
4904+ (NOTE: All Objective-C object references are transferred through
4905+ a bridged cast (__bridge const void*) to sokol_gfx, which will use an
4906+ unretained bridged cast (__bridge id<xxx>) to retrieve the Objective-C
4907+ references back. Since the bridge cast is unretained, the caller
4908+ must hold a strong reference to the Objective-C object until sg_setup()
4909+ returns.
4910+
4911+ .metal.force_managed_storage_mode
4912+ when enabled, Metal buffers and texture resources are created in managed storage
4913+ mode, otherwise sokol-gfx will decide whether to create buffers and
4914+ textures in managed or shared storage mode (this is mainly a debugging option)
4915+ .metal.use_command_buffer_with_retained_references
4916+ when true, the sokol-gfx Metal backend will use Metal command buffers which
4917+ bump the reference count of resource objects as long as they are inflight,
4918+ this is slower than the default command-buffer-with-unretained-references
4919+ method, this may be a workaround when confronted with lifetime validation
4920+ errors from the Metal validation layer until a proper fix has been implemented
4921+ .environment.metal.device
4922+ a pointer to the MTLDevice object
4923+
4924+ D3D11 specific:
4925+ .environment.d3d11.device
4926+ a pointer to the ID3D11Device object, this must have been created
4927+ before sg_setup() is called
4928+ .environment.d3d11.device_context
4929+ a pointer to the ID3D11DeviceContext object
4930+ .d3d11.shader_debugging
4931+ set this to true to compile shaders which are provided as HLSL source
4932+ code with debug information and without optimization, this allows
4933+ shader debugging in tools like RenderDoc, to output source code
4934+ instead of byte code from sokol-shdc, omit the `--binary` cmdline
4935+ option
4936+
4937+ WebGPU specific:
4938+ .wgpu.disable_bindgroups_cache
4939+ When this is true, the WebGPU backend will create and immediately
4940+ release a BindGroup object in the sg_apply_bindings() call, only
4941+ use this for debugging purposes.
4942+ .wgpu.bindgroups_cache_size
4943+ The size of the bindgroups cache for re-using BindGroup objects
4944+ between sg_apply_bindings() calls. The smaller the cache size,
4945+ the more likely are cache slot collisions which will cause
4946+ a BindGroups object to be destroyed and a new one created.
4947+ Use the information returned by sg_query_stats() to check
4948+ if this is a frequent occurrence, and increase the cache size as
4949+ needed (the default is 1024).
4950+ NOTE: wgpu_bindgroups_cache_size must be a power-of-2 number!
4951+ .environment.wgpu.device
4952+ a WGPUDevice handle
4953+
4954+ Vulkan specific:
4955+ .vulkan.copy_staging_buffer_size
4956+ Size of the staging buffer in bytes for uploading the initial
4957+ content of buffers and images, and for updating
4958+ .usage.dynamic_update resources. The default is 4 MB,
4959+ bigger resource updates are split into multiple chunks
4960+ of the staging buffer size
4961+ .vulkan.stream_staging_buffer_size
4962+ Size of the staging buffer in bytes for updating .usage.stream_update
4963+ resources. The default is 16 MB. The size must be big enough
4964+ to accomodate all update into .usage.stream_update resources.
4965+ Any additional data will cause an error log message and
4966+ incomplete rendering. Note that the actually allocated size
4967+ will be twice as much because the stream-staging-buffer is
4968+ double-buffered.
4969+ .vulkan.descriptor_buffer_size
4970+ Size of the descriptor-upload buffer in bytes. The default
4971+ size is 16 bytes. The size must be big enough to accomodate
4972+ all unifrom-block, view- and sampler-bindings in a single
4973+ frame (assume a worst-case of 256 bytes per binding). Note
4974+ that the actually allocated size will be twice as much
4975+ because the descriptor-buffer is double-buffered.
4976+
4977+ When using sokol_gfx.h and sokol_app.h together, consider using the
4978+ helper function sglue_environment() in the sokol_glue.h header to
4979+ initialize the sg_desc.environment nested struct. sglue_environment() returns
4980+ a completely initialized sg_environment struct with information
4981+ provided by sokol_app.h.
4982+*/
4983+typedef struct sg_environment_defaults {
4984+ sg_pixel_format color_format;
4985+ sg_pixel_format depth_format;
4986+ int sample_count;
4987+} sg_environment_defaults;
4988+
4989+typedef struct sg_metal_environment {
4990+ const void* device;
4991+} sg_metal_environment;
4992+
4993+typedef struct sg_d3d11_environment {
4994+ const void* device;
4995+ const void* device_context;
4996+} sg_d3d11_environment;
4997+
4998+typedef struct sg_wgpu_environment {
4999+ const void* device;
5000+} sg_wgpu_environment;
5001+
5002+typedef struct sg_vulkan_environment {
5003+ const void* instance;
5004+ const void* physical_device;
5005+ const void* device;
5006+ const void* queue;
5007+ uint32_t queue_family_index;
5008+} sg_vulkan_environment;
5009+
5010+typedef struct sg_environment {
5011+ sg_environment_defaults defaults;
5012+ sg_metal_environment metal;
5013+ sg_d3d11_environment d3d11;
5014+ sg_wgpu_environment wgpu;
5015+ sg_vulkan_environment vulkan;
5016+} sg_environment;
5017+
5018+/*
5019+ sg_commit_listener
5020+
5021+ Used with function sg_add_commit_listener() to add a callback
5022+ which will be called in sg_commit(). This is useful for libraries
5023+ building on top of sokol-gfx to be notified about when a frame
5024+ ends (instead of having to guess, or add a manual 'new-frame'
5025+ function.
5026+*/
5027+typedef struct sg_commit_listener {
5028+ void (*func)(void* user_data);
5029+ void* user_data;
5030+} sg_commit_listener;
5031+
5032+/*
5033+ sg_allocator
5034+
5035+ Used in sg_desc to provide custom memory-alloc and -free functions
5036+ to sokol_gfx.h. If memory management should be overridden, both the
5037+ alloc_fn and free_fn function must be provided (e.g. it's not valid to
5038+ override one function but not the other).
5039+*/
5040+typedef struct sg_allocator {
5041+ void* (*alloc_fn)(size_t size, void* user_data);
5042+ void (*free_fn)(void* ptr, void* user_data);
5043+ void* user_data;
5044+} sg_allocator;
5045+
5046+/*
5047+ sg_logger
5048+
5049+ Used in sg_desc to provide a logging function. Please be aware
5050+ that without logging function, sokol-gfx will be completely
5051+ silent, e.g. it will not report errors, warnings and
5052+ validation layer messages. For maximum error verbosity,
5053+ compile in debug mode (e.g. NDEBUG *not* defined) and provide a
5054+ compatible logger function in the sg_setup() call
5055+ (for instance the standard logging function from sokol_log.h).
5056+*/
5057+typedef struct sg_logger {
5058+ void (*func)(
5059+ const char* tag, // always "sg"
5060+ uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info
5061+ uint32_t log_item_id, // SG_LOGITEM_*
5062+ const char* message_or_null, // a message string, may be nullptr in release mode
5063+ uint32_t line_nr, // line number in sokol_gfx.h
5064+ const char* filename_or_null, // source filename, may be nullptr in release mode
5065+ void* user_data);
5066+ void* user_data;
5067+} sg_logger;
5068+
5069+typedef struct sg_d3d11_desc {
5070+ bool shader_debugging; // if true, HLSL shaders are compiled with D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION
5071+} sg_d3d11_desc;
5072+
5073+typedef struct sg_metal_desc {
5074+ bool force_managed_storage_mode; // for debugging: use Metal managed storage mode for resources even with UMA
5075+ bool use_command_buffer_with_retained_references; // Metal: use a managed MTLCommandBuffer which ref-counts used resources
5076+} sg_metal_desc;
5077+
5078+typedef struct sg_wgpu_desc {
5079+ bool disable_bindgroups_cache; // set to true to disable the WebGPU backend BindGroup cache
5080+ int bindgroups_cache_size; // number of slots in the WebGPU bindgroup cache (must be 2^N)
5081+} sg_wgpu_desc;
5082+
5083+typedef struct sg_vulkan_desc {
5084+ int copy_staging_buffer_size; // size of staging buffer for immutable and dynamic resources (default: 4 MB)
5085+ int stream_staging_buffer_size; // size of per-frame staging buffer for updating streaming resources (default: 16 MB)
5086+ int descriptor_buffer_size; // size of per-frame descriptor buffer for updating resource bindings (default: 16 MB)
5087+} sg_vulkan_desc;
5088+
5089+typedef struct sg_desc {
5090+ uint32_t _start_canary;
5091+ int buffer_pool_size;
5092+ int image_pool_size;
5093+ int sampler_pool_size;
5094+ int shader_pool_size;
5095+ int pipeline_pool_size;
5096+ int view_pool_size;
5097+ int uniform_buffer_size; // max size of all sg_apply_uniform() calls per frame, with worst-case 256 byte alignment
5098+ int max_commit_listeners; // max number of commit listener hook functions
5099+ bool disable_validation; // disable validation layer even in debug mode, useful for tests
5100+ bool enforce_portable_limits; // if true, enforce portable resource binding limits (SG_MAX_PORTABLE_*)
5101+ sg_d3d11_desc d3d11; // d3d11-specific setup parameters
5102+ sg_metal_desc metal; // metal-specific setup parameters
5103+ sg_wgpu_desc wgpu; // webgpu-specific setup parameters
5104+ sg_vulkan_desc vulkan; // vulkan-specific setup parameters
5105+ sg_allocator allocator; // optional memory allocation hooks
5106+ sg_logger logger; // optional log function override
5107+ sg_environment environment; // required externally provided runtime objects and defaults
5108+ uint32_t _end_canary;
5109+} sg_desc;
5110+
5111+// setup and misc functions
5112+SOKOL_GFX_API_DECL void sg_setup(const sg_desc* desc);
5113+SOKOL_GFX_API_DECL void sg_shutdown(void);
5114+SOKOL_GFX_API_DECL bool sg_isvalid(void);
5115+SOKOL_GFX_API_DECL void sg_reset_state_cache(void);
5116+SOKOL_GFX_API_DECL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks);
5117+SOKOL_GFX_API_DECL void sg_push_debug_group(const char* name);
5118+SOKOL_GFX_API_DECL void sg_pop_debug_group(void);
5119+SOKOL_GFX_API_DECL bool sg_add_commit_listener(sg_commit_listener listener);
5120+SOKOL_GFX_API_DECL bool sg_remove_commit_listener(sg_commit_listener listener);
5121+
5122+// resource creation, destruction and updating
5123+SOKOL_GFX_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc);
5124+SOKOL_GFX_API_DECL sg_image sg_make_image(const sg_image_desc* desc);
5125+SOKOL_GFX_API_DECL sg_sampler sg_make_sampler(const sg_sampler_desc* desc);
5126+SOKOL_GFX_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc);
5127+SOKOL_GFX_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc);
5128+SOKOL_GFX_API_DECL sg_view sg_make_view(const sg_view_desc* desc);
5129+SOKOL_GFX_API_DECL void sg_destroy_buffer(sg_buffer buf);
5130+SOKOL_GFX_API_DECL void sg_destroy_image(sg_image img);
5131+SOKOL_GFX_API_DECL void sg_destroy_sampler(sg_sampler smp);
5132+SOKOL_GFX_API_DECL void sg_destroy_shader(sg_shader shd);
5133+SOKOL_GFX_API_DECL void sg_destroy_pipeline(sg_pipeline pip);
5134+SOKOL_GFX_API_DECL void sg_destroy_view(sg_view view);
5135+SOKOL_GFX_API_DECL void sg_update_buffer(sg_buffer buf, const sg_range* data);
5136+SOKOL_GFX_API_DECL void sg_update_image(sg_image img, const sg_image_data* data);
5137+SOKOL_GFX_API_DECL int sg_append_buffer(sg_buffer buf, const sg_range* data);
5138+SOKOL_GFX_API_DECL bool sg_query_buffer_overflow(sg_buffer buf);
5139+SOKOL_GFX_API_DECL bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size);
5140+
5141+// render and compute functions
5142+SOKOL_GFX_API_DECL void sg_begin_pass(const sg_pass* pass);
5143+SOKOL_GFX_API_DECL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left);
5144+SOKOL_GFX_API_DECL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left);
5145+SOKOL_GFX_API_DECL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left);
5146+SOKOL_GFX_API_DECL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left);
5147+SOKOL_GFX_API_DECL void sg_apply_pipeline(sg_pipeline pip);
5148+SOKOL_GFX_API_DECL void sg_apply_bindings(const sg_bindings* bindings);
5149+SOKOL_GFX_API_DECL void sg_apply_uniforms(int ub_slot, const sg_range* data);
5150+SOKOL_GFX_API_DECL void sg_draw(int base_element, int num_elements, int num_instances);
5151+SOKOL_GFX_API_DECL void sg_draw_ex(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance);
5152+SOKOL_GFX_API_DECL void sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z);
5153+SOKOL_GFX_API_DECL void sg_end_pass(void);
5154+SOKOL_GFX_API_DECL void sg_commit(void);
5155+
5156+// getting information
5157+SOKOL_GFX_API_DECL sg_desc sg_query_desc(void);
5158+SOKOL_GFX_API_DECL sg_backend sg_query_backend(void);
5159+SOKOL_GFX_API_DECL sg_features sg_query_features(void);
5160+SOKOL_GFX_API_DECL sg_limits sg_query_limits(void);
5161+SOKOL_GFX_API_DECL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt);
5162+SOKOL_GFX_API_DECL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes);
5163+SOKOL_GFX_API_DECL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes);
5164+// get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID)
5165+SOKOL_GFX_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf);
5166+SOKOL_GFX_API_DECL sg_resource_state sg_query_image_state(sg_image img);
5167+SOKOL_GFX_API_DECL sg_resource_state sg_query_sampler_state(sg_sampler smp);
5168+SOKOL_GFX_API_DECL sg_resource_state sg_query_shader_state(sg_shader shd);
5169+SOKOL_GFX_API_DECL sg_resource_state sg_query_pipeline_state(sg_pipeline pip);
5170+SOKOL_GFX_API_DECL sg_resource_state sg_query_view_state(sg_view view);
5171+// get runtime information about a resource
5172+SOKOL_GFX_API_DECL sg_buffer_info sg_query_buffer_info(sg_buffer buf);
5173+SOKOL_GFX_API_DECL sg_image_info sg_query_image_info(sg_image img);
5174+SOKOL_GFX_API_DECL sg_sampler_info sg_query_sampler_info(sg_sampler smp);
5175+SOKOL_GFX_API_DECL sg_shader_info sg_query_shader_info(sg_shader shd);
5176+SOKOL_GFX_API_DECL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip);
5177+SOKOL_GFX_API_DECL sg_view_info sg_query_view_info(sg_view view);
5178+// get desc structs matching a specific resource (NOTE that not all creation attributes may be provided)
5179+SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf);
5180+SOKOL_GFX_API_DECL sg_image_desc sg_query_image_desc(sg_image img);
5181+SOKOL_GFX_API_DECL sg_sampler_desc sg_query_sampler_desc(sg_sampler smp);
5182+SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_desc(sg_shader shd);
5183+SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip);
5184+SOKOL_GFX_API_DECL sg_view_desc sg_query_view_desc(sg_view view);
5185+// get resource creation desc struct with their default values replaced
5186+SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc);
5187+SOKOL_GFX_API_DECL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc);
5188+SOKOL_GFX_API_DECL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc);
5189+SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc);
5190+SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc);
5191+SOKOL_GFX_API_DECL sg_view_desc sg_query_view_defaults(const sg_view_desc* desc);
5192+// assorted query functions
5193+SOKOL_GFX_API_DECL size_t sg_query_buffer_size(sg_buffer buf);
5194+SOKOL_GFX_API_DECL sg_buffer_usage sg_query_buffer_usage(sg_buffer buf);
5195+SOKOL_GFX_API_DECL sg_image_type sg_query_image_type(sg_image img);
5196+SOKOL_GFX_API_DECL int sg_query_image_width(sg_image img);
5197+SOKOL_GFX_API_DECL int sg_query_image_height(sg_image img);
5198+SOKOL_GFX_API_DECL int sg_query_image_num_slices(sg_image img);
5199+SOKOL_GFX_API_DECL int sg_query_image_num_mipmaps(sg_image img);
5200+SOKOL_GFX_API_DECL sg_pixel_format sg_query_image_pixelformat(sg_image img);
5201+SOKOL_GFX_API_DECL sg_image_usage sg_query_image_usage(sg_image img);
5202+SOKOL_GFX_API_DECL int sg_query_image_sample_count(sg_image img);
5203+SOKOL_GFX_API_DECL sg_view_type sg_query_view_type(sg_view view);
5204+SOKOL_GFX_API_DECL sg_image sg_query_view_image(sg_view view);
5205+SOKOL_GFX_API_DECL sg_buffer sg_query_view_buffer(sg_view view);
5206+
5207+// separate resource allocation and initialization (for async setup)
5208+SOKOL_GFX_API_DECL sg_buffer sg_alloc_buffer(void);
5209+SOKOL_GFX_API_DECL sg_image sg_alloc_image(void);
5210+SOKOL_GFX_API_DECL sg_sampler sg_alloc_sampler(void);
5211+SOKOL_GFX_API_DECL sg_shader sg_alloc_shader(void);
5212+SOKOL_GFX_API_DECL sg_pipeline sg_alloc_pipeline(void);
5213+SOKOL_GFX_API_DECL sg_view sg_alloc_view(void);
5214+SOKOL_GFX_API_DECL void sg_dealloc_buffer(sg_buffer buf);
5215+SOKOL_GFX_API_DECL void sg_dealloc_image(sg_image img);
5216+SOKOL_GFX_API_DECL void sg_dealloc_sampler(sg_sampler smp);
5217+SOKOL_GFX_API_DECL void sg_dealloc_shader(sg_shader shd);
5218+SOKOL_GFX_API_DECL void sg_dealloc_pipeline(sg_pipeline pip);
5219+SOKOL_GFX_API_DECL void sg_dealloc_view(sg_view view);
5220+SOKOL_GFX_API_DECL void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc);
5221+SOKOL_GFX_API_DECL void sg_init_image(sg_image img, const sg_image_desc* desc);
5222+SOKOL_GFX_API_DECL void sg_init_sampler(sg_sampler smg, const sg_sampler_desc* desc);
5223+SOKOL_GFX_API_DECL void sg_init_shader(sg_shader shd, const sg_shader_desc* desc);
5224+SOKOL_GFX_API_DECL void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc);
5225+SOKOL_GFX_API_DECL void sg_init_view(sg_view view, const sg_view_desc* desc);
5226+SOKOL_GFX_API_DECL void sg_uninit_buffer(sg_buffer buf);
5227+SOKOL_GFX_API_DECL void sg_uninit_image(sg_image img);
5228+SOKOL_GFX_API_DECL void sg_uninit_sampler(sg_sampler smp);
5229+SOKOL_GFX_API_DECL void sg_uninit_shader(sg_shader shd);
5230+SOKOL_GFX_API_DECL void sg_uninit_pipeline(sg_pipeline pip);
5231+SOKOL_GFX_API_DECL void sg_uninit_view(sg_view view);
5232+SOKOL_GFX_API_DECL void sg_fail_buffer(sg_buffer buf);
5233+SOKOL_GFX_API_DECL void sg_fail_image(sg_image img);
5234+SOKOL_GFX_API_DECL void sg_fail_sampler(sg_sampler smp);
5235+SOKOL_GFX_API_DECL void sg_fail_shader(sg_shader shd);
5236+SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip);
5237+SOKOL_GFX_API_DECL void sg_fail_view(sg_view view);
5238+
5239+// frame and total stats
5240+SOKOL_GFX_API_DECL void sg_enable_stats(void);
5241+SOKOL_GFX_API_DECL void sg_disable_stats(void);
5242+SOKOL_GFX_API_DECL bool sg_stats_enabled(void);
5243+SOKOL_GFX_API_DECL sg_stats sg_query_stats(void);
5244+
5245+/* Backend-specific structs and functions, these may come in handy for mixing
5246+ sokol-gfx rendering with 'native backend' rendering functions.
5247+
5248+ This group of functions will be expanded as needed.
5249+*/
5250+
5251+typedef struct sg_d3d11_buffer_info {
5252+ const void* buf; // ID3D11Buffer*
5253+} sg_d3d11_buffer_info;
5254+
5255+typedef struct sg_d3d11_image_info {
5256+ const void* tex2d; // ID3D11Texture2D*
5257+ const void* tex3d; // ID3D11Texture3D*
5258+ const void* res; // ID3D11Resource* (either tex2d or tex3d)
5259+} sg_d3d11_image_info;
5260+
5261+typedef struct sg_d3d11_sampler_info {
5262+ const void* smp; // ID3D11SamplerState*
5263+} sg_d3d11_sampler_info;
5264+
5265+typedef struct sg_d3d11_shader_info {
5266+ const void* cbufs[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; // ID3D11Buffer* (constant buffers by bind slot)
5267+ const void* vs; // ID3D11VertexShader*
5268+ const void* fs; // ID3D11PixelShader*
5269+} sg_d3d11_shader_info;
5270+
5271+typedef struct sg_d3d11_pipeline_info {
5272+ const void* il; // ID3D11InputLayout*
5273+ const void* rs; // ID3D11RasterizerState*
5274+ const void* dss; // ID3D11DepthStencilState*
5275+ const void* bs; // ID3D11BlendState*
5276+} sg_d3d11_pipeline_info;
5277+
5278+typedef struct sg_d3d11_view_info {
5279+ const void* srv; // ID3D11ShaderResourceView
5280+ const void* uav; // ID3D11UnorderedAccessView
5281+ const void* rtv; // ID3D11RenderTargetView
5282+ const void* dsv; // ID3D11DepthStencilView
5283+} sg_d3d11_view_info;
5284+
5285+typedef struct sg_mtl_buffer_info {
5286+ const void* buf[SG_NUM_INFLIGHT_FRAMES]; // id<MTLBuffer>
5287+ int active_slot;
5288+} sg_mtl_buffer_info;
5289+
5290+typedef struct sg_mtl_image_info {
5291+ const void* tex[SG_NUM_INFLIGHT_FRAMES]; // id<MTLTexture>
5292+ int active_slot;
5293+} sg_mtl_image_info;
5294+
5295+typedef struct sg_mtl_sampler_info {
5296+ const void* smp; // id<MTLSamplerState>
5297+} sg_mtl_sampler_info;
5298+
5299+typedef struct sg_mtl_shader_info {
5300+ const void* vertex_lib; // id<MTLLibrary>
5301+ const void* fragment_lib; // id<MTLLibrary>
5302+ const void* vertex_func; // id<MTLFunction>
5303+ const void* fragment_func; // id<MTLFunction>
5304+} sg_mtl_shader_info;
5305+
5306+typedef struct sg_mtl_pipeline_info {
5307+ const void* rps; // id<MTLRenderPipelineState>
5308+ const void* dss; // id<MTLDepthStencilState>
5309+} sg_mtl_pipeline_info;
5310+
5311+typedef struct sg_wgpu_buffer_info {
5312+ const void* buf; // WGPUBuffer
5313+} sg_wgpu_buffer_info;
5314+
5315+typedef struct sg_wgpu_image_info {
5316+ const void* tex; // WGPUTexture
5317+} sg_wgpu_image_info;
5318+
5319+typedef struct sg_wgpu_sampler_info {
5320+ const void* smp; // WGPUSampler
5321+} sg_wgpu_sampler_info;
5322+
5323+typedef struct sg_wgpu_shader_info {
5324+ const void* vs_mod; // WGPUShaderModule
5325+ const void* fs_mod; // WGPUShaderModule
5326+ const void* bgl; // WGPUBindGroupLayout;
5327+} sg_wgpu_shader_info;
5328+
5329+typedef struct sg_wgpu_pipeline_info {
5330+ const void* render_pipeline; // WGPURenderPipeline
5331+ const void* compute_pipeline; // WGPUComputePipeline
5332+} sg_wgpu_pipeline_info;
5333+
5334+typedef struct sg_wgpu_view_info {
5335+ const void* view; // WGPUTextureView
5336+} sg_wgpu_view_info;
5337+
5338+typedef struct sg_gl_buffer_info {
5339+ uint32_t buf[SG_NUM_INFLIGHT_FRAMES];
5340+ int active_slot;
5341+} sg_gl_buffer_info;
5342+
5343+typedef struct sg_gl_image_info {
5344+ uint32_t tex[SG_NUM_INFLIGHT_FRAMES];
5345+ uint32_t tex_target;
5346+ int active_slot;
5347+} sg_gl_image_info;
5348+
5349+typedef struct sg_gl_sampler_info {
5350+ uint32_t smp;
5351+} sg_gl_sampler_info;
5352+
5353+typedef struct sg_gl_shader_info {
5354+ uint32_t prog;
5355+} sg_gl_shader_info;
5356+
5357+typedef struct sg_gl_view_info {
5358+ uint32_t tex_view[SG_NUM_INFLIGHT_FRAMES];
5359+ uint32_t msaa_render_buffer;
5360+ uint32_t msaa_resolve_frame_buffer;
5361+} sg_gl_view_info;
5362+
5363+// D3D11: return ID3D11Device
5364+SOKOL_GFX_API_DECL const void* sg_d3d11_device(void);
5365+// D3D11: return ID3D11DeviceContext
5366+SOKOL_GFX_API_DECL const void* sg_d3d11_device_context(void);
5367+// D3D11: get internal buffer resource objects
5368+SOKOL_GFX_API_DECL sg_d3d11_buffer_info sg_d3d11_query_buffer_info(sg_buffer buf);
5369+// D3D11: get internal image resource objects
5370+SOKOL_GFX_API_DECL sg_d3d11_image_info sg_d3d11_query_image_info(sg_image img);
5371+// D3D11: get internal sampler resource objects
5372+SOKOL_GFX_API_DECL sg_d3d11_sampler_info sg_d3d11_query_sampler_info(sg_sampler smp);
5373+// D3D11: get internal shader resource objects
5374+SOKOL_GFX_API_DECL sg_d3d11_shader_info sg_d3d11_query_shader_info(sg_shader shd);
5375+// D3D11: get internal pipeline resource objects
5376+SOKOL_GFX_API_DECL sg_d3d11_pipeline_info sg_d3d11_query_pipeline_info(sg_pipeline pip);
5377+// D3D11: get internal view resource objects
5378+SOKOL_GFX_API_DECL sg_d3d11_view_info sg_d3d11_query_view_info(sg_view view);
5379+
5380+// Metal: return __bridge-casted MTLDevice
5381+SOKOL_GFX_API_DECL const void* sg_mtl_device(void);
5382+// Metal: return __bridge-casted MTLRenderCommandEncoder when inside render pass (otherwise zero)
5383+SOKOL_GFX_API_DECL const void* sg_mtl_render_command_encoder(void);
5384+// Metal: return __bridge-casted MTLComputeCommandEncoder when inside compute pass (otherwise zero)
5385+SOKOL_GFX_API_DECL const void* sg_mtl_compute_command_encoder(void);
5386+// Metal: return __bridge-casted MTLCommandQueue
5387+SOKOL_GFX_API_DECL const void* sg_mtl_command_queue(void);
5388+// Metal: get internal __bridge-casted buffer resource objects
5389+SOKOL_GFX_API_DECL sg_mtl_buffer_info sg_mtl_query_buffer_info(sg_buffer buf);
5390+// Metal: get internal __bridge-casted image resource objects
5391+SOKOL_GFX_API_DECL sg_mtl_image_info sg_mtl_query_image_info(sg_image img);
5392+// Metal: get internal __bridge-casted sampler resource objects
5393+SOKOL_GFX_API_DECL sg_mtl_sampler_info sg_mtl_query_sampler_info(sg_sampler smp);
5394+// Metal: get internal __bridge-casted shader resource objects
5395+SOKOL_GFX_API_DECL sg_mtl_shader_info sg_mtl_query_shader_info(sg_shader shd);
5396+// Metal: get internal __bridge-casted pipeline resource objects
5397+SOKOL_GFX_API_DECL sg_mtl_pipeline_info sg_mtl_query_pipeline_info(sg_pipeline pip);
5398+
5399+// WebGPU: return WGPUDevice object
5400+SOKOL_GFX_API_DECL const void* sg_wgpu_device(void);
5401+// WebGPU: return WGPUQueue object
5402+SOKOL_GFX_API_DECL const void* sg_wgpu_queue(void);
5403+// WebGPU: return this frame's WGPUCommandEncoder
5404+SOKOL_GFX_API_DECL const void* sg_wgpu_command_encoder(void);
5405+// WebGPU: return WGPURenderPassEncoder of current pass (returns 0 when outside pass or in a compute pass)
5406+SOKOL_GFX_API_DECL const void* sg_wgpu_render_pass_encoder(void);
5407+// WebGPU: return WGPUComputePassEncoder of current pass (returns 0 when outside pass or in a render pass)
5408+SOKOL_GFX_API_DECL const void* sg_wgpu_compute_pass_encoder(void);
5409+// WebGPU: get internal buffer resource objects
5410+SOKOL_GFX_API_DECL sg_wgpu_buffer_info sg_wgpu_query_buffer_info(sg_buffer buf);
5411+// WebGPU: get internal image resource objects
5412+SOKOL_GFX_API_DECL sg_wgpu_image_info sg_wgpu_query_image_info(sg_image img);
5413+// WebGPU: get internal sampler resource objects
5414+SOKOL_GFX_API_DECL sg_wgpu_sampler_info sg_wgpu_query_sampler_info(sg_sampler smp);
5415+// WebGPU: get internal shader resource objects
5416+SOKOL_GFX_API_DECL sg_wgpu_shader_info sg_wgpu_query_shader_info(sg_shader shd);
5417+// WebGPU: get internal pipeline resource objects
5418+SOKOL_GFX_API_DECL sg_wgpu_pipeline_info sg_wgpu_query_pipeline_info(sg_pipeline pip);
5419+// WebGPU: get internal view resource objects
5420+SOKOL_GFX_API_DECL sg_wgpu_view_info sg_wgpu_query_view_info(sg_view view);
5421+
5422+// GL: get internal buffer resource objects
5423+SOKOL_GFX_API_DECL sg_gl_buffer_info sg_gl_query_buffer_info(sg_buffer buf);
5424+// GL: get internal image resource objects
5425+SOKOL_GFX_API_DECL sg_gl_image_info sg_gl_query_image_info(sg_image img);
5426+// GL: get internal sampler resource objects
5427+SOKOL_GFX_API_DECL sg_gl_sampler_info sg_gl_query_sampler_info(sg_sampler smp);
5428+// GL: get internal shader resource objects
5429+SOKOL_GFX_API_DECL sg_gl_shader_info sg_gl_query_shader_info(sg_shader shd);
5430+// GL: get internal view resource objects
5431+SOKOL_GFX_API_DECL sg_gl_view_info sg_gl_query_view_info(sg_view view);
5432+
5433+#ifdef __cplusplus
5434+} // extern "C"
5435+
5436+// reference-based equivalents for c++
5437+inline void sg_setup(const sg_desc& desc) { return sg_setup(&desc); }
5438+
5439+inline sg_buffer sg_make_buffer(const sg_buffer_desc& desc) { return sg_make_buffer(&desc); }
5440+inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image(&desc); }
5441+inline sg_sampler sg_make_sampler(const sg_sampler_desc& desc) { return sg_make_sampler(&desc); }
5442+inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); }
5443+inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); }
5444+inline sg_view sg_make_view(const sg_view_desc& desc) { return sg_make_view(&desc); }
5445+inline void sg_update_image(sg_image img, const sg_image_data& data) { return sg_update_image(img, &data); }
5446+
5447+inline void sg_begin_pass(const sg_pass& pass) { return sg_begin_pass(&pass); }
5448+inline void sg_apply_bindings(const sg_bindings& bindings) { return sg_apply_bindings(&bindings); }
5449+inline void sg_apply_uniforms(int ub_slot, const sg_range& data) { return sg_apply_uniforms(ub_slot, &data); }
5450+
5451+inline sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc& desc) { return sg_query_buffer_defaults(&desc); }
5452+inline sg_image_desc sg_query_image_defaults(const sg_image_desc& desc) { return sg_query_image_defaults(&desc); }
5453+inline sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc& desc) { return sg_query_sampler_defaults(&desc); }
5454+inline sg_shader_desc sg_query_shader_defaults(const sg_shader_desc& desc) { return sg_query_shader_defaults(&desc); }
5455+inline sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc& desc) { return sg_query_pipeline_defaults(&desc); }
5456+inline sg_view_desc sg_query_view_defaults(const sg_view_desc& desc) { return sg_query_view_defaults(&desc); }
5457+
5458+inline void sg_init_buffer(sg_buffer buf, const sg_buffer_desc& desc) { return sg_init_buffer(buf, &desc); }
5459+inline void sg_init_image(sg_image img, const sg_image_desc& desc) { return sg_init_image(img, &desc); }
5460+inline void sg_init_sampler(sg_sampler smp, const sg_sampler_desc& desc) { return sg_init_sampler(smp, &desc); }
5461+inline void sg_init_shader(sg_shader shd, const sg_shader_desc& desc) { return sg_init_shader(shd, &desc); }
5462+inline void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc& desc) { return sg_init_pipeline(pip, &desc); }
5463+inline void sg_init_view(sg_view view, const sg_view_desc& desc) { return sg_init_view(view, &desc); }
5464+
5465+inline void sg_update_buffer(sg_buffer buf_id, const sg_range& data) { return sg_update_buffer(buf_id, &data); }
5466+inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_append_buffer(buf_id, &data); }
5467+#endif
5468+#endif // SOKOL_GFX_INCLUDED
5469+
5470+// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██
5471+// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
5472+// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██
5473+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
5474+// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████
5475+//
5476+// >>implementation
5477+#ifdef SOKOL_GFX_IMPL
5478+#define SOKOL_GFX_IMPL_INCLUDED (1)
5479+
5480+#if !(defined(SOKOL_GLCORE)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_VULKAN)||defined(SOKOL_DUMMY_BACKEND))
5481+#error "Please select a backend with SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU, SOKOL_VULKAN or SOKOL_DUMMY_BACKEND"
5482+#endif
5483+#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE)
5484+#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sg_desc.allocator to override memory allocation functions"
5485+#endif
5486+
5487+#include <stdlib.h> // malloc, free, qsort
5488+#include <string.h> // memset
5489+#include <float.h> // FLT_MAX
5490+
5491+#ifndef SOKOL_API_IMPL
5492+ #define SOKOL_API_IMPL
5493+#endif
5494+#ifndef SOKOL_DEBUG
5495+ #ifndef NDEBUG
5496+ #define SOKOL_DEBUG
5497+ #endif
5498+#endif
5499+#ifndef SOKOL_ASSERT
5500+ #include <assert.h>
5501+ #define SOKOL_ASSERT(c) assert(c)
5502+#endif
5503+#ifndef SOKOL_UNREACHABLE
5504+ #define SOKOL_UNREACHABLE SOKOL_ASSERT(false)
5505+#endif
5506+
5507+#ifndef _SOKOL_PRIVATE
5508+ #if defined(__GNUC__) || defined(__clang__)
5509+ #define _SOKOL_PRIVATE __attribute__((unused)) static
5510+ #else
5511+ #define _SOKOL_PRIVATE static
5512+ #endif
5513+#endif
5514+
5515+#ifndef _SOKOL_UNUSED
5516+ #define _SOKOL_UNUSED(x) (void)(x)
5517+#endif
5518+
5519+#if defined(SOKOL_TRACE_HOOKS)
5520+#define _SG_TRACE_ARGS(fn, ...) if (_sg.hooks.fn) { _sg.hooks.fn(__VA_ARGS__, _sg.hooks.user_data); }
5521+#define _SG_TRACE_NOARGS(fn) if (_sg.hooks.fn) { _sg.hooks.fn(_sg.hooks.user_data); }
5522+#else
5523+#define _SG_TRACE_ARGS(fn, ...)
5524+#define _SG_TRACE_NOARGS(fn)
5525+#endif
5526+
5527+#ifdef __cplusplus
5528+#define _SG_STRUCT(TYPE, NAME) TYPE NAME = {}
5529+#else
5530+#define _SG_STRUCT(TYPE, NAME) TYPE NAME = {0}
5531+#endif
5532+
5533+// default clear values
5534+#ifndef SG_DEFAULT_CLEAR_RED
5535+#define SG_DEFAULT_CLEAR_RED (0.5f)
5536+#endif
5537+#ifndef SG_DEFAULT_CLEAR_GREEN
5538+#define SG_DEFAULT_CLEAR_GREEN (0.5f)
5539+#endif
5540+#ifndef SG_DEFAULT_CLEAR_BLUE
5541+#define SG_DEFAULT_CLEAR_BLUE (0.5f)
5542+#endif
5543+#ifndef SG_DEFAULT_CLEAR_ALPHA
5544+#define SG_DEFAULT_CLEAR_ALPHA (1.0f)
5545+#endif
5546+#ifndef SG_DEFAULT_CLEAR_DEPTH
5547+#define SG_DEFAULT_CLEAR_DEPTH (1.0f)
5548+#endif
5549+#ifndef SG_DEFAULT_CLEAR_STENCIL
5550+#define SG_DEFAULT_CLEAR_STENCIL (0)
5551+#endif
5552+
5553+#ifdef _MSC_VER
5554+#pragma warning(push)
5555+#pragma warning(disable:4115) // named type definition in parentheses
5556+#pragma warning(disable:4505) // unreferenced local function has been removed
5557+#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union (needed by d3d11.h)
5558+#pragma warning(disable:4054) // 'type cast': from function pointer
5559+#pragma warning(disable:4055) // 'type cast': from data pointer
5560+#endif
5561+
5562+#if defined(SOKOL_D3D11)
5563+ #if defined(__GNUC__)
5564+ #pragma GCC diagnostic push
5565+ #pragma GCC diagnostic ignored "-Wunknown-pragmas"
5566+ #endif
5567+ #ifndef D3D11_NO_HELPERS
5568+ #define D3D11_NO_HELPERS
5569+ #endif
5570+ #ifndef WIN32_LEAN_AND_MEAN
5571+ #define WIN32_LEAN_AND_MEAN
5572+ #endif
5573+ #ifndef NOMINMAX
5574+ #define NOMINMAX
5575+ #endif
5576+ #include <d3d11.h>
5577+ #include <d3dcompiler.h>
5578+ #pragma comment (lib, "kernel32")
5579+ #pragma comment (lib, "user32")
5580+ #pragma comment (lib, "dxgi")
5581+ #pragma comment (lib, "d3d11")
5582+ #if defined(__GNUC__)
5583+ #pragma GCC diagnostic pop
5584+ #endif
5585+#elif defined(SOKOL_METAL)
5586+ // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting
5587+ #if !defined(__cplusplus)
5588+ #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields)
5589+ #error "sokol_gfx.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)"
5590+ #endif
5591+ #endif
5592+ #include <TargetConditionals.h>
5593+ #include <AvailabilityMacros.h>
5594+ #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
5595+ #define _SG_TARGET_MACOS (1)
5596+ #else
5597+ #define _SG_TARGET_IOS (1)
5598+ #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR
5599+ #define _SG_TARGET_IOS_SIMULATOR (1)
5600+ #endif
5601+ #endif
5602+ #import <Metal/Metal.h>
5603+ #import <QuartzCore/CoreAnimation.h> // needed for CAMetalDrawable
5604+#elif defined(SOKOL_WGPU)
5605+ #include <webgpu/webgpu.h>
5606+ #if defined(__EMSCRIPTEN__)
5607+ #include <emscripten/emscripten.h>
5608+ #endif
5609+#elif defined(SOKOL_VULKAN)
5610+ #include <vulkan/vulkan.h>
5611+#elif defined(SOKOL_GLCORE) || defined(SOKOL_GLES3)
5612+ #define _SOKOL_ANY_GL (1)
5613+
5614+ // include platform specific GL headers (or on Win32: use an embedded GL loader)
5615+ #if !defined(SOKOL_EXTERNAL_GL_LOADER)
5616+ #if defined(_WIN32)
5617+ #if defined(SOKOL_GLCORE)
5618+ #define _SOKOL_USE_WIN32_GL_LOADER (1)
5619+ #ifndef WIN32_LEAN_AND_MEAN
5620+ #define WIN32_LEAN_AND_MEAN
5621+ #endif
5622+ #ifndef NOMINMAX
5623+ #define NOMINMAX
5624+ #endif
5625+ #include <windows.h>
5626+ #pragma comment (lib, "kernel32") // GetProcAddress()
5627+ #endif
5628+ #elif defined(__APPLE__)
5629+ #include <TargetConditionals.h>
5630+ #ifndef GL_SILENCE_DEPRECATION
5631+ #define GL_SILENCE_DEPRECATION
5632+ #endif
5633+ #ifndef GLES_SILENCE_DEPRECATION
5634+ #define GLES_SILENCE_DEPRECATION
5635+ #endif
5636+ #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
5637+ #if defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST
5638+ #include <OpenGL/gl3.h>
5639+ #else
5640+ #include <OpenGLES/ES3/gl.h>
5641+ #include <OpenGLES/ES3/glext.h>
5642+ #endif
5643+ #else
5644+ #include <OpenGL/gl3.h>
5645+ #endif
5646+ #elif defined(__EMSCRIPTEN__)
5647+ #if defined(SOKOL_GLES3)
5648+ #include <GLES3/gl3.h>
5649+ #endif
5650+ #elif defined(__ANDROID__)
5651+ #if __ANDROID_API__ >= 24
5652+ #include <GLES3/gl32.h>
5653+ #else
5654+ #include <GLES3/gl31.h>
5655+ #endif
5656+ #elif defined(__linux__) || defined(__unix__)
5657+ #if defined(SOKOL_GLCORE)
5658+ #define GL_GLEXT_PROTOTYPES
5659+ #include <GL/gl.h>
5660+ #else
5661+ #include <GLES3/gl32.h>
5662+ #include <GLES3/gl3ext.h>
5663+ #endif
5664+ #endif
5665+ #endif
5666+
5667+ // broad GL feature availability defines (DON'T merge this into the above ifdef-block!)
5668+ #if defined(_WIN32)
5669+ #define _SOKOL_GL_HAS_COLORMASKI (1)
5670+ #if defined(GL_VERSION_4_3) || defined(_SOKOL_USE_WIN32_GL_LOADER)
5671+ #define _SOKOL_GL_HAS_COMPUTE (1)
5672+ #define _SOKOL_GL_HAS_TEXVIEWS (1)
5673+ #endif
5674+ #if defined(GL_VERSION_4_2) || defined(_SOKOL_USE_WIN32_GL_LOADER)
5675+ #define _SOKOL_GL_HAS_TEXSTORAGE (1)
5676+ #define _SOKOL_GL_HAS_BASEINSTANCE (1)
5677+ #endif
5678+ #if defined(GL_VERSION_3_3) || defined(_SOKOL_USE_WIN32_GL_LOADER)
5679+ #define _SOKOL_GL_HAS_DUALSOURCEBLENDING (1)
5680+ #endif
5681+ #if defined(GL_VERSION_3_2) || defined(_SOKOL_USE_WIN32_GL_LOADER)
5682+ #define _SOKOL_GL_HAS_BASEVERTEX (1)
5683+ #endif
5684+ #elif defined(__APPLE__)
5685+ #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
5686+ #if defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST
5687+ #define _SOKOL_GL_HAS_COLORMASKI (1)
5688+ #define _SOKOL_GL_HAS_BASEVERTEX (1)
5689+ #define _SOKOL_GL_HAS_DUALSOURCEBLENDING (1)
5690+ #else
5691+ #define _SOKOL_GL_HAS_TEXSTORAGE (1)
5692+ #endif
5693+ #else
5694+ #define _SOKOL_GL_HAS_COLORMASKI (1)
5695+ #define _SOKOL_GL_HAS_BASEVERTEX (1)
5696+ #define _SOKOL_GL_HAS_DUALSOURCEBLENDING (1)
5697+ #endif
5698+ #elif defined(__EMSCRIPTEN__)
5699+ #define _SOKOL_GL_HAS_TEXSTORAGE (1)
5700+ #elif defined(__ANDROID__)
5701+ #define _SOKOL_GL_HAS_COMPUTE (1)
5702+ #define _SOKOL_GL_HAS_TEXSTORAGE (1)
5703+ #if defined(GL_ES_VERSION_3_2)
5704+ #define _SOKOL_GL_HAS_COLORMASKI (1)
5705+ #endif
5706+ #elif defined(__linux__) || defined(__unix__)
5707+ #define _SOKOL_GL_HAS_COLORMASKI (1)
5708+ #if defined(SOKOL_GLCORE)
5709+ #if defined(GL_VERSION_4_3)
5710+ #define _SOKOL_GL_HAS_COMPUTE (1)
5711+ #define _SOKOL_GL_HAS_TEXVIEWS (1)
5712+ #endif
5713+ #if defined(GL_VERSION_4_2)
5714+ #define _SOKOL_GL_HAS_TEXSTORAGE (1)
5715+ #define _SOKOL_GL_HAS_BASEINSTANCE (1)
5716+ #endif
5717+ #if defined(GL_VERSION_3_3)
5718+ #define _SOKOL_GL_HAS_DUALSOURCEBLENDING (1)
5719+ #endif
5720+ #if defined(GL_VERSION_3_2)
5721+ #define _SOKOL_GL_HAS_BASEVERTEX (1)
5722+ #endif
5723+ #else
5724+ #define _SOKOL_GL_HAS_COMPUTE (1)
5725+ #define _SOKOL_GL_HAS_TEXSTORAGE (1)
5726+ #define _SOKOL_GL_HAS_BASEVERTEX (1)
5727+ #endif
5728+ #endif
5729+
5730+ // optional GL loader definitions (only on Win32)
5731+ #if defined(_SOKOL_USE_WIN32_GL_LOADER)
5732+ #define __gl_h_ 1
5733+ #define __gl32_h_ 1
5734+ #define __gl31_h_ 1
5735+ #define __GL_H__ 1
5736+ #define __glext_h_ 1
5737+ #define __GLEXT_H_ 1
5738+ #define __gltypes_h_ 1
5739+ #define __glcorearb_h_ 1
5740+ #define __gl_glcorearb_h_ 1
5741+ #define GL_APIENTRY APIENTRY
5742+
5743+ typedef unsigned int GLenum;
5744+ typedef unsigned int GLuint;
5745+ typedef int GLsizei;
5746+ typedef char GLchar;
5747+ typedef ptrdiff_t GLintptr;
5748+ typedef ptrdiff_t GLsizeiptr;
5749+ typedef double GLclampd;
5750+ typedef unsigned short GLushort;
5751+ typedef unsigned char GLubyte;
5752+ typedef unsigned char GLboolean;
5753+ typedef uint64_t GLuint64;
5754+ typedef double GLdouble;
5755+ typedef unsigned short GLhalf;
5756+ typedef float GLclampf;
5757+ typedef unsigned int GLbitfield;
5758+ typedef signed char GLbyte;
5759+ typedef short GLshort;
5760+ typedef void GLvoid;
5761+ typedef int64_t GLint64;
5762+ typedef float GLfloat;
5763+ typedef int GLint;
5764+ #define GL_INT_2_10_10_10_REV 0x8D9F
5765+ #define GL_R32F 0x822E
5766+ #define GL_PROGRAM_POINT_SIZE 0x8642
5767+ #define GL_DEPTH_ATTACHMENT 0x8D00
5768+ #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
5769+ #define GL_COLOR_ATTACHMENT0 0x8CE0
5770+ #define GL_R16F 0x822D
5771+ #define GL_DRAW_FRAMEBUFFER 0x8CA9
5772+ #define GL_FRAMEBUFFER_COMPLETE 0x8CD5
5773+ #define GL_NUM_EXTENSIONS 0x821D
5774+ #define GL_INFO_LOG_LENGTH 0x8B84
5775+ #define GL_VERTEX_SHADER 0x8B31
5776+ #define GL_INCR 0x1E02
5777+ #define GL_DYNAMIC_DRAW 0x88E8
5778+ #define GL_STATIC_DRAW 0x88E4
5779+ #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
5780+ #define GL_TEXTURE_CUBE_MAP 0x8513
5781+ #define GL_FUNC_SUBTRACT 0x800A
5782+ #define GL_FUNC_REVERSE_SUBTRACT 0x800B
5783+ #define GL_CONSTANT_COLOR 0x8001
5784+ #define GL_DECR_WRAP 0x8508
5785+ #define GL_R8 0x8229
5786+ #define GL_LINEAR_MIPMAP_LINEAR 0x2703
5787+ #define GL_ELEMENT_ARRAY_BUFFER 0x8893
5788+ #define GL_SHORT 0x1402
5789+ #define GL_DEPTH_TEST 0x0B71
5790+ #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
5791+ #define GL_LINK_STATUS 0x8B82
5792+ #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
5793+ #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E
5794+ #define GL_RGBA16F 0x881A
5795+ #define GL_CONSTANT_ALPHA 0x8003
5796+ #define GL_READ_FRAMEBUFFER 0x8CA8
5797+ #define GL_TEXTURE0 0x84C0
5798+ #define GL_TEXTURE_MIN_LOD 0x813A
5799+ #define GL_CLAMP_TO_EDGE 0x812F
5800+ #define GL_UNSIGNED_SHORT_5_6_5 0x8363
5801+ #define GL_TEXTURE_WRAP_R 0x8072
5802+ #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
5803+ #define GL_NEAREST_MIPMAP_NEAREST 0x2700
5804+ #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
5805+ #define GL_SRC_ALPHA_SATURATE 0x0308
5806+ #define GL_STREAM_DRAW 0x88E0
5807+ #define GL_ONE 1
5808+ #define GL_NEAREST_MIPMAP_LINEAR 0x2702
5809+ #define GL_RGB10_A2 0x8059
5810+ #define GL_RGBA8 0x8058
5811+ #define GL_SRGB8_ALPHA8 0x8C43
5812+ #define GL_RGBA4 0x8056
5813+ #define GL_RGB8 0x8051
5814+ #define GL_ARRAY_BUFFER 0x8892
5815+ #define GL_STENCIL 0x1802
5816+ #define GL_TEXTURE_2D 0x0DE1
5817+ #define GL_DEPTH 0x1801
5818+ #define GL_FRONT 0x0404
5819+ #define GL_STENCIL_BUFFER_BIT 0x00000400
5820+ #define GL_REPEAT 0x2901
5821+ #define GL_RGBA 0x1908
5822+ #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
5823+ #define GL_DECR 0x1E03
5824+ #define GL_FRAGMENT_SHADER 0x8B30
5825+ #define GL_COMPUTE_SHADER 0x91B9
5826+ #define GL_FLOAT 0x1406
5827+ #define GL_TEXTURE_MAX_LOD 0x813B
5828+ #define GL_DEPTH_COMPONENT 0x1902
5829+ #define GL_ONE_MINUS_DST_ALPHA 0x0305
5830+ #define GL_COLOR 0x1800
5831+ #define GL_TEXTURE_2D_ARRAY 0x8C1A
5832+ #define GL_TRIANGLES 0x0004
5833+ #define GL_UNSIGNED_BYTE 0x1401
5834+ #define GL_TEXTURE_MAG_FILTER 0x2800
5835+ #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
5836+ #define GL_NONE 0
5837+ #define GL_SRC_COLOR 0x0300
5838+ #define GL_SRC1_ALPHA 0x8589
5839+ #define GL_SRC1_COLOR 0x88F9
5840+ #define GL_ONE_MINUS_SRC1_ALPHA 0x88FB
5841+ #define GL_ONE_MINUS_SRC1_COLOR 0x88FA
5842+ #define GL_BYTE 0x1400
5843+ #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
5844+ #define GL_LINE_STRIP 0x0003
5845+ #define GL_TEXTURE_3D 0x806F
5846+ #define GL_CW 0x0900
5847+ #define GL_LINEAR 0x2601
5848+ #define GL_RENDERBUFFER 0x8D41
5849+ #define GL_GEQUAL 0x0206
5850+ #define GL_COLOR_BUFFER_BIT 0x00004000
5851+ #define GL_RGBA32F 0x8814
5852+ #define GL_BLEND 0x0BE2
5853+ #define GL_ONE_MINUS_SRC_ALPHA 0x0303
5854+ #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
5855+ #define GL_TEXTURE_WRAP_T 0x2803
5856+ #define GL_TEXTURE_WRAP_S 0x2802
5857+ #define GL_TEXTURE_MIN_FILTER 0x2801
5858+ #define GL_LINEAR_MIPMAP_NEAREST 0x2701
5859+ #define GL_EXTENSIONS 0x1F03
5860+ #define GL_NO_ERROR 0
5861+ #define GL_REPLACE 0x1E01
5862+ #define GL_KEEP 0x1E00
5863+ #define GL_CCW 0x0901
5864+ #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
5865+ #define GL_RGB 0x1907
5866+ #define GL_TRIANGLE_STRIP 0x0005
5867+ #define GL_FALSE 0
5868+ #define GL_ZERO 0
5869+ #define GL_CULL_FACE 0x0B44
5870+ #define GL_INVERT 0x150A
5871+ #define GL_INT 0x1404
5872+ #define GL_UNSIGNED_INT 0x1405
5873+ #define GL_UNSIGNED_SHORT 0x1403
5874+ #define GL_NEAREST 0x2600
5875+ #define GL_SCISSOR_TEST 0x0C11
5876+ #define GL_LEQUAL 0x0203
5877+ #define GL_STENCIL_TEST 0x0B90
5878+ #define GL_DITHER 0x0BD0
5879+ #define GL_DEPTH_COMPONENT32F 0x8CAC
5880+ #define GL_EQUAL 0x0202
5881+ #define GL_FRAMEBUFFER 0x8D40
5882+ #define GL_RGB5 0x8050
5883+ #define GL_LINES 0x0001
5884+ #define GL_DEPTH_BUFFER_BIT 0x00000100
5885+ #define GL_SRC_ALPHA 0x0302
5886+ #define GL_INCR_WRAP 0x8507
5887+ #define GL_LESS 0x0201
5888+ #define GL_MULTISAMPLE 0x809D
5889+ #define GL_FRAMEBUFFER_BINDING 0x8CA6
5890+ #define GL_BACK 0x0405
5891+ #define GL_ALWAYS 0x0207
5892+ #define GL_FUNC_ADD 0x8006
5893+ #define GL_ONE_MINUS_DST_COLOR 0x0307
5894+ #define GL_NOTEQUAL 0x0205
5895+ #define GL_DST_COLOR 0x0306
5896+ #define GL_COMPILE_STATUS 0x8B81
5897+ #define GL_RED 0x1903
5898+ #define GL_DST_ALPHA 0x0304
5899+ #define GL_RGB5_A1 0x8057
5900+ #define GL_GREATER 0x0204
5901+ #define GL_POLYGON_OFFSET_FILL 0x8037
5902+ #define GL_TRUE 1
5903+ #define GL_NEVER 0x0200
5904+ #define GL_POINTS 0x0000
5905+ #define GL_ONE_MINUS_SRC_COLOR 0x0301
5906+ #define GL_MIRRORED_REPEAT 0x8370
5907+ #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
5908+ #define GL_R11F_G11F_B10F 0x8C3A
5909+ #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B
5910+ #define GL_RGB9_E5 0x8C3D
5911+ #define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E
5912+ #define GL_RGBA32UI 0x8D70
5913+ #define GL_RGB32UI 0x8D71
5914+ #define GL_RGBA16UI 0x8D76
5915+ #define GL_RGB16UI 0x8D77
5916+ #define GL_RGBA8UI 0x8D7C
5917+ #define GL_RGB8UI 0x8D7D
5918+ #define GL_RGBA32I 0x8D82
5919+ #define GL_RGB32I 0x8D83
5920+ #define GL_RGBA16I 0x8D88
5921+ #define GL_RGB16I 0x8D89
5922+ #define GL_RGBA8I 0x8D8E
5923+ #define GL_RGB8I 0x8D8F
5924+ #define GL_RED_INTEGER 0x8D94
5925+ #define GL_RG 0x8227
5926+ #define GL_RG_INTEGER 0x8228
5927+ #define GL_R8 0x8229
5928+ #define GL_R16 0x822A
5929+ #define GL_RG8 0x822B
5930+ #define GL_RG16 0x822C
5931+ #define GL_R16F 0x822D
5932+ #define GL_R32F 0x822E
5933+ #define GL_RG16F 0x822F
5934+ #define GL_RG32F 0x8230
5935+ #define GL_R8I 0x8231
5936+ #define GL_R8UI 0x8232
5937+ #define GL_R16I 0x8233
5938+ #define GL_R16UI 0x8234
5939+ #define GL_R32I 0x8235
5940+ #define GL_R32UI 0x8236
5941+ #define GL_RG8I 0x8237
5942+ #define GL_RG8UI 0x8238
5943+ #define GL_RG16I 0x8239
5944+ #define GL_RG16UI 0x823A
5945+ #define GL_RG32I 0x823B
5946+ #define GL_RG32UI 0x823C
5947+ #define GL_RGBA_INTEGER 0x8D99
5948+ #define GL_R8_SNORM 0x8F94
5949+ #define GL_RG8_SNORM 0x8F95
5950+ #define GL_RGB8_SNORM 0x8F96
5951+ #define GL_RGBA8_SNORM 0x8F97
5952+ #define GL_R16_SNORM 0x8F98
5953+ #define GL_RG16_SNORM 0x8F99
5954+ #define GL_RGB16_SNORM 0x8F9A
5955+ #define GL_RGBA16_SNORM 0x8F9B
5956+ #define GL_RGBA16 0x805B
5957+ #define GL_MAX_TEXTURE_SIZE 0x0D33
5958+ #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
5959+ #define GL_MAX_3D_TEXTURE_SIZE 0x8073
5960+ #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
5961+ #define GL_MAX_VERTEX_ATTRIBS 0x8869
5962+ #define GL_CLAMP_TO_BORDER 0x812D
5963+ #define GL_TEXTURE_BORDER_COLOR 0x1004
5964+ #define GL_CURRENT_PROGRAM 0x8B8D
5965+ #define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
5966+ #define GL_UNPACK_ALIGNMENT 0x0CF5
5967+ #define GL_FRAMEBUFFER_SRGB 0x8DB9
5968+ #define GL_TEXTURE_COMPARE_MODE 0x884C
5969+ #define GL_TEXTURE_COMPARE_FUNC 0x884D
5970+ #define GL_COMPARE_REF_TO_TEXTURE 0x884E
5971+ #define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
5972+ #define GL_TEXTURE_MAX_LEVEL 0x813D
5973+ #define GL_FRAMEBUFFER_UNDEFINED 0x8219
5974+ #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
5975+ #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
5976+ #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
5977+ #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56
5978+ #define GL_MAJOR_VERSION 0x821B
5979+ #define GL_MINOR_VERSION 0x821C
5980+ #define GL_TEXTURE_2D_MULTISAMPLE 0x9100
5981+ #define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102
5982+ #define GL_SHADER_STORAGE_BARRIER_BIT 0x2000
5983+ #define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001
5984+ #define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002
5985+ #define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008
5986+ #define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
5987+ #define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400
5988+ #define GL_MIN 0x8007
5989+ #define GL_MAX 0x8008
5990+ #define GL_WRITE_ONLY 0x88B9
5991+ #define GL_READ_WRITE 0x88BA
5992+ #define GL_MAX_DRAW_BUFFERS 0x8824
5993+ #define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
5994+ #define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD
5995+ #define GL_MAX_IMAGE_UNITS 0x8F38
5996+ #define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD
5997+ #define GL_DEPTH32F_STENCIL8 0x8CAD
5998+ #endif
5999+
6000+ #ifndef GL_UNSIGNED_INT_2_10_10_10_REV
6001+ #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
6002+ #endif
6003+ #ifndef GL_UNSIGNED_INT_24_8
6004+ #define GL_UNSIGNED_INT_24_8 0x84FA
6005+ #endif
6006+ #ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
6007+ #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
6008+ #endif
6009+ #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
6010+ #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
6011+ #endif
6012+ #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
6013+ #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
6014+ #endif
6015+ #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
6016+ #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
6017+ #endif
6018+ #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
6019+ #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
6020+ #endif
6021+ #ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT
6022+ #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
6023+ #endif
6024+ #ifndef GL_COMPRESSED_RED_RGTC1
6025+ #define GL_COMPRESSED_RED_RGTC1 0x8DBB
6026+ #endif
6027+ #ifndef GL_COMPRESSED_SIGNED_RED_RGTC1
6028+ #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC
6029+ #endif
6030+ #ifndef GL_COMPRESSED_RED_GREEN_RGTC2
6031+ #define GL_COMPRESSED_RED_GREEN_RGTC2 0x8DBD
6032+ #endif
6033+ #ifndef GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2
6034+ #define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 0x8DBE
6035+ #endif
6036+ #ifndef GL_COMPRESSED_RGBA_BPTC_UNORM_ARB
6037+ #define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C
6038+ #endif
6039+ #ifndef GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB
6040+ #define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D
6041+ #endif
6042+ #ifndef GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB
6043+ #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E
6044+ #endif
6045+ #ifndef GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB
6046+ #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F
6047+ #endif
6048+ #ifndef GL_COMPRESSED_RGB8_ETC2
6049+ #define GL_COMPRESSED_RGB8_ETC2 0x9274
6050+ #endif
6051+ #ifndef GL_COMPRESSED_SRGB8_ETC2
6052+ #define GL_COMPRESSED_SRGB8_ETC2 0x9275
6053+ #endif
6054+ #ifndef GL_COMPRESSED_RGBA8_ETC2_EAC
6055+ #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
6056+ #endif
6057+ #ifndef GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC
6058+ #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279
6059+ #endif
6060+ #ifndef GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
6061+ #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276
6062+ #endif
6063+ #ifndef GL_COMPRESSED_R11_EAC
6064+ #define GL_COMPRESSED_R11_EAC 0x9270
6065+ #endif
6066+ #ifndef GL_COMPRESSED_SIGNED_R11_EAC
6067+ #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271
6068+ #endif
6069+ #ifndef GL_COMPRESSED_RG11_EAC
6070+ #define GL_COMPRESSED_RG11_EAC 0x9272
6071+ #endif
6072+ #ifndef GL_COMPRESSED_SIGNED_RG11_EAC
6073+ #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273
6074+ #endif
6075+ #ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR
6076+ #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0
6077+ #endif
6078+ #ifndef GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR
6079+ #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0
6080+ #endif
6081+ #ifndef GL_DEPTH24_STENCIL8
6082+ #define GL_DEPTH24_STENCIL8 0x88F0
6083+ #endif
6084+ #ifndef GL_HALF_FLOAT
6085+ #define GL_HALF_FLOAT 0x140B
6086+ #endif
6087+ #ifndef GL_DEPTH_STENCIL
6088+ #define GL_DEPTH_STENCIL 0x84F9
6089+ #endif
6090+ #ifndef GL_LUMINANCE
6091+ #define GL_LUMINANCE 0x1909
6092+ #endif
6093+ #ifndef GL_COMPUTE_SHADER
6094+ #define GL_COMPUTE_SHADER 0x91B9
6095+ #endif
6096+ #ifndef _SG_GL_CHECK_ERROR
6097+ #if defined(__EMSCRIPTEN__)
6098+ // generally turn off glGetError() on WASM, it's a too big performance hit
6099+ // and WebGL provides much better diagnostics anyway
6100+ #define _SG_GL_CHECK_ERROR()
6101+ #elif defined(SOKOL_DEBUG)
6102+ // make sure that glGetError() is only called in debug mode
6103+ #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); }
6104+ #else
6105+ #define _SG_GL_CHECK_ERROR()
6106+ #endif
6107+ #endif
6108+ // make some GL constants generally available to simplify compilation,
6109+ // use of those constants will be filtered by runtime flags
6110+ #ifndef GL_SHADER_STORAGE_BUFFER
6111+ #define GL_SHADER_STORAGE_BUFFER 0x90D2
6112+ #endif
6113+#endif
6114+
6115+#if defined(SOKOL_GLES3)
6116+ // on WebGL2, GL_FRAMEBUFFER_UNDEFINED technically doesn't exist (it is defined
6117+ // in the Emscripten headers, but may not exist in other WebGL2 shims)
6118+ // see: https://github.com/floooh/sokol/pull/933
6119+ #ifndef GL_FRAMEBUFFER_UNDEFINED
6120+ #define GL_FRAMEBUFFER_UNDEFINED 0x8219
6121+ #endif
6122+#endif
6123+
6124+// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████
6125+// ██ ██ ██ ██ ██ ██ ██ ██ ██
6126+// ███████ ██ ██████ ██ ██ ██ ██ ███████
6127+// ██ ██ ██ ██ ██ ██ ██ ██ ██
6128+// ███████ ██ ██ ██ ██████ ██████ ██ ███████
6129+//
6130+// >>structs
6131+
6132+typedef struct { int x, y, w, h; } _sg_recti_t;
6133+typedef struct { int width, height; } _sg_dimi_t;
6134+
6135+// resource pool slots
6136+typedef struct {
6137+ uint32_t id;
6138+ uint32_t uninit_count;
6139+ sg_resource_state state;
6140+} _sg_slot_t;
6141+
6142+// resource pool housekeeping struct
6143+typedef struct {
6144+ int size;
6145+ int queue_top;
6146+ uint32_t* gen_ctrs;
6147+ int* free_queue;
6148+} _sg_pool_t;
6149+
6150+// resource hazard tracking struct
6151+typedef struct {
6152+ int num_slots;
6153+ int cur_slot;
6154+ uint32_t* slots; // tracked unique resource ids
6155+ uint32_t occupy_num_bytes; // size of occupy_bits array in bytes
6156+ uint8_t* occupy_bits; // one set bit for each unique resource (idx = (id & 0xFFFF) >> 3)
6157+} _sg_track_t;
6158+
6159+// resource func forward decls
6160+struct _sg_buffer_s;
6161+struct _sg_image_s;
6162+struct _sg_sampler_s;
6163+struct _sg_shader_s;
6164+struct _sg_pipeline_s;
6165+struct _sg_view_s;
6166+
6167+// a general resource slot reference useful for caches
6168+typedef struct _sg_sref_s {
6169+ uint32_t id;
6170+ uint32_t uninit_count;
6171+} _sg_sref_t;
6172+
6173+// safe (in debug mode) internal resource references
6174+typedef struct _sg_buffer_ref_s {
6175+ struct _sg_buffer_s* ptr;
6176+ _sg_sref_t sref;
6177+} _sg_buffer_ref_t;
6178+
6179+typedef struct _sg_image_ref_s {
6180+ struct _sg_image_s* ptr;
6181+ _sg_sref_t sref;
6182+} _sg_image_ref_t;
6183+
6184+typedef struct _sg_sampler_ref_t {
6185+ struct _sg_sampler_s* ptr;
6186+ _sg_sref_t sref;
6187+} _sg_sampler_ref_t;
6188+
6189+typedef struct _sg_shader_ref_s {
6190+ struct _sg_shader_s* ptr;
6191+ _sg_sref_t sref;
6192+} _sg_shader_ref_t;
6193+
6194+typedef struct _sg_pipeline_ref_s {
6195+ struct _sg_pipeline_s* ptr;
6196+ _sg_sref_t sref;
6197+} _sg_pipeline_ref_t;
6198+
6199+typedef struct _sg_view_ref_s {
6200+ struct _sg_view_s* ptr;
6201+ _sg_sref_t sref;
6202+} _sg_view_ref_t;
6203+
6204+// constants
6205+enum {
6206+ _SG_STRING_SIZE = 32,
6207+ _SG_SLOT_SHIFT = 16,
6208+ _SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1,
6209+ _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT),
6210+ _SG_DEFAULT_BUFFER_POOL_SIZE = 128,
6211+ _SG_DEFAULT_IMAGE_POOL_SIZE = 128,
6212+ _SG_DEFAULT_SAMPLER_POOL_SIZE = 64,
6213+ _SG_DEFAULT_SHADER_POOL_SIZE = 32,
6214+ _SG_DEFAULT_PIPELINE_POOL_SIZE = 64,
6215+ _SG_DEFAULT_VIEW_POOL_SIZE = 256,
6216+ _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024,
6217+ _SG_DEFAULT_MAX_COMMIT_LISTENERS = 1024,
6218+ _SG_DEFAULT_WGPU_BINDGROUP_CACHE_SIZE = 1024,
6219+ _SG_DEFAULT_VK_COPY_STAGING_SIZE = (4 * 1024 * 1024),
6220+ _SG_DEFAULT_VK_STREAM_STAGING_SIZE = (16 * 1024 * 1024),
6221+ _SG_DEFAULT_VK_DESCRIPTOR_BUFFER_SIZE = (16 * 1024 * 1024),
6222+ _SG_MAX_STORAGEBUFFER_BINDINGS_PER_STAGE = SG_MAX_VIEW_BINDSLOTS,
6223+ _SG_MAX_STORAGEIMAGE_BINDINGS_PER_STAGE = SG_MAX_VIEW_BINDSLOTS,
6224+ _SG_MAX_TEXTURE_BINDINGS_PER_STAGE = SG_MAX_VIEW_BINDSLOTS,
6225+ _SG_MAX_UNIFORMBLOCK_BINDINGS_PER_STAGE = 8,
6226+};
6227+
6228+// fixed-size string
6229+typedef struct {
6230+ char buf[_SG_STRING_SIZE];
6231+} _sg_str_t;
6232+
6233+typedef struct {
6234+ int size;
6235+ int append_pos;
6236+ bool append_overflow;
6237+ uint32_t update_frame_index;
6238+ uint32_t append_frame_index;
6239+ int num_slots;
6240+ int active_slot;
6241+ sg_buffer_usage usage;
6242+} _sg_buffer_common_t;
6243+
6244+typedef struct {
6245+ uint32_t upd_frame_index;
6246+ int num_slots;
6247+ int active_slot;
6248+ sg_image_type type;
6249+ int width;
6250+ int height;
6251+ int num_slices;
6252+ int num_mipmaps;
6253+ sg_image_usage usage;
6254+ sg_pixel_format pixel_format;
6255+ int sample_count;
6256+} _sg_image_common_t;
6257+
6258+typedef struct {
6259+ sg_filter min_filter;
6260+ sg_filter mag_filter;
6261+ sg_filter mipmap_filter;
6262+ sg_wrap wrap_u;
6263+ sg_wrap wrap_v;
6264+ sg_wrap wrap_w;
6265+ float min_lod;
6266+ float max_lod;
6267+ sg_border_color border_color;
6268+ sg_compare_func compare;
6269+ uint32_t max_anisotropy;
6270+} _sg_sampler_common_t;
6271+
6272+typedef struct {
6273+ sg_shader_attr_base_type base_type;
6274+} _sg_shader_attr_t;
6275+
6276+typedef struct {
6277+ sg_shader_stage stage;
6278+ uint32_t size;
6279+} _sg_shader_uniform_block_t;
6280+
6281+typedef struct {
6282+ sg_shader_stage stage;
6283+ sg_view_type view_type;
6284+ sg_image_type image_type;
6285+ sg_pixel_format access_format;
6286+ sg_image_sample_type sample_type;
6287+ bool sbuf_readonly;
6288+ bool simg_writeonly;
6289+ bool multisampled;
6290+} _sg_shader_view_t;
6291+
6292+typedef struct {
6293+ sg_shader_stage stage;
6294+ sg_sampler_type sampler_type;
6295+} _sg_shader_sampler_t;
6296+
6297+typedef struct {
6298+ sg_shader_stage stage;
6299+ uint8_t view_slot;
6300+ uint8_t sampler_slot;
6301+} _sg_shader_texture_sampler_t;
6302+
6303+typedef struct {
6304+ uint32_t required_bindings_and_uniforms;
6305+ bool is_compute;
6306+ _sg_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES];
6307+ _sg_shader_uniform_block_t uniform_blocks[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
6308+ _sg_shader_view_t views[SG_MAX_VIEW_BINDSLOTS];
6309+ _sg_shader_sampler_t samplers[SG_MAX_SAMPLER_BINDSLOTS];
6310+ _sg_shader_texture_sampler_t texture_samplers[SG_MAX_TEXTURE_SAMPLER_PAIRS];
6311+} _sg_shader_common_t;
6312+
6313+typedef struct {
6314+ bool vertex_buffer_layout_active[SG_MAX_VERTEXBUFFER_BINDSLOTS];
6315+ bool use_instanced_draw;
6316+ bool is_compute;
6317+ uint32_t required_bindings_and_uniforms;
6318+ _sg_shader_ref_t shader;
6319+ sg_vertex_layout_state layout;
6320+ sg_depth_state depth;
6321+ sg_stencil_state stencil;
6322+ int color_count;
6323+ sg_color_target_state colors[SG_MAX_COLOR_ATTACHMENTS];
6324+ sg_primitive_type primitive_type;
6325+ sg_index_type index_type;
6326+ sg_cull_mode cull_mode;
6327+ sg_face_winding face_winding;
6328+ int sample_count;
6329+ sg_color blend_color;
6330+ bool alpha_to_coverage_enabled;
6331+} _sg_pipeline_common_t;
6332+
6333+typedef struct {
6334+ _sg_buffer_ref_t ref;
6335+ int offset;
6336+} _sg_buffer_view_common_t;
6337+
6338+typedef struct {
6339+ _sg_image_ref_t ref;
6340+ int mip_level;
6341+ int slice;
6342+ int mip_level_count;
6343+ int slice_count;
6344+} _sg_image_view_common_t;
6345+
6346+typedef struct {
6347+ sg_view_type type;
6348+ _sg_buffer_view_common_t buf;
6349+ _sg_image_view_common_t img;
6350+} _sg_view_common_t;
6351+
6352+#if defined(SOKOL_DUMMY_BACKEND)
6353+typedef struct _sg_buffer_s {
6354+ _sg_slot_t slot;
6355+ _sg_buffer_common_t cmn;
6356+} _sg_dummy_buffer_t;
6357+typedef _sg_dummy_buffer_t _sg_buffer_t;
6358+
6359+typedef struct _sg_image_s {
6360+ _sg_slot_t slot;
6361+ _sg_image_common_t cmn;
6362+} _sg_dummy_image_t;
6363+typedef _sg_dummy_image_t _sg_image_t;
6364+
6365+typedef struct _sg_sampler_s {
6366+ _sg_slot_t slot;
6367+ _sg_sampler_common_t cmn;
6368+} _sg_dummy_sampler_t;
6369+typedef _sg_dummy_sampler_t _sg_sampler_t;
6370+
6371+typedef struct _sg_shader_s {
6372+ _sg_slot_t slot;
6373+ _sg_shader_common_t cmn;
6374+} _sg_dummy_shader_t;
6375+typedef _sg_dummy_shader_t _sg_shader_t;
6376+
6377+typedef struct _sg_pipeline_s {
6378+ _sg_slot_t slot;
6379+ _sg_pipeline_common_t cmn;
6380+} _sg_dummy_pipeline_t;
6381+typedef _sg_dummy_pipeline_t _sg_pipeline_t;
6382+
6383+typedef struct _sg_view_s {
6384+ _sg_slot_t slot;
6385+ _sg_view_common_t cmn;
6386+} _sg_dummy_view_t;
6387+typedef _sg_dummy_view_t _sg_view_t;
6388+
6389+#elif defined(_SOKOL_ANY_GL)
6390+
6391+typedef enum {
6392+ _SG_GL_GPUDIRTY_VERTEXBUFFER = (1<<0),
6393+ _SG_GL_GPUDIRTY_INDEXBUFFER = (1<<1),
6394+ _SG_GL_GPUDIRTY_STORAGEBUFFER = (1<<2),
6395+ _SG_GL_GPUDIRTY_TEXTURE = (1<<3),
6396+ _SG_GL_GPUDIRTY_STORAGEIMAGE = (1<<4),
6397+ _SG_GL_GPUDIRTY_ATTACHMENT = (1<<5),
6398+ _SG_GL_GPUDIRTY_BUFFER_ALL = _SG_GL_GPUDIRTY_VERTEXBUFFER | _SG_GL_GPUDIRTY_INDEXBUFFER | _SG_GL_GPUDIRTY_STORAGEBUFFER,
6399+ _SG_GL_GPUDIRTY_IMAGE_ALL = _SG_GL_GPUDIRTY_TEXTURE | _SG_GL_GPUDIRTY_STORAGEIMAGE | _SG_GL_GPUDIRTY_ATTACHMENT,
6400+} _sg_gl_gpudirty_t;
6401+
6402+typedef struct _sg_buffer_s {
6403+ _sg_slot_t slot;
6404+ _sg_buffer_common_t cmn;
6405+ struct {
6406+ GLuint buf[SG_NUM_INFLIGHT_FRAMES];
6407+ uint8_t gpu_dirty_flags; // combination of _sg_gl_gpudirty_t flags
6408+ bool injected; // if true, external buffers were injected with sg_buffer_desc.gl_buffers
6409+ } gl;
6410+} _sg_gl_buffer_t;
6411+typedef _sg_gl_buffer_t _sg_buffer_t;
6412+
6413+typedef struct _sg_image_s {
6414+ _sg_slot_t slot;
6415+ _sg_image_common_t cmn;
6416+ struct {
6417+ GLenum target;
6418+ GLuint tex[SG_NUM_INFLIGHT_FRAMES];
6419+ uint8_t gpu_dirty_flags; // combination of _sg_gl_gpudirty_flags
6420+ bool injected; // if true, external textures were injected with sg_image_desc.gl_textures
6421+ } gl;
6422+} _sg_gl_image_t;
6423+typedef _sg_gl_image_t _sg_image_t;
6424+
6425+typedef struct _sg_sampler_s {
6426+ _sg_slot_t slot;
6427+ _sg_sampler_common_t cmn;
6428+ struct {
6429+ GLuint smp;
6430+ bool injected; // true if external sampler was injects in sg_sampler_desc.gl_sampler
6431+ } gl;
6432+} _sg_gl_sampler_t;
6433+typedef _sg_gl_sampler_t _sg_sampler_t;
6434+
6435+typedef struct {
6436+ GLint gl_loc;
6437+ sg_uniform_type type;
6438+ uint16_t count;
6439+ uint16_t offset;
6440+} _sg_gl_uniform_t;
6441+
6442+typedef struct {
6443+ int num_uniforms;
6444+ _sg_gl_uniform_t uniforms[SG_MAX_UNIFORMBLOCK_MEMBERS];
6445+} _sg_gl_uniform_block_t;
6446+
6447+typedef struct {
6448+ _sg_str_t name;
6449+} _sg_gl_shader_attr_t;
6450+
6451+typedef struct _sg_shader_s {
6452+ _sg_slot_t slot;
6453+ _sg_shader_common_t cmn;
6454+ struct {
6455+ GLuint prog;
6456+ _sg_gl_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES];
6457+ _sg_gl_uniform_block_t uniform_blocks[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
6458+ uint8_t sbuf_binding[SG_MAX_VIEW_BINDSLOTS];
6459+ uint8_t simg_binding[SG_MAX_VIEW_BINDSLOTS];
6460+ int8_t tex_slot[SG_MAX_TEXTURE_SAMPLER_PAIRS]; // GL texture unit index
6461+ } gl;
6462+} _sg_gl_shader_t;
6463+typedef _sg_gl_shader_t _sg_shader_t;
6464+
6465+typedef struct {
6466+ int8_t vb_index; // -1 if attr is not enabled
6467+ int8_t divisor; // -1 if not initialized
6468+ uint8_t stride;
6469+ uint8_t size;
6470+ uint8_t normalized;
6471+ int offset;
6472+ GLenum type;
6473+ sg_shader_attr_base_type base_type;
6474+} _sg_gl_attr_t;
6475+
6476+typedef struct _sg_pipeline_s {
6477+ _sg_slot_t slot;
6478+ _sg_pipeline_common_t cmn;
6479+ struct {
6480+ _sg_gl_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES];
6481+ sg_depth_state depth;
6482+ sg_stencil_state stencil;
6483+ sg_primitive_type primitive_type;
6484+ sg_blend_state blend;
6485+ sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS];
6486+ sg_cull_mode cull_mode;
6487+ sg_face_winding face_winding;
6488+ int sample_count;
6489+ bool alpha_to_coverage_enabled;
6490+ } gl;
6491+} _sg_gl_pipeline_t;
6492+typedef _sg_gl_pipeline_t _sg_pipeline_t;
6493+
6494+typedef struct _sg_view_s {
6495+ _sg_slot_t slot;
6496+ _sg_view_common_t cmn;
6497+ struct {
6498+ GLuint tex_view[SG_NUM_INFLIGHT_FRAMES]; // only if sg_features.gl_texture_views
6499+ GLuint msaa_render_buffer; // only if !msaa_texture_bindings
6500+ GLuint msaa_resolve_frame_buffer;
6501+ } gl;
6502+} _sg_gl_view_t;
6503+typedef _sg_gl_view_t _sg_view_t;
6504+
6505+typedef struct {
6506+ _sg_gl_attr_t gl_attr;
6507+ GLuint gl_vbuf;
6508+} _sg_gl_cache_attr_t;
6509+
6510+typedef struct {
6511+ GLenum target;
6512+ GLuint texture;
6513+ GLuint sampler;
6514+} _sg_gl_cache_texture_sampler_bind_slot;
6515+
6516+#define _SG_GL_MAX_SBUF_BINDINGS (_SG_MAX_STORAGEBUFFER_BINDINGS_PER_STAGE)
6517+#define _SG_GL_MAX_SIMG_BINDINGS (_SG_MAX_STORAGEIMAGE_BINDINGS_PER_STAGE)
6518+#define _SG_GL_MAX_TEX_SMP_BINDINGS (SG_MAX_TEXTURE_SAMPLER_PAIRS)
6519+typedef struct {
6520+ sg_depth_state depth;
6521+ sg_stencil_state stencil;
6522+ sg_blend_state blend;
6523+ sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS];
6524+ sg_cull_mode cull_mode;
6525+ sg_face_winding face_winding;
6526+ bool polygon_offset_enabled;
6527+ int sample_count;
6528+ sg_color blend_color;
6529+ bool alpha_to_coverage_enabled;
6530+ _sg_gl_cache_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES];
6531+ GLuint vertex_buffer;
6532+ GLuint index_buffer;
6533+ GLuint storage_buffer; // general bind point
6534+ GLuint storage_buffers[_SG_GL_MAX_SBUF_BINDINGS];
6535+ int storage_buffer_offsets[_SG_GL_MAX_SBUF_BINDINGS];
6536+ GLuint stored_vertex_buffer;
6537+ GLuint stored_index_buffer;
6538+ GLuint stored_storage_buffer;
6539+ GLuint prog;
6540+ _sg_gl_cache_texture_sampler_bind_slot texture_samplers[_SG_GL_MAX_TEX_SMP_BINDINGS];
6541+ _sg_gl_cache_texture_sampler_bind_slot stored_texture_sampler;
6542+ int cur_ib_offset;
6543+ GLenum cur_primitive_type;
6544+ GLenum cur_index_type;
6545+ GLenum cur_active_texture;
6546+ _sg_sref_t cur_pip;
6547+} _sg_gl_cache_t;
6548+
6549+typedef struct {
6550+ bool valid;
6551+ GLuint vao; // global mutated vertex-array-object
6552+ GLuint fb; // global mutated framebuffer
6553+ _sg_gl_cache_t cache;
6554+ bool ext_anisotropic;
6555+ GLint max_anisotropy;
6556+ sg_store_action color_store_actions[SG_MAX_COLOR_ATTACHMENTS];
6557+ sg_store_action depth_store_action;
6558+ sg_store_action stencil_store_action;
6559+ #if _SOKOL_USE_WIN32_GL_LOADER
6560+ HINSTANCE opengl32_dll;
6561+ #endif
6562+} _sg_gl_backend_t;
6563+
6564+#elif defined(SOKOL_D3D11)
6565+
6566+typedef struct _sg_buffer_s {
6567+ _sg_slot_t slot;
6568+ _sg_buffer_common_t cmn;
6569+ struct {
6570+ ID3D11Buffer* buf;
6571+ } d3d11;
6572+} _sg_d3d11_buffer_t;
6573+typedef _sg_d3d11_buffer_t _sg_buffer_t;
6574+
6575+typedef struct _sg_image_s {
6576+ _sg_slot_t slot;
6577+ _sg_image_common_t cmn;
6578+ struct {
6579+ DXGI_FORMAT format;
6580+ ID3D11Texture2D* tex2d;
6581+ ID3D11Texture3D* tex3d;
6582+ ID3D11Resource* res; // either tex2d or tex3d
6583+ } d3d11;
6584+} _sg_d3d11_image_t;
6585+typedef _sg_d3d11_image_t _sg_image_t;
6586+
6587+typedef struct _sg_sampler_s {
6588+ _sg_slot_t slot;
6589+ _sg_sampler_common_t cmn;
6590+ struct {
6591+ ID3D11SamplerState* smp;
6592+ } d3d11;
6593+} _sg_d3d11_sampler_t;
6594+typedef _sg_d3d11_sampler_t _sg_sampler_t;
6595+
6596+typedef struct {
6597+ _sg_str_t sem_name;
6598+ int sem_index;
6599+} _sg_d3d11_shader_attr_t;
6600+
6601+#define _SG_D3D11_MAX_TEXTUREARRAY_LAYERS (2048)
6602+#define _SG_D3D11_MAX_TEXTURE_SUBRESOURCES (SG_MAX_MIPMAPS * _SG_D3D11_MAX_TEXTUREARRAY_LAYERS)
6603+#define _SG_D3D11_MAX_STAGE_UB_BINDINGS (_SG_MAX_UNIFORMBLOCK_BINDINGS_PER_STAGE)
6604+#define _SG_D3D11_MAX_STAGE_SRV_BINDINGS (SG_MAX_VIEW_BINDSLOTS)
6605+#define _SG_D3D11_MAX_STAGE_UAV_BINDINGS (SG_MAX_VIEW_BINDSLOTS)
6606+#define _SG_D3D11_MAX_STAGE_SMP_BINDINGS (SG_MAX_SAMPLER_BINDSLOTS)
6607+
6608+typedef struct _sg_shader_s {
6609+ _sg_slot_t slot;
6610+ _sg_shader_common_t cmn;
6611+ struct {
6612+ _sg_d3d11_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES];
6613+ ID3D11VertexShader* vs;
6614+ ID3D11PixelShader* fs;
6615+ ID3D11ComputeShader* cs;
6616+ void* vs_blob;
6617+ size_t vs_blob_length;
6618+ uint8_t ub_register_b_n[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
6619+ uint8_t view_register_t_n[SG_MAX_VIEW_BINDSLOTS];
6620+ uint8_t view_register_u_n[SG_MAX_VIEW_BINDSLOTS];
6621+ uint8_t smp_register_s_n[SG_MAX_SAMPLER_BINDSLOTS];
6622+ ID3D11Buffer* all_cbufs[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
6623+ ID3D11Buffer* vs_cbufs[_SG_D3D11_MAX_STAGE_UB_BINDINGS];
6624+ ID3D11Buffer* fs_cbufs[_SG_D3D11_MAX_STAGE_UB_BINDINGS];
6625+ ID3D11Buffer* cs_cbufs[_SG_D3D11_MAX_STAGE_UB_BINDINGS];
6626+ } d3d11;
6627+} _sg_d3d11_shader_t;
6628+typedef _sg_d3d11_shader_t _sg_shader_t;
6629+
6630+typedef struct _sg_pipeline_s {
6631+ _sg_slot_t slot;
6632+ _sg_pipeline_common_t cmn;
6633+ struct {
6634+ UINT stencil_ref;
6635+ UINT vb_strides[SG_MAX_VERTEXBUFFER_BINDSLOTS];
6636+ D3D_PRIMITIVE_TOPOLOGY topology;
6637+ DXGI_FORMAT index_format;
6638+ ID3D11InputLayout* il;
6639+ ID3D11RasterizerState* rs;
6640+ ID3D11DepthStencilState* dss;
6641+ ID3D11BlendState* bs;
6642+ } d3d11;
6643+} _sg_d3d11_pipeline_t;
6644+typedef _sg_d3d11_pipeline_t _sg_pipeline_t;
6645+
6646+typedef struct _sg_view_s {
6647+ _sg_slot_t slot;
6648+ _sg_view_common_t cmn;
6649+ struct {
6650+ ID3D11ShaderResourceView* srv;
6651+ ID3D11UnorderedAccessView* uav;
6652+ ID3D11RenderTargetView* rtv;
6653+ ID3D11DepthStencilView* dsv;
6654+ } d3d11;
6655+} _sg_d3d11_view_t;
6656+typedef _sg_d3d11_view_t _sg_view_t;
6657+
6658+typedef struct {
6659+ bool valid;
6660+ ID3D11Device* dev;
6661+ ID3D11DeviceContext* ctx;
6662+ struct {
6663+ ID3D11RenderTargetView* render_view;
6664+ ID3D11RenderTargetView* resolve_view;
6665+ } cur_swapchain;
6666+ // on-demand loaded d3dcompiler_47.dll handles
6667+ HINSTANCE d3dcompiler_dll;
6668+ bool d3dcompiler_dll_load_failed;
6669+ pD3DCompile D3DCompile_func;
6670+ // static bindings arrays
6671+ struct {
6672+ ID3D11Buffer* vbs[SG_MAX_VERTEXBUFFER_BINDSLOTS];
6673+ UINT vb_offsets[SG_MAX_VERTEXBUFFER_BINDSLOTS];
6674+ ID3D11ShaderResourceView* vs_srvs[_SG_D3D11_MAX_STAGE_SRV_BINDINGS];
6675+ ID3D11ShaderResourceView* fs_srvs[_SG_D3D11_MAX_STAGE_SRV_BINDINGS];
6676+ ID3D11ShaderResourceView* cs_srvs[_SG_D3D11_MAX_STAGE_SRV_BINDINGS];
6677+ ID3D11UnorderedAccessView* cs_uavs[_SG_D3D11_MAX_STAGE_UAV_BINDINGS];
6678+ ID3D11SamplerState* vs_smps[_SG_D3D11_MAX_STAGE_SMP_BINDINGS];
6679+ ID3D11SamplerState* fs_smps[_SG_D3D11_MAX_STAGE_SMP_BINDINGS];
6680+ ID3D11SamplerState* cs_smps[_SG_D3D11_MAX_STAGE_SMP_BINDINGS];
6681+ } bnd;
6682+ // global subresourcedata array for texture updates
6683+ D3D11_SUBRESOURCE_DATA subres_data[_SG_D3D11_MAX_TEXTURE_SUBRESOURCES];
6684+} _sg_d3d11_backend_t;
6685+
6686+#elif defined(SOKOL_METAL)
6687+
6688+#if defined(_SG_TARGET_MACOS) || defined(_SG_TARGET_IOS_SIMULATOR)
6689+#define _SG_MTL_UB_ALIGN (256)
6690+#else
6691+#define _SG_MTL_UB_ALIGN (16)
6692+#endif
6693+#define _SG_MTL_INVALID_SLOT_INDEX (0)
6694+
6695+typedef struct {
6696+ uint32_t frame_index; // frame index at which it is safe to release this resource
6697+ int slot_index;
6698+} _sg_mtl_release_item_t;
6699+
6700+typedef struct {
6701+ NSMutableArray* pool;
6702+ int num_slots;
6703+ int free_queue_top;
6704+ int* free_queue;
6705+ int release_queue_front;
6706+ int release_queue_back;
6707+ _sg_mtl_release_item_t* release_queue;
6708+} _sg_mtl_idpool_t;
6709+
6710+typedef struct _sg_buffer_s {
6711+ _sg_slot_t slot;
6712+ _sg_buffer_common_t cmn;
6713+ struct {
6714+ int buf[SG_NUM_INFLIGHT_FRAMES]; // index into _sg_mtl_pool
6715+ } mtl;
6716+} _sg_mtl_buffer_t;
6717+typedef _sg_mtl_buffer_t _sg_buffer_t;
6718+
6719+typedef struct _sg_image_s {
6720+ _sg_slot_t slot;
6721+ _sg_image_common_t cmn;
6722+ struct {
6723+ int tex[SG_NUM_INFLIGHT_FRAMES];
6724+ } mtl;
6725+} _sg_mtl_image_t;
6726+typedef _sg_mtl_image_t _sg_image_t;
6727+
6728+typedef struct _sg_sampler_s {
6729+ _sg_slot_t slot;
6730+ _sg_sampler_common_t cmn;
6731+ struct {
6732+ int sampler_state;
6733+ } mtl;
6734+} _sg_mtl_sampler_t;
6735+typedef _sg_mtl_sampler_t _sg_sampler_t;
6736+
6737+typedef struct {
6738+ int mtl_lib;
6739+ int mtl_func;
6740+} _sg_mtl_shader_func_t;
6741+
6742+typedef struct _sg_shader_s {
6743+ _sg_slot_t slot;
6744+ _sg_shader_common_t cmn;
6745+ struct {
6746+ _sg_mtl_shader_func_t vertex_func;
6747+ _sg_mtl_shader_func_t fragment_func;
6748+ _sg_mtl_shader_func_t compute_func;
6749+ MTLSize threads_per_threadgroup;
6750+ uint8_t ub_buffer_n[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
6751+ uint8_t view_buffer_texture_n[SG_MAX_VIEW_BINDSLOTS];
6752+ uint8_t smp_sampler_n[SG_MAX_SAMPLER_BINDSLOTS];
6753+ } mtl;
6754+} _sg_mtl_shader_t;
6755+typedef _sg_mtl_shader_t _sg_shader_t;
6756+
6757+typedef struct _sg_pipeline_s {
6758+ _sg_slot_t slot;
6759+ _sg_pipeline_common_t cmn;
6760+ struct {
6761+ MTLPrimitiveType prim_type;
6762+ int index_size;
6763+ MTLIndexType index_type;
6764+ MTLCullMode cull_mode;
6765+ MTLWinding winding;
6766+ uint32_t stencil_ref;
6767+ MTLSize threads_per_threadgroup;
6768+ int cps; // MTLComputePipelineState
6769+ int rps; // MTLRenderPipelineState
6770+ int dss; // MTLDepthStencilState
6771+ } mtl;
6772+} _sg_mtl_pipeline_t;
6773+typedef _sg_mtl_pipeline_t _sg_pipeline_t;
6774+
6775+typedef struct _sg_view_s {
6776+ _sg_slot_t slot;
6777+ _sg_view_common_t cmn;
6778+ struct {
6779+ int tex_view[SG_NUM_INFLIGHT_FRAMES];
6780+ } mtl;
6781+} _sg_mtl_view_t;
6782+typedef _sg_mtl_view_t _sg_view_t;
6783+
6784+// resource binding state cache
6785+//
6786+// NOTE: reserved buffer bindslot ranges:
6787+// - 0..<=7: uniform buffer bindings
6788+// - 8..<=22: storage buffer bindings
6789+// - 23..<=30: vertex buffer bindings
6790+//
6791+#define _SG_MTL_MAX_STAGE_BUFFER_BINDINGS (31) // see: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
6792+#define _SG_MTL_MAX_STAGE_UB_BINDINGS (_SG_MAX_UNIFORMBLOCK_BINDINGS_PER_STAGE)
6793+#define _SG_MTL_MAX_STAGE_UB_SBUF_BINDINGS (_SG_MTL_MAX_STAGE_BUFFER_BINDINGS - SG_MAX_VERTEXBUFFER_BINDSLOTS)
6794+#define _SG_MTL_MAX_STAGE_TEXTURE_BINDINGS (SG_MAX_VIEW_BINDSLOTS)
6795+#define _SG_MTL_MAX_STAGE_SAMPLER_BINDINGS (SG_MAX_SAMPLER_BINDSLOTS)
6796+
6797+typedef struct {
6798+ _sg_sref_t sref;
6799+ int active_slot;
6800+ int offset;
6801+} _sg_mtl_cache_buf_t;
6802+
6803+typedef struct {
6804+ _sg_sref_t sref;
6805+ int active_slot;
6806+} _sg_mtl_cache_tex_t;
6807+
6808+typedef enum {
6809+ _SG_MTL_CACHE_CMP_EQUAL = 0,
6810+ _SG_MTL_CACHE_CMP_SREF = (1<<1),
6811+ _SG_MTL_CACHE_CMP_OFFSET = (1<<2),
6812+ _SG_MTL_CACHE_CMP_ACTIVESLOT = (1<<3),
6813+} _sg_mtl_cache_cmp_result_t;
6814+
6815+typedef struct {
6816+ _sg_sref_t cur_pip;
6817+ _sg_buffer_ref_t cur_ibuf;
6818+ int cur_ibuf_offset;
6819+ _sg_mtl_cache_buf_t cur_vsbufs[_SG_MTL_MAX_STAGE_BUFFER_BINDINGS];
6820+ _sg_mtl_cache_buf_t cur_fsbufs[_SG_MTL_MAX_STAGE_BUFFER_BINDINGS];
6821+ _sg_mtl_cache_buf_t cur_csbufs[_SG_MTL_MAX_STAGE_BUFFER_BINDINGS];
6822+ _sg_mtl_cache_tex_t cur_vstexs[_SG_MTL_MAX_STAGE_TEXTURE_BINDINGS];
6823+ _sg_mtl_cache_tex_t cur_fstexs[_SG_MTL_MAX_STAGE_TEXTURE_BINDINGS];
6824+ _sg_mtl_cache_tex_t cur_cstexs[_SG_MTL_MAX_STAGE_TEXTURE_BINDINGS];
6825+ _sg_sref_t cur_vssmps[_SG_MTL_MAX_STAGE_SAMPLER_BINDINGS];
6826+ _sg_sref_t cur_fssmps[_SG_MTL_MAX_STAGE_SAMPLER_BINDINGS];
6827+ _sg_sref_t cur_cssmps[_SG_MTL_MAX_STAGE_SAMPLER_BINDINGS];
6828+} _sg_mtl_cache_t;
6829+
6830+typedef struct {
6831+ bool valid;
6832+ bool use_shared_storage_mode;
6833+ uint32_t cur_frame_rotate_index;
6834+ int ub_size;
6835+ int cur_ub_offset;
6836+ uint8_t* cur_ub_base_ptr;
6837+ _sg_mtl_cache_t cache;
6838+ _sg_mtl_idpool_t idpool;
6839+ dispatch_semaphore_t sem;
6840+ id<MTLDevice> device;
6841+ id<MTLCommandQueue> cmd_queue;
6842+ id<MTLCommandBuffer> cmd_buffer;
6843+ id<MTLRenderCommandEncoder> render_cmd_encoder;
6844+ id<MTLComputeCommandEncoder> compute_cmd_encoder;
6845+ id<CAMetalDrawable> cur_drawable;
6846+ id<MTLBuffer> uniform_buffers[SG_NUM_INFLIGHT_FRAMES];
6847+} _sg_mtl_backend_t;
6848+
6849+#elif defined(SOKOL_WGPU)
6850+
6851+#define _SG_WGPU_ROWPITCH_ALIGN (256)
6852+#define _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE (1<<16) // also see WGPULimits.maxUniformBufferBindingSize
6853+#define _SG_WGPU_MAX_BINDGROUPS (2) // 0: uniforms, 1: images, samplers, storage buffers, storage images
6854+#define _SG_WGPU_UB_BINDGROUP_INDEX (0)
6855+#define _SG_WGPU_VIEW_SMP_BINDGROUP_INDEX (1)
6856+#define _SG_WGPU_MAX_UB_BINDGROUP_ENTRIES (SG_MAX_UNIFORMBLOCK_BINDSLOTS)
6857+#define _SG_WGPU_MAX_UB_BINDGROUP_WGSL_SLOTS (2 * SG_MAX_UNIFORMBLOCK_BINDSLOTS)
6858+#define _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES (SG_MAX_VIEW_BINDSLOTS + SG_MAX_SAMPLER_BINDSLOTS)
6859+#define _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_WGSL_SLOTS (128)
6860+
6861+typedef struct _sg_buffer_s {
6862+ _sg_slot_t slot;
6863+ _sg_buffer_common_t cmn;
6864+ struct {
6865+ WGPUBuffer buf;
6866+ } wgpu;
6867+} _sg_wgpu_buffer_t;
6868+typedef _sg_wgpu_buffer_t _sg_buffer_t;
6869+
6870+typedef struct _sg_image_s {
6871+ _sg_slot_t slot;
6872+ _sg_image_common_t cmn;
6873+ struct {
6874+ WGPUTexture tex;
6875+ } wgpu;
6876+} _sg_wgpu_image_t;
6877+typedef _sg_wgpu_image_t _sg_image_t;
6878+
6879+typedef struct _sg_sampler_s {
6880+ _sg_slot_t slot;
6881+ _sg_sampler_common_t cmn;
6882+ struct {
6883+ WGPUSampler smp;
6884+ } wgpu;
6885+} _sg_wgpu_sampler_t;
6886+typedef _sg_wgpu_sampler_t _sg_sampler_t;
6887+
6888+typedef struct {
6889+ WGPUShaderModule module;
6890+ _sg_str_t entry;
6891+} _sg_wgpu_shader_func_t;
6892+
6893+typedef struct _sg_shader_s {
6894+ _sg_slot_t slot;
6895+ _sg_shader_common_t cmn;
6896+ struct {
6897+ _sg_wgpu_shader_func_t vertex_func;
6898+ _sg_wgpu_shader_func_t fragment_func;
6899+ _sg_wgpu_shader_func_t compute_func;
6900+ WGPUBindGroupLayout bgl_ub;
6901+ WGPUBindGroup bg_ub;
6902+ WGPUBindGroupLayout bgl_view_smp;
6903+ // a mapping of sokol-gfx bind slots to setBindGroup dynamic-offset-array indices
6904+ uint8_t ub_num_dynoffsets;
6905+ uint8_t ub_dynoffsets[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
6906+ // indexed by sokol-gfx bind slot:
6907+ uint8_t ub_grp0_bnd_n[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
6908+ uint8_t view_grp1_bnd_n[SG_MAX_VIEW_BINDSLOTS];
6909+ uint8_t smp_grp1_bnd_n[SG_MAX_SAMPLER_BINDSLOTS];
6910+ } wgpu;
6911+} _sg_wgpu_shader_t;
6912+typedef _sg_wgpu_shader_t _sg_shader_t;
6913+
6914+typedef struct _sg_pipeline_s {
6915+ _sg_slot_t slot;
6916+ _sg_pipeline_common_t cmn;
6917+ struct {
6918+ WGPURenderPipeline rpip;
6919+ WGPUComputePipeline cpip;
6920+ WGPUColor blend_color;
6921+ } wgpu;
6922+} _sg_wgpu_pipeline_t;
6923+typedef _sg_wgpu_pipeline_t _sg_pipeline_t;
6924+
6925+typedef struct _sg_view_s {
6926+ _sg_slot_t slot;
6927+ _sg_view_common_t cmn;
6928+ struct {
6929+ WGPUTextureView view;
6930+ } wgpu;
6931+} _sg_wgpu_view_t;
6932+typedef _sg_wgpu_view_t _sg_view_t;
6933+
6934+// a pool of per-frame uniform buffers
6935+typedef struct {
6936+ uint32_t num_bytes;
6937+ uint32_t offset; // current offset into buf
6938+ uint8_t* staging; // intermediate buffer for uniform data updates
6939+ WGPUBuffer buf; // the GPU-side uniform buffer
6940+ bool dirty;
6941+ uint32_t bind_offsets[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; // NOTE: index is sokol-gfx ub slot index!
6942+} _sg_wgpu_uniform_system_t;
6943+
6944+typedef struct {
6945+ uint32_t id;
6946+} _sg_wgpu_bindgroup_handle_t;
6947+
6948+typedef enum {
6949+ _SG_WGPU_BINDGROUPSCACHEITEMTYPE_NONE = 0,
6950+ _SG_WGPU_BINDGROUPSCACHEITEMTYPE_VIEW = 1,
6951+ _SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER = 2,
6952+ _SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE = 3,
6953+} _sg_wgpu_bindgroups_cache_item_type_t;
6954+
6955+#define _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS (1 + _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES)
6956+typedef struct {
6957+ uint64_t hash;
6958+ // the format of cache key items is BBTCCCCCIIIIIIII
6959+ // where
6960+ // - BB: 8 bits WGPU binding
6961+ // - T: 2 bits _sg_wgpu_bindgroups_cache_item_type_t
6962+ // - CCCCC: 22 bits slot.uninit_count
6963+ // - IIIIIIII: 32 bits slot.id
6964+ //
6965+ // where the item type is a per-resource-type bit pattern
6966+ uint64_t items[_SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS];
6967+} _sg_wgpu_bindgroups_cache_key_t;
6968+
6969+typedef struct {
6970+ uint32_t num; // must be 2^n
6971+ uint32_t index_mask; // mask to turn hash into valid index
6972+ _sg_wgpu_bindgroup_handle_t* items;
6973+} _sg_wgpu_bindgroups_cache_t;
6974+
6975+typedef struct {
6976+ _sg_slot_t slot;
6977+ WGPUBindGroup bindgroup;
6978+ _sg_wgpu_bindgroups_cache_key_t key;
6979+} _sg_wgpu_bindgroup_t;
6980+
6981+typedef struct {
6982+ _sg_pool_t pool;
6983+ _sg_wgpu_bindgroup_t* bindgroups;
6984+} _sg_wgpu_bindgroups_pool_t;
6985+
6986+typedef struct {
6987+ struct {
6988+ sg_buffer buffer;
6989+ uint64_t offset;
6990+ } vbs[SG_MAX_VERTEXBUFFER_BINDSLOTS];
6991+ struct {
6992+ sg_buffer buffer;
6993+ uint64_t offset;
6994+ } ib;
6995+ _sg_wgpu_bindgroup_handle_t bg;
6996+} _sg_wgpu_bindings_cache_t;
6997+
6998+// the WGPU backend state
6999+typedef struct {
7000+ bool valid;
7001+ WGPUDevice dev;
7002+ WGPULimits limits;
7003+ WGPUQueue queue;
7004+ WGPUCommandEncoder cmd_enc;
7005+ WGPURenderPassEncoder rpass_enc;
7006+ WGPUComputePassEncoder cpass_enc;
7007+ _sg_wgpu_uniform_system_t uniform;
7008+ _sg_wgpu_bindings_cache_t bindings_cache;
7009+ _sg_wgpu_bindgroups_cache_t bindgroups_cache;
7010+ _sg_wgpu_bindgroups_pool_t bindgroups_pool;
7011+} _sg_wgpu_backend_t;
7012+
7013+#elif defined(SOKOL_VULKAN)
7014+
7015+#define _SG_VK_MAX_UNIFORM_UPDATE_SIZE (1<<16)
7016+#define _SG_VK_NUM_DESCRIPTORSETS (2) // 0: uniforms, 1: images, samplers, storage buffers, storage images
7017+#define _SG_VK_UB_DESCRIPTORSET_INDEX (0)
7018+#define _SG_VK_VIEW_SMP_DESCRIPTORSET_INDEX (1)
7019+#define _SG_VK_MAX_UB_DESCRIPTORSET_ENTRIES (SG_MAX_UNIFORMBLOCK_BINDSLOTS)
7020+#define _SG_VK_MAX_UB_DESCRIPTORSET_SLOTS (2 * SG_MAX_UNIFORMBLOCK_BINDSLOTS)
7021+#define _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_ENTRIES (SG_MAX_VIEW_BINDSLOTS + SG_MAX_SAMPLER_BINDSLOTS)
7022+#define _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_SLOTS (128)
7023+#define _SG_VK_MAX_DESCRIPTOR_DATA_SIZE (256) // FIXME: llvmpipe needs 280 bytes, do we need to care about that?
7024+
7025+typedef enum {
7026+ _SG_VK_MEMTYPE_STORAGE_BUFFER,
7027+ _SG_VK_MEMTYPE_GENERIC_BUFFER,
7028+ _SG_VK_MEMTYPE_IMAGE,
7029+ _SG_VK_MEMTYPE_STAGING_COPY,
7030+ _SG_VK_MEMTYPE_STAGING_STREAM,
7031+ _SG_VK_MEMTYPE_UNIFORMS,
7032+ _SG_VK_MEMTYPE_DESCRIPTORS,
7033+} _sg_vk_memtype_t;
7034+
7035+typedef void (*_sg_vk_delete_queue_destructor_t)(void* obj);
7036+
7037+typedef struct {
7038+ _sg_vk_delete_queue_destructor_t destructor;
7039+ void* obj;
7040+} _sg_vk_delete_queue_item_t;
7041+
7042+typedef struct {
7043+ uint32_t index;
7044+ uint32_t num;
7045+ _sg_vk_delete_queue_item_t* items;
7046+} _sg_vk_delete_queue_t;
7047+
7048+typedef enum {
7049+ _SG_VK_ACCESS_NONE = (0), // initial state for new resources
7050+ _SG_VK_ACCESS_STAGING = (1<<0),
7051+ _SG_VK_ACCESS_VERTEXBUFFER = (1<<1),
7052+ _SG_VK_ACCESS_INDEXBUFFER = (1<<2),
7053+ _SG_VK_ACCESS_STORAGEBUFFER_RO = (1<<3),
7054+ _SG_VK_ACCESS_STORAGEBUFFER_RW = (1<<4),
7055+ _SG_VK_ACCESS_TEXTURE = (1<<5),
7056+ _SG_VK_ACCESS_STORAGEIMAGE = (1<<6),
7057+ _SG_VK_ACCESS_COLOR_ATTACHMENT = (1<<7),
7058+ _SG_VK_ACCESS_RESOLVE_ATTACHMENT = (1<<8),
7059+ _SG_VK_ACCESS_DEPTH_ATTACHMENT = (1<<9),
7060+ _SG_VK_ACCESS_STENCIL_ATTACHMENT = (1<<10),
7061+ _SG_VK_ACCESS_DISCARD = (1<<11), // in combination with attachments
7062+ _SG_VK_ACCESS_PRESENT = (1<<12),
7063+} _sg_vk_access_bits_t;
7064+typedef int _sg_vk_access_t;
7065+
7066+typedef struct _sg_buffer_s {
7067+ _sg_slot_t slot;
7068+ _sg_buffer_common_t cmn;
7069+ struct {
7070+ VkBuffer buf;
7071+ VkDeviceMemory mem;
7072+ VkDeviceAddress dev_addr; // only valid for storage buffers
7073+ _sg_vk_access_t cur_access;
7074+ } vk;
7075+} _sg_vk_buffer_t;
7076+typedef _sg_vk_buffer_t _sg_buffer_t;
7077+
7078+typedef struct _sg_image_s {
7079+ _sg_slot_t slot;
7080+ _sg_image_common_t cmn;
7081+ struct {
7082+ VkImage img;
7083+ VkDeviceMemory mem;
7084+ _sg_vk_access_t cur_access;
7085+ } vk;
7086+} _sg_vk_image_t;
7087+typedef _sg_vk_image_t _sg_image_t;
7088+
7089+typedef struct _sg_sampler_s {
7090+ _sg_slot_t slot;
7091+ _sg_sampler_common_t cmn;
7092+ struct {
7093+ VkSampler smp;
7094+ size_t descriptor_size;
7095+ uint8_t descriptor_data[_SG_VK_MAX_DESCRIPTOR_DATA_SIZE];
7096+ } vk;
7097+} _sg_vk_sampler_t;
7098+typedef _sg_vk_sampler_t _sg_sampler_t;
7099+
7100+typedef struct {
7101+ VkShaderModule module;
7102+ _sg_str_t entry;
7103+} _sg_vk_shader_func_t;
7104+
7105+typedef struct _sg_shader_s {
7106+ _sg_slot_t slot;
7107+ _sg_shader_common_t cmn;
7108+ struct {
7109+ _sg_vk_shader_func_t vertex_func;
7110+ _sg_vk_shader_func_t fragment_func;
7111+ _sg_vk_shader_func_t compute_func;
7112+ VkDescriptorSetLayout ub_dsl;
7113+ VkDeviceSize ub_dset_size;
7114+ VkDescriptorSetLayout view_smp_dsl;
7115+ VkDeviceSize view_smp_dset_size;
7116+ VkPipelineLayout pip_layout;
7117+ // indexed by sokol-gfx bind-slot
7118+ uint8_t ub_set0_bnd_n[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
7119+ uint8_t view_set1_bnd_n[SG_MAX_VIEW_BINDSLOTS];
7120+ uint8_t smp_set1_bnd_n[SG_MAX_SAMPLER_BINDSLOTS];
7121+ // relative descriptor offsets to start of descriptor set in descriptor buffer
7122+ uint16_t ub_dset_offsets[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
7123+ uint16_t view_dset_offsets[SG_MAX_VIEW_BINDSLOTS];
7124+ uint16_t smp_dset_offsets[SG_MAX_SAMPLER_BINDSLOTS];
7125+ } vk;
7126+} _sg_vk_shader_t;
7127+typedef _sg_vk_shader_t _sg_shader_t;
7128+
7129+typedef struct _sg_pipeline_s {
7130+ _sg_slot_t slot;
7131+ _sg_pipeline_common_t cmn;
7132+ struct {
7133+ VkPipeline pip;
7134+ } vk;
7135+} _sg_vk_pipeline_t;
7136+typedef _sg_vk_pipeline_t _sg_pipeline_t;
7137+
7138+typedef struct _sg_view_s {
7139+ _sg_slot_t slot;
7140+ _sg_view_common_t cmn;
7141+ struct {
7142+ VkImageView img_view;
7143+ size_t descriptor_size;
7144+ uint8_t descriptor_data[_SG_VK_MAX_DESCRIPTOR_DATA_SIZE];
7145+ } vk;
7146+} _sg_vk_view_t;
7147+typedef _sg_vk_view_t _sg_view_t;
7148+
7149+// a double-buffer cpu-write / gpu-read buffer
7150+#define _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT (0xFFFFFFFF)
7151+typedef struct {
7152+ uint32_t size; // buffer size
7153+ uint32_t align; // required buffer offset alignemnt
7154+ uint32_t offset; // current offset into buffer
7155+ VkBuffer cur_buf; // currently mapped buffer
7156+ void* cur_mem_ptr; // current pointer into currently mapped buffer
7157+ VkDeviceAddress cur_dev_addr; // current buffer device address (only valid for some buffer types)
7158+ bool overflown; // true when in overflown state
7159+ struct {
7160+ VkBuffer buf;
7161+ VkDeviceMemory mem;
7162+ VkDeviceAddress dev_addr; // only valid for some buffer types!
7163+ void* mem_ptr;
7164+ } slots[SG_NUM_INFLIGHT_FRAMES];
7165+} _sg_vk_shared_buffer_t;
7166+
7167+typedef struct {
7168+ bool valid;
7169+ VkInstance instance;
7170+ VkPhysicalDevice phys_dev;
7171+ VkDevice dev;
7172+ VkQueue queue;
7173+ uint32_t queue_family_index;
7174+ sg_vulkan_swapchain swapchain;
7175+ VkSemaphore present_complete_sem;
7176+ VkSemaphore render_finished_sem;
7177+
7178+ // extension function pointers
7179+ struct {
7180+ PFN_vkSetDebugUtilsObjectNameEXT set_debug_utils_object_name_ext;
7181+ PFN_vkGetDescriptorSetLayoutSizeEXT get_descriptor_set_layout_size;
7182+ PFN_vkGetDescriptorSetLayoutBindingOffsetEXT get_descriptor_set_layout_binding_offset;
7183+ PFN_vkGetDescriptorEXT get_descriptor;
7184+ PFN_vkCmdBindDescriptorBuffersEXT cmd_bind_descriptor_buffers;
7185+ PFN_vkCmdSetDescriptorBufferOffsetsEXT cmd_set_descriptor_buffer_offsets;
7186+ } ext;
7187+
7188+ uint32_t frame_slot;
7189+ struct {
7190+ VkCommandPool cmd_pool;
7191+ VkCommandBuffer cmd_buf;
7192+ VkCommandBuffer stream_cmd_buf;
7193+ struct {
7194+ VkFence fence;
7195+ VkCommandBuffer command_buffer;
7196+ VkCommandBuffer stream_command_buffer;
7197+ _sg_vk_delete_queue_t delete_queue;
7198+ } slot[SG_NUM_INFLIGHT_FRAMES];
7199+ } frame;
7200+ // staging system
7201+ struct {
7202+ // staging system for immutable and dynamic resources, generally causes a stall
7203+ struct {
7204+ VkCommandPool cmd_pool;
7205+ VkCommandBuffer cmd_buf;
7206+ uint32_t size;
7207+ VkBuffer buf;
7208+ VkDeviceMemory mem;
7209+ } copy;
7210+ // staging buffer for per-frame streaming updates
7211+ _sg_vk_shared_buffer_t stream;
7212+ } stage;
7213+ // uniform update system
7214+ struct {
7215+ bool dirty;
7216+ _sg_vk_shared_buffer_t dbuf; // descriptor buffer
7217+ VkDescriptorAddressInfoEXT addr_info[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
7218+ VkDescriptorGetInfoEXT get_info[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
7219+ size_t dset_cache_size;
7220+ uint8_t* dset_cache;
7221+ } uniforms;
7222+ // resource binding system (using descriptor buffers)
7223+ _sg_vk_shared_buffer_t bind;
7224+ // hazard tracking system for buffers and images
7225+ struct {
7226+ _sg_track_t buffers;
7227+ _sg_track_t images;
7228+ } track;
7229+ // device properties and features (initialized at startup)
7230+ VkPhysicalDeviceProperties2 dev_props;
7231+ VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer_props;
7232+ VkPhysicalDeviceFeatures2 dev_features;
7233+} _sg_vk_backend_t;
7234+
7235+#endif // SOKOL_VULKAN
7236+
7237+// this *MUST* remain 0
7238+#define _SG_INVALID_SLOT_INDEX (0)
7239+
7240+typedef struct _sg_pools_s {
7241+ _sg_pool_t buffer_pool;
7242+ _sg_pool_t image_pool;
7243+ _sg_pool_t sampler_pool;
7244+ _sg_pool_t shader_pool;
7245+ _sg_pool_t pipeline_pool;
7246+ _sg_pool_t view_pool;
7247+ _sg_buffer_t* buffers;
7248+ _sg_image_t* images;
7249+ _sg_sampler_t* samplers;
7250+ _sg_shader_t* shaders;
7251+ _sg_pipeline_t* pipelines;
7252+ _sg_view_t* views;
7253+} _sg_pools_t;
7254+
7255+typedef struct {
7256+ int num; // number of allocated commit listener items
7257+ int upper; // the current upper index (no valid items past this point)
7258+ sg_commit_listener* items;
7259+} _sg_commit_listeners_t;
7260+
7261+// resolved pass attachments struct
7262+typedef struct {
7263+ bool empty;
7264+ int num_color_views;
7265+ _sg_view_t* color_views[SG_MAX_COLOR_ATTACHMENTS];
7266+ _sg_view_t* resolve_views[SG_MAX_COLOR_ATTACHMENTS];
7267+ _sg_view_t* ds_view;
7268+} _sg_attachments_ptrs_t;
7269+
7270+// resolved resource bindings struct
7271+typedef struct {
7272+ _sg_pipeline_t* pip;
7273+ int vb_offsets[SG_MAX_VERTEXBUFFER_BINDSLOTS];
7274+ int ib_offset;
7275+ _sg_buffer_t* vbs[SG_MAX_VERTEXBUFFER_BINDSLOTS];
7276+ _sg_buffer_t* ib;
7277+ _sg_view_t* views[SG_MAX_VIEW_BINDSLOTS];
7278+ _sg_sampler_t* smps[SG_MAX_SAMPLER_BINDSLOTS];
7279+} _sg_bindings_ptrs_t;
7280+
7281+typedef struct {
7282+ bool sample;
7283+ bool filter;
7284+ bool render;
7285+ bool blend;
7286+ bool msaa;
7287+ bool depth;
7288+ bool read;
7289+ bool write;
7290+} _sg_pixelformat_info_t;
7291+
7292+typedef struct {
7293+ bool valid;
7294+ sg_desc desc; // original desc with default values patched in
7295+ uint32_t frame_index;
7296+ struct {
7297+ bool valid;
7298+ bool in_pass;
7299+ bool is_compute;
7300+ _sg_dimi_t dim;
7301+ sg_attachments atts;
7302+ sg_pass_action action;
7303+ struct {
7304+ bool invalid;
7305+ sg_pixel_format color_fmt;
7306+ sg_pixel_format depth_fmt;
7307+ int sample_count;
7308+ } swapchain;
7309+ } cur_pass;
7310+ _sg_pipeline_ref_t cur_pip;
7311+ bool next_draw_valid;
7312+ bool use_indexed_draw;
7313+ bool use_instanced_draw;
7314+ uint32_t required_bindings_and_uniforms; // used to check that bindings and uniforms are applied after applying pipeline
7315+ uint32_t applied_bindings_and_uniforms; // bits 0..7: uniform blocks, bit 8: bindings
7316+ #if defined(SOKOL_DEBUG)
7317+ sg_log_item validate_error;
7318+ #endif
7319+ _sg_pools_t pools;
7320+ sg_backend backend;
7321+ sg_features features;
7322+ sg_limits limits;
7323+ _sg_pixelformat_info_t formats[_SG_PIXELFORMAT_NUM];
7324+ bool stats_enabled;
7325+ sg_stats stats;
7326+ #if defined(_SOKOL_ANY_GL)
7327+ _sg_gl_backend_t gl;
7328+ #elif defined(SOKOL_METAL)
7329+ _sg_mtl_backend_t mtl;
7330+ #elif defined(SOKOL_D3D11)
7331+ _sg_d3d11_backend_t d3d11;
7332+ #elif defined(SOKOL_WGPU)
7333+ _sg_wgpu_backend_t wgpu;
7334+ #elif defined(SOKOL_VULKAN)
7335+ _sg_vk_backend_t vk;
7336+ #endif
7337+ #if defined(SOKOL_TRACE_HOOKS)
7338+ sg_trace_hooks hooks;
7339+ #endif
7340+ _sg_commit_listeners_t commit_listeners;
7341+} _sg_state_t;
7342+static _sg_state_t _sg;
7343+
7344+// ██ ██████ ██████ ██████ ██ ███ ██ ██████
7345+// ██ ██ ██ ██ ██ ██ ████ ██ ██
7346+// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███
7347+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
7348+// ███████ ██████ ██████ ██████ ██ ██ ████ ██████
7349+//
7350+// >>logging
7351+#if defined(SOKOL_DEBUG)
7352+#define _SG_LOGITEM_XMACRO(item,msg) #item ": " msg,
7353+static const char* _sg_log_messages[] = {
7354+ _SG_LOG_ITEMS
7355+};
7356+#undef _SG_LOGITEM_XMACRO
7357+#endif // SOKOL_DEBUG
7358+
7359+#define _SG_PANIC(code) _sg_log(SG_LOGITEM_ ##code, 0, 0, __LINE__)
7360+#define _SG_ERROR(code) _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__)
7361+#define _SG_WARN(code) _sg_log(SG_LOGITEM_ ##code, 2, 0, __LINE__)
7362+#define _SG_INFO(code) _sg_log(SG_LOGITEM_ ##code, 3, 0, __LINE__)
7363+#define _SG_LOGMSG(code,msg) _sg_log(SG_LOGITEM_ ##code, 3, msg, __LINE__)
7364+#define _SG_VALIDATE(cond,code) if (!(cond)){ _sg.validate_error = SG_LOGITEM_ ##code; _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__); }
7365+
7366+static void _sg_log(sg_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) {
7367+ if (_sg.desc.logger.func) {
7368+ const char* filename = 0;
7369+ #if defined(SOKOL_DEBUG)
7370+ filename = __FILE__;
7371+ if (0 == msg) {
7372+ msg = _sg_log_messages[log_item];
7373+ }
7374+ #endif
7375+ _sg.desc.logger.func("sg", log_level, (uint32_t)log_item, msg, line_nr, filename, _sg.desc.logger.user_data);
7376+ } else {
7377+ // for log level PANIC it would be 'undefined behaviour' to continue
7378+ if (log_level == 0) {
7379+ abort();
7380+ }
7381+ }
7382+}
7383+
7384+// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██
7385+// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██
7386+// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████
7387+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
7388+// ██ ██ ███████ ██ ██ ██████ ██ ██ ██
7389+//
7390+// >>memory
7391+
7392+_SOKOL_PRIVATE int _sg_roundup(int val, int round_to) {
7393+ return (val+(round_to-1)) & ~(round_to-1);
7394+}
7395+
7396+_SOKOL_PRIVATE uint32_t _sg_roundup_u32(uint32_t val, uint32_t round_to) {
7397+ return (val+(round_to-1)) & ~(round_to-1);
7398+}
7399+
7400+_SOKOL_PRIVATE uint64_t _sg_roundup_u64(uint64_t val, uint64_t round_to) {
7401+ return (val+(round_to-1)) & ~(round_to-1);
7402+}
7403+
7404+_SOKOL_PRIVATE bool _sg_multiple_u64(uint64_t val, uint64_t of) {
7405+ return (val & (of-1)) == 0;
7406+}
7407+
7408+// a helper macro to clear a struct with potentially ARC'ed ObjC references
7409+#if defined(SOKOL_METAL)
7410+ #if defined(__cplusplus)
7411+ #define _SG_CLEAR_ARC_STRUCT(type, item) { item = type(); }
7412+ #else
7413+ #define _SG_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; }
7414+ #endif
7415+#else
7416+ #define _SG_CLEAR_ARC_STRUCT(type, item) { _sg_clear(&item, sizeof(item)); }
7417+#endif
7418+
7419+_SOKOL_PRIVATE void _sg_clear(void* ptr, size_t size) {
7420+ SOKOL_ASSERT(ptr && (size > 0));
7421+ memset(ptr, 0, size);
7422+}
7423+
7424+_SOKOL_PRIVATE void* _sg_malloc(size_t size) {
7425+ SOKOL_ASSERT(size > 0);
7426+ void* ptr;
7427+ if (_sg.desc.allocator.alloc_fn) {
7428+ ptr = _sg.desc.allocator.alloc_fn(size, _sg.desc.allocator.user_data);
7429+ } else {
7430+ ptr = malloc(size);
7431+ }
7432+ if (0 == ptr) {
7433+ _SG_PANIC(MALLOC_FAILED);
7434+ }
7435+ return ptr;
7436+}
7437+
7438+_SOKOL_PRIVATE void* _sg_malloc_clear(size_t size) {
7439+ void* ptr = _sg_malloc(size);
7440+ _sg_clear(ptr, size);
7441+ return ptr;
7442+}
7443+
7444+_SOKOL_PRIVATE void _sg_free(void* ptr) {
7445+ if (_sg.desc.allocator.free_fn) {
7446+ _sg.desc.allocator.free_fn(ptr, _sg.desc.allocator.user_data);
7447+ } else {
7448+ free(ptr);
7449+ }
7450+}
7451+
7452+_SOKOL_PRIVATE bool _sg_strempty(const _sg_str_t* str) {
7453+ return 0 == str->buf[0];
7454+}
7455+
7456+_SOKOL_PRIVATE const char* _sg_strptr(const _sg_str_t* str) {
7457+ return &str->buf[0];
7458+}
7459+
7460+_SOKOL_PRIVATE void _sg_strcpy(_sg_str_t* dst, const char* src) {
7461+ SOKOL_ASSERT(dst);
7462+ if (src) {
7463+ #if defined(_MSC_VER)
7464+ strncpy_s(dst->buf, _SG_STRING_SIZE, src, (_SG_STRING_SIZE-1));
7465+ #else
7466+ strncpy(dst->buf, src, _SG_STRING_SIZE);
7467+ #endif
7468+ dst->buf[_SG_STRING_SIZE-1] = 0;
7469+ } else {
7470+ _sg_clear(dst->buf, _SG_STRING_SIZE);
7471+ }
7472+}
7473+
7474+// ██████ ██████ ██████ ██
7475+// ██ ██ ██ ██ ██ ██ ██
7476+// ██████ ██ ██ ██ ██ ██
7477+// ██ ██ ██ ██ ██ ██
7478+// ██ ██████ ██████ ███████
7479+//
7480+// >>pool
7481+_SOKOL_PRIVATE void _sg_pool_init(_sg_pool_t* pool, int num) {
7482+ SOKOL_ASSERT(pool && (num >= 1));
7483+ // slot 0 is reserved for the 'invalid id', so bump the pool size by 1
7484+ pool->size = num + 1;
7485+ pool->queue_top = 0;
7486+ // generation counters indexable by pool slot index, slot 0 is reserved
7487+ size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size;
7488+ pool->gen_ctrs = (uint32_t*)_sg_malloc_clear(gen_ctrs_size);
7489+ // it's not a bug to only reserve 'num' here
7490+ pool->free_queue = (int*) _sg_malloc_clear(sizeof(int) * (size_t)num);
7491+ // never allocate the zero-th pool item since the invalid id is 0
7492+ for (int i = pool->size-1; i >= 1; i--) {
7493+ pool->free_queue[pool->queue_top++] = i;
7494+ }
7495+}
7496+
7497+_SOKOL_PRIVATE void _sg_pool_discard(_sg_pool_t* pool) {
7498+ SOKOL_ASSERT(pool);
7499+ SOKOL_ASSERT(pool->free_queue);
7500+ _sg_free(pool->free_queue);
7501+ pool->free_queue = 0;
7502+ SOKOL_ASSERT(pool->gen_ctrs);
7503+ _sg_free(pool->gen_ctrs);
7504+ pool->gen_ctrs = 0;
7505+ pool->size = 0;
7506+ pool->queue_top = 0;
7507+}
7508+
7509+_SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool) {
7510+ SOKOL_ASSERT(pool);
7511+ SOKOL_ASSERT(pool->free_queue);
7512+ if (pool->queue_top > 0) {
7513+ int slot_index = pool->free_queue[--pool->queue_top];
7514+ SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size));
7515+ return slot_index;
7516+ } else {
7517+ // pool exhausted
7518+ return _SG_INVALID_SLOT_INDEX;
7519+ }
7520+}
7521+
7522+_SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) {
7523+ SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size));
7524+ SOKOL_ASSERT(pool);
7525+ SOKOL_ASSERT(pool->free_queue);
7526+ SOKOL_ASSERT(pool->queue_top < pool->size);
7527+ #ifdef SOKOL_DEBUG
7528+ // debug check against double-free
7529+ for (int i = 0; i < pool->queue_top; i++) {
7530+ SOKOL_ASSERT(pool->free_queue[i] != slot_index);
7531+ }
7532+ #endif
7533+ pool->free_queue[pool->queue_top++] = slot_index;
7534+ SOKOL_ASSERT(pool->queue_top <= (pool->size-1));
7535+}
7536+
7537+_SOKOL_PRIVATE void _sg_slot_reset(_sg_slot_t* slot) {
7538+ SOKOL_ASSERT(slot);
7539+ _sg_clear(slot, sizeof(_sg_slot_t));
7540+}
7541+
7542+_SOKOL_PRIVATE void _sg_reset_buffer_to_alloc_state(_sg_buffer_t* buf) {
7543+ SOKOL_ASSERT(buf);
7544+ _sg_slot_t slot = buf->slot;
7545+ _sg_clear(buf, sizeof(*buf));
7546+ buf->slot = slot;
7547+ buf->slot.uninit_count += 1;
7548+ buf->slot.state = SG_RESOURCESTATE_ALLOC;
7549+}
7550+
7551+_SOKOL_PRIVATE void _sg_reset_image_to_alloc_state(_sg_image_t* img) {
7552+ SOKOL_ASSERT(img);
7553+ _sg_slot_t slot = img->slot;
7554+ _sg_clear(img, sizeof(*img));
7555+ img->slot = slot;
7556+ img->slot.uninit_count += 1;
7557+ img->slot.state = SG_RESOURCESTATE_ALLOC;
7558+}
7559+
7560+_SOKOL_PRIVATE void _sg_reset_sampler_to_alloc_state(_sg_sampler_t* smp) {
7561+ SOKOL_ASSERT(smp);
7562+ _sg_slot_t slot = smp->slot;
7563+ _sg_clear(smp, sizeof(*smp));
7564+ smp->slot = slot;
7565+ smp->slot.uninit_count += 1;
7566+ smp->slot.state = SG_RESOURCESTATE_ALLOC;
7567+}
7568+
7569+_SOKOL_PRIVATE void _sg_reset_shader_to_alloc_state(_sg_shader_t* shd) {
7570+ SOKOL_ASSERT(shd);
7571+ _sg_slot_t slot = shd->slot;
7572+ _sg_clear(shd, sizeof(*shd));
7573+ shd->slot = slot;
7574+ shd->slot.uninit_count += 1;
7575+ shd->slot.state = SG_RESOURCESTATE_ALLOC;
7576+}
7577+
7578+_SOKOL_PRIVATE void _sg_reset_pipeline_to_alloc_state(_sg_pipeline_t* pip) {
7579+ SOKOL_ASSERT(pip);
7580+ _sg_slot_t slot = pip->slot;
7581+ _sg_clear(pip, sizeof(*pip));
7582+ pip->slot = slot;
7583+ pip->slot.uninit_count += 1;
7584+ pip->slot.state = SG_RESOURCESTATE_ALLOC;
7585+}
7586+
7587+_SOKOL_PRIVATE void _sg_reset_view_to_alloc_state(_sg_view_t* view) {
7588+ SOKOL_ASSERT(view);
7589+ _sg_slot_t slot = view->slot;
7590+ _sg_clear(view, sizeof(*view));
7591+ view->slot = slot;
7592+ view->slot.uninit_count += 1;
7593+ view->slot.state = SG_RESOURCESTATE_ALLOC;
7594+}
7595+
7596+_SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) {
7597+ SOKOL_ASSERT(p);
7598+ SOKOL_ASSERT(desc);
7599+ // note: the pools here will have an additional item, since slot 0 is reserved
7600+ SOKOL_ASSERT((desc->buffer_pool_size > 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE));
7601+ _sg_pool_init(&p->buffer_pool, desc->buffer_pool_size);
7602+ size_t buffer_pool_byte_size = sizeof(_sg_buffer_t) * (size_t)p->buffer_pool.size;
7603+ p->buffers = (_sg_buffer_t*) _sg_malloc_clear(buffer_pool_byte_size);
7604+
7605+ SOKOL_ASSERT((desc->image_pool_size > 0) && (desc->image_pool_size < _SG_MAX_POOL_SIZE));
7606+ _sg_pool_init(&p->image_pool, desc->image_pool_size);
7607+ size_t image_pool_byte_size = sizeof(_sg_image_t) * (size_t)p->image_pool.size;
7608+ p->images = (_sg_image_t*) _sg_malloc_clear(image_pool_byte_size);
7609+
7610+ SOKOL_ASSERT((desc->sampler_pool_size > 0) && (desc->sampler_pool_size < _SG_MAX_POOL_SIZE));
7611+ _sg_pool_init(&p->sampler_pool, desc->sampler_pool_size);
7612+ size_t sampler_pool_byte_size = sizeof(_sg_sampler_t) * (size_t)p->sampler_pool.size;
7613+ p->samplers = (_sg_sampler_t*) _sg_malloc_clear(sampler_pool_byte_size);
7614+
7615+ SOKOL_ASSERT((desc->shader_pool_size > 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE));
7616+ _sg_pool_init(&p->shader_pool, desc->shader_pool_size);
7617+ size_t shader_pool_byte_size = sizeof(_sg_shader_t) * (size_t)p->shader_pool.size;
7618+ p->shaders = (_sg_shader_t*) _sg_malloc_clear(shader_pool_byte_size);
7619+
7620+ SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SG_MAX_POOL_SIZE));
7621+ _sg_pool_init(&p->pipeline_pool, desc->pipeline_pool_size);
7622+ size_t pipeline_pool_byte_size = sizeof(_sg_pipeline_t) * (size_t)p->pipeline_pool.size;
7623+ p->pipelines = (_sg_pipeline_t*) _sg_malloc_clear(pipeline_pool_byte_size);
7624+
7625+ SOKOL_ASSERT((desc->view_pool_size > 0) && (desc->view_pool_size < _SG_MAX_POOL_SIZE));
7626+ _sg_pool_init(&p->view_pool, desc->view_pool_size);
7627+ size_t view_pool_byte_size = sizeof(_sg_view_t) * (size_t)p->view_pool.size;
7628+ p->views = (_sg_view_t*) _sg_malloc_clear(view_pool_byte_size);
7629+}
7630+
7631+_SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) {
7632+ SOKOL_ASSERT(p);
7633+ _sg_free(p->views); p->views = 0;
7634+ _sg_free(p->pipelines); p->pipelines = 0;
7635+ _sg_free(p->shaders); p->shaders = 0;
7636+ _sg_free(p->samplers); p->samplers = 0;
7637+ _sg_free(p->images); p->images = 0;
7638+ _sg_free(p->buffers); p->buffers = 0;
7639+ _sg_pool_discard(&p->view_pool);
7640+ _sg_pool_discard(&p->pipeline_pool);
7641+ _sg_pool_discard(&p->shader_pool);
7642+ _sg_pool_discard(&p->sampler_pool);
7643+ _sg_pool_discard(&p->image_pool);
7644+ _sg_pool_discard(&p->buffer_pool);
7645+}
7646+
7647+/* allocate the slot at slot_index:
7648+ - bump the slot's generation counter
7649+ - create a resource id from the generation counter and slot index
7650+ - set the slot's id to this id
7651+ - set the slot's state to ALLOC
7652+ - return the resource id
7653+*/
7654+_SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index) {
7655+ /* FIXME: add handling for an overflowing generation counter,
7656+ for now, just overflow (another option is to disable
7657+ the slot)
7658+ */
7659+ SOKOL_ASSERT(pool && pool->gen_ctrs);
7660+ SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size));
7661+ SOKOL_ASSERT(slot->id == SG_INVALID_ID);
7662+ SOKOL_ASSERT(slot->state == SG_RESOURCESTATE_INITIAL);
7663+ uint32_t ctr = ++pool->gen_ctrs[slot_index];
7664+ slot->id = (ctr<<_SG_SLOT_SHIFT)|(slot_index & _SG_SLOT_MASK);
7665+ slot->state = SG_RESOURCESTATE_ALLOC;
7666+ return slot->id;
7667+}
7668+
7669+// extract slot index from id
7670+_SOKOL_PRIVATE int _sg_slot_index(uint32_t id) {
7671+ int slot_index = (int) (id & _SG_SLOT_MASK);
7672+ SOKOL_ASSERT(_SG_INVALID_SLOT_INDEX != slot_index);
7673+ return slot_index;
7674+}
7675+
7676+// returns pointer to resource by id without matching id check
7677+_SOKOL_PRIVATE _sg_buffer_t* _sg_buffer_at(uint32_t buf_id) {
7678+ SOKOL_ASSERT(SG_INVALID_ID != buf_id);
7679+ int slot_index = _sg_slot_index(buf_id);
7680+ SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.buffer_pool.size));
7681+ return &_sg.pools.buffers[slot_index];
7682+}
7683+
7684+_SOKOL_PRIVATE _sg_image_t* _sg_image_at(uint32_t img_id) {
7685+ SOKOL_ASSERT(SG_INVALID_ID != img_id);
7686+ int slot_index = _sg_slot_index(img_id);
7687+ SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.image_pool.size));
7688+ return &_sg.pools.images[slot_index];
7689+}
7690+
7691+_SOKOL_PRIVATE _sg_sampler_t* _sg_sampler_at(uint32_t smp_id) {
7692+ SOKOL_ASSERT(SG_INVALID_ID != smp_id);
7693+ int slot_index = _sg_slot_index(smp_id);
7694+ SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.sampler_pool.size));
7695+ return &_sg.pools.samplers[slot_index];
7696+}
7697+
7698+_SOKOL_PRIVATE _sg_shader_t* _sg_shader_at(uint32_t shd_id) {
7699+ SOKOL_ASSERT(SG_INVALID_ID != shd_id);
7700+ int slot_index = _sg_slot_index(shd_id);
7701+ SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.shader_pool.size));
7702+ return &_sg.pools.shaders[slot_index];
7703+}
7704+
7705+_SOKOL_PRIVATE _sg_pipeline_t* _sg_pipeline_at(uint32_t pip_id) {
7706+ SOKOL_ASSERT(SG_INVALID_ID != pip_id);
7707+ int slot_index = _sg_slot_index(pip_id);
7708+ SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.pipeline_pool.size));
7709+ return &_sg.pools.pipelines[slot_index];
7710+}
7711+
7712+_SOKOL_PRIVATE _sg_view_t* _sg_view_at(uint32_t view_id) {
7713+ SOKOL_ASSERT(SG_INVALID_ID != view_id);
7714+ int slot_index = _sg_slot_index(view_id);
7715+ SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < _sg.pools.view_pool.size));
7716+ return &_sg.pools.views[slot_index];
7717+}
7718+
7719+// returns pointer to resource with matching id check, may return 0
7720+_SOKOL_PRIVATE _sg_buffer_t* _sg_lookup_buffer(uint32_t buf_id) {
7721+ if (SG_INVALID_ID != buf_id) {
7722+ _sg_buffer_t* buf = _sg_buffer_at(buf_id);
7723+ if (buf->slot.id == buf_id) {
7724+ return buf;
7725+ }
7726+ }
7727+ return 0;
7728+}
7729+
7730+_SOKOL_PRIVATE _sg_image_t* _sg_lookup_image(uint32_t img_id) {
7731+ if (SG_INVALID_ID != img_id) {
7732+ _sg_image_t* img = _sg_image_at(img_id);
7733+ if (img->slot.id == img_id) {
7734+ return img;
7735+ }
7736+ }
7737+ return 0;
7738+}
7739+
7740+_SOKOL_PRIVATE _sg_sampler_t* _sg_lookup_sampler(uint32_t smp_id) {
7741+ if (SG_INVALID_ID != smp_id) {
7742+ _sg_sampler_t* smp = _sg_sampler_at(smp_id);
7743+ if (smp->slot.id == smp_id) {
7744+ return smp;
7745+ }
7746+ }
7747+ return 0;
7748+}
7749+
7750+_SOKOL_PRIVATE _sg_shader_t* _sg_lookup_shader(uint32_t shd_id) {
7751+ if (SG_INVALID_ID != shd_id) {
7752+ _sg_shader_t* shd = _sg_shader_at(shd_id);
7753+ if (shd->slot.id == shd_id) {
7754+ return shd;
7755+ }
7756+ }
7757+ return 0;
7758+}
7759+
7760+_SOKOL_PRIVATE _sg_pipeline_t* _sg_lookup_pipeline(uint32_t pip_id) {
7761+ if (SG_INVALID_ID != pip_id) {
7762+ _sg_pipeline_t* pip = _sg_pipeline_at(pip_id);
7763+ if (pip->slot.id == pip_id) {
7764+ return pip;
7765+ }
7766+ }
7767+ return 0;
7768+}
7769+
7770+_SOKOL_PRIVATE _sg_view_t* _sg_lookup_view(uint32_t view_id) {
7771+ if (SG_INVALID_ID != view_id) {
7772+ _sg_view_t* view = _sg_view_at(view_id);
7773+ if (view->slot.id == view_id) {
7774+ return view;
7775+ }
7776+ }
7777+ return 0;
7778+}
7779+
7780+// ████████ ██████ █████ ██████ ██ ██
7781+// ██ ██ ██ ██ ██ ██ ██ ██
7782+// ██ ██████ ███████ ██ █████
7783+// ██ ██ ██ ██ ██ ██ ██ ██
7784+// ██ ██ ██ ██ ██ ██████ ██ ██
7785+//
7786+// >>track
7787+_SOKOL_PRIVATE void _sg_track_init(_sg_track_t* track, int num_slots) {
7788+ SOKOL_ASSERT(track && (num_slots > 0));
7789+ _sg_clear(track, sizeof(_sg_track_t));
7790+ track->num_slots = num_slots;
7791+ track->slots = (uint32_t*)_sg_malloc_clear((size_t)num_slots * sizeof(uint32_t));
7792+ track->occupy_num_bytes = _sg_roundup_u32((uint32_t)num_slots, 8) >> 3;
7793+ track->occupy_bits = (uint8_t*)_sg_malloc_clear(track->occupy_num_bytes);
7794+}
7795+
7796+_SOKOL_PRIVATE void _sg_track_discard(_sg_track_t* track) {
7797+ SOKOL_ASSERT(track);
7798+ if (track->slots) {
7799+ _sg_free(track->slots);
7800+ track->slots = 0;
7801+ }
7802+ if (track->occupy_bits) {
7803+ _sg_free(track->occupy_bits);
7804+ track->occupy_num_bytes = 0;
7805+ track->occupy_bits = 0;
7806+ }
7807+ track->num_slots = 0;
7808+ track->cur_slot = 0;
7809+}
7810+
7811+_SOKOL_PRIVATE void _sg_track_reset(_sg_track_t* track) {
7812+ SOKOL_ASSERT(track && track->slots && track->occupy_bits);
7813+ track->cur_slot = 0;
7814+ _sg_clear(track->occupy_bits, track->occupy_num_bytes);
7815+}
7816+
7817+_SOKOL_PRIVATE int _sg_track_occupy_index(int slot_index) {
7818+ const int occupy_index = slot_index >> 3;
7819+ return occupy_index;
7820+}
7821+
7822+_SOKOL_PRIVATE uint8_t _sg_track_occupy_mask(int slot_index) {
7823+ return (uint8_t)(1 << (slot_index & 7));
7824+}
7825+
7826+_SOKOL_PRIVATE void _sg_track_add(_sg_track_t* track, uint32_t id) {
7827+ SOKOL_ASSERT(track && track->slots && track->occupy_bits);
7828+ SOKOL_ASSERT(id != SG_INVALID_ID);
7829+ const int slot_index = _sg_slot_index(id);
7830+ const int occupy_index = _sg_track_occupy_index(slot_index);
7831+ SOKOL_ASSERT((uint32_t)occupy_index < track->occupy_num_bytes);
7832+ const uint8_t occupy_mask = _sg_track_occupy_mask(slot_index);
7833+ // don't record the same resource twice
7834+ if (0 == (track->occupy_bits[occupy_index] & occupy_mask)) {
7835+ SOKOL_ASSERT(track->cur_slot < track->num_slots);
7836+ track->slots[track->cur_slot++] = id;
7837+ track->occupy_bits[occupy_index] |= occupy_mask;
7838+ }
7839+}
7840+
7841+_SOKOL_PRIVATE void _sg_track_remove(_sg_track_t* track, uint32_t id) {
7842+ SOKOL_ASSERT(track && track->slots && track->occupy_bits);
7843+ SOKOL_ASSERT(id != SG_INVALID_ID);
7844+ const int slot_index = _sg_slot_index(id);
7845+ const int occupy_index = _sg_track_occupy_index(slot_index);
7846+ const uint8_t occupy_mask = _sg_track_occupy_mask(slot_index);
7847+ if (track->occupy_bits[occupy_index] & occupy_mask) {
7848+ track->occupy_bits[occupy_index] &= ~occupy_mask;
7849+ // remove tracked id from the slots array
7850+ for (int i = 0; i < track->cur_slot; i++) {
7851+ if (id == track->slots[i]) {
7852+ SOKOL_ASSERT(track->cur_slot > 0);
7853+ track->slots[i] = track->slots[--track->cur_slot];
7854+ break;
7855+ }
7856+ }
7857+ }
7858+}
7859+
7860+// ██████ ███████ ███████ ███████
7861+// ██ ██ ██ ██ ██
7862+// ██████ █████ █████ ███████
7863+// ██ ██ ██ ██ ██
7864+// ██ ██ ███████ ██ ███████
7865+//
7866+// >>refs
7867+_SOKOL_PRIVATE _sg_sref_t _sg_sref(const _sg_slot_t* slot) {
7868+ _SG_STRUCT(_sg_sref_t, sref);
7869+ if (slot) {
7870+ sref.id = slot->id;
7871+ sref.uninit_count = slot->uninit_count;
7872+ }
7873+ return sref;
7874+}
7875+
7876+_SOKOL_PRIVATE bool _sg_sref_slot_eql(const _sg_sref_t* sref, const _sg_slot_t* slot) {
7877+ SOKOL_ASSERT(sref && slot);
7878+ return (sref->id == slot->id) && (sref->uninit_count == slot->uninit_count);
7879+}
7880+
7881+_SOKOL_PRIVATE bool _sg_sref_sref_eql(const _sg_sref_t* sref0, const _sg_sref_t* sref1) {
7882+ SOKOL_ASSERT(sref0 && sref1);
7883+ return (sref0->id == sref1->id) && (sref0->uninit_count == sref1->uninit_count);
7884+}
7885+
7886+_SOKOL_PRIVATE _sg_buffer_ref_t _sg_buffer_ref(_sg_buffer_t* buf_or_null) {
7887+ _SG_STRUCT(_sg_buffer_ref_t, ref);
7888+ if (buf_or_null) {
7889+ _sg_buffer_t* buf = buf_or_null;
7890+ SOKOL_ASSERT(buf->slot.id != SG_INVALID_ID);
7891+ ref.ptr = buf;
7892+ ref.sref = _sg_sref(&buf->slot);
7893+ }
7894+ return ref;
7895+}
7896+
7897+_SOKOL_PRIVATE _sg_image_ref_t _sg_image_ref(_sg_image_t* img_or_null) {
7898+ _SG_STRUCT(_sg_image_ref_t, ref);
7899+ if (img_or_null) {
7900+ _sg_image_t* img = img_or_null;
7901+ SOKOL_ASSERT(img->slot.id != SG_INVALID_ID);
7902+ ref.ptr = img;
7903+ ref.sref = _sg_sref(&img->slot);
7904+ }
7905+ return ref;
7906+}
7907+
7908+_SOKOL_PRIVATE _sg_sampler_ref_t _sg_sampler_ref(_sg_sampler_t* smp_or_null) {
7909+ _SG_STRUCT(_sg_sampler_ref_t, ref);
7910+ if (smp_or_null) {
7911+ _sg_sampler_t* smp = smp_or_null;
7912+ SOKOL_ASSERT(smp->slot.id != SG_INVALID_ID);
7913+ ref.ptr = smp;
7914+ ref.sref = _sg_sref(&smp->slot);
7915+ }
7916+ return ref;
7917+}
7918+
7919+_SOKOL_PRIVATE _sg_shader_ref_t _sg_shader_ref(_sg_shader_t* shd_or_null) {
7920+ _SG_STRUCT(_sg_shader_ref_t, ref);
7921+ if (shd_or_null) {
7922+ _sg_shader_t* shd = shd_or_null;
7923+ SOKOL_ASSERT(shd->slot.id != SG_INVALID_ID);
7924+ ref.ptr = shd;
7925+ ref.sref = _sg_sref(&shd->slot);
7926+ }
7927+ return ref;
7928+}
7929+
7930+_SOKOL_PRIVATE _sg_pipeline_ref_t _sg_pipeline_ref(_sg_pipeline_t* pip_or_null) {
7931+ _SG_STRUCT(_sg_pipeline_ref_t, ref);
7932+ if (pip_or_null) {
7933+ _sg_pipeline_t* pip = pip_or_null;
7934+ SOKOL_ASSERT(pip->slot.id != SG_INVALID_ID);
7935+ ref.ptr = pip;
7936+ ref.sref = _sg_sref(&pip->slot);
7937+ }
7938+ return ref;
7939+}
7940+
7941+_SOKOL_PRIVATE _sg_view_ref_t _sg_view_ref(_sg_view_t* view_or_null) {
7942+ _SG_STRUCT(_sg_view_ref_t, ref);
7943+ if (view_or_null) {
7944+ _sg_view_t* view = view_or_null;
7945+ SOKOL_ASSERT(view->slot.id != SG_INVALID_ID);
7946+ ref.ptr = view;
7947+ ref.sref = _sg_sref(&view->slot);
7948+ }
7949+ return ref;
7950+}
7951+
7952+#define _SG_IMPL_RES_EQL(NAME,REF,RES) _SOKOL_PRIVATE bool NAME(const REF* ref, const RES* res) { SOKOL_ASSERT(ref && res); return _sg_sref_slot_eql(&ref->sref, &res->slot); }
7953+_SG_IMPL_RES_EQL(_sg_buffer_ref_eql, _sg_buffer_ref_t, _sg_buffer_t)
7954+_SG_IMPL_RES_EQL(_sg_image_ref_eql, _sg_image_ref_t, _sg_image_t)
7955+_SG_IMPL_RES_EQL(_sg_sampler_ref_eql, _sg_sampler_ref_t, _sg_sampler_t)
7956+_SG_IMPL_RES_EQL(_sg_shader_ref_eql, _sg_shader_ref_t, _sg_shader_t)
7957+_SG_IMPL_RES_EQL(_sg_pipeline_ref_eql, _sg_pipeline_ref_t, _sg_pipeline_t)
7958+_SG_IMPL_RES_EQL(_sg_view_ref_eql, _sg_view_ref_t, _sg_view_t)
7959+
7960+#define _SG_IMPL_RES_NULL(NAME,REF) _SOKOL_PRIVATE bool NAME(const REF* ref) { SOKOL_ASSERT(ref); return SG_INVALID_ID == ref->sref.id; }
7961+_SG_IMPL_RES_NULL(_sg_buffer_ref_null, _sg_buffer_ref_t)
7962+_SG_IMPL_RES_NULL(_sg_image_ref_null, _sg_image_ref_t)
7963+_SG_IMPL_RES_NULL(_sg_sampler_ref_null, _sg_sampler_ref_t)
7964+_SG_IMPL_RES_NULL(_sg_shader_ref_null, _sg_shader_ref_t)
7965+_SG_IMPL_RES_NULL(_sg_pipeline_ref_null, _sg_pipeline_ref_t)
7966+_SG_IMPL_RES_NULL(_sg_view_ref_null, _sg_view_ref_t)
7967+
7968+#define _SG_IMPL_RES_ALIVE(NAME,REF) _SOKOL_PRIVATE bool NAME(const REF* ref) { SOKOL_ASSERT(ref); return ref->ptr && _sg_sref_slot_eql(&ref->sref, &ref->ptr->slot); }
7969+_SG_IMPL_RES_ALIVE(_sg_buffer_ref_alive, _sg_buffer_ref_t)
7970+_SG_IMPL_RES_ALIVE(_sg_image_ref_alive, _sg_image_ref_t)
7971+_SG_IMPL_RES_ALIVE(_sg_sampler_ref_alive, _sg_sampler_ref_t)
7972+_SG_IMPL_RES_ALIVE(_sg_shader_ref_alive, _sg_shader_ref_t)
7973+_SG_IMPL_RES_ALIVE(_sg_pipeline_ref_alive, _sg_pipeline_ref_t)
7974+_SG_IMPL_RES_ALIVE(_sg_view_ref_alive, _sg_view_ref_t)
7975+
7976+#define _SG_IMPL_RES_VALID(NAME,REF) _SOKOL_PRIVATE bool NAME(const REF* ref) { SOKOL_ASSERT(ref); return ref->ptr && _sg_sref_slot_eql(&ref->sref, &ref->ptr->slot) && (ref->ptr->slot.state == SG_RESOURCESTATE_VALID); }
7977+_SG_IMPL_RES_VALID(_sg_buffer_ref_valid, _sg_buffer_ref_t)
7978+_SG_IMPL_RES_VALID(_sg_image_ref_valid, _sg_image_ref_t)
7979+_SG_IMPL_RES_VALID(_sg_sampler_ref_valid, _sg_sampler_ref_t)
7980+_SG_IMPL_RES_VALID(_sg_shader_ref_valid, _sg_shader_ref_t)
7981+_SG_IMPL_RES_VALID(_sg_pipeline_ref_valid, _sg_pipeline_ref_t)
7982+_SG_IMPL_RES_VALID(_sg_view_ref_valid, _sg_view_ref_t)
7983+
7984+#define _SG_IMPL_RES_PTR(NAME,REF,RES) _SOKOL_PRIVATE RES* NAME(const REF* ref) { SOKOL_ASSERT(ref && ref->ptr && _sg_sref_slot_eql(&ref->sref, &ref->ptr->slot)); return ref->ptr; }
7985+_SG_IMPL_RES_PTR(_sg_buffer_ref_ptr, _sg_buffer_ref_t, _sg_buffer_t)
7986+_SG_IMPL_RES_PTR(_sg_image_ref_ptr, _sg_image_ref_t, _sg_image_t)
7987+_SG_IMPL_RES_PTR(_sg_sampler_ref_ptr, _sg_sampler_ref_t, _sg_sampler_t)
7988+_SG_IMPL_RES_PTR(_sg_shader_ref_ptr, _sg_shader_ref_t, _sg_shader_t)
7989+_SG_IMPL_RES_PTR(_sg_pipeline_ref_ptr, _sg_pipeline_ref_t, _sg_pipeline_t)
7990+_SG_IMPL_RES_PTR(_sg_view_ref_ptr, _sg_view_ref_t, _sg_view_t)
7991+
7992+#define _SG_IMPL_RES_PTR_OR_NULL(NAME,REF,RES) _SOKOL_PRIVATE RES* NAME(const REF* ref) { SOKOL_ASSERT(ref); if ((SG_INVALID_ID != ref->sref.id) && _sg_sref_slot_eql(&ref->sref, &ref->ptr->slot)) { return ref->ptr; } else { return 0; } }
7993+_SG_IMPL_RES_PTR_OR_NULL(_sg_buffer_ref_ptr_or_null, _sg_buffer_ref_t, _sg_buffer_t)
7994+_SG_IMPL_RES_PTR_OR_NULL(_sg_image_ref_ptr_or_null, _sg_image_ref_t, _sg_image_t)
7995+_SG_IMPL_RES_PTR_OR_NULL(_sg_sampler_ref_ptr_or_null, _sg_sampler_ref_t, _sg_sampler_t)
7996+_SG_IMPL_RES_PTR_OR_NULL(_sg_shader_ref_ptr_or_null, _sg_shader_ref_t, _sg_shader_t)
7997+_SG_IMPL_RES_PTR_OR_NULL(_sg_pipeline_ref_ptr_or_null, _sg_pipeline_ref_t, _sg_pipeline_t)
7998+_SG_IMPL_RES_PTR_OR_NULL(_sg_view_ref_ptr_or_null, _sg_view_ref_t, _sg_view_t)
7999+
8000+// ██ ██ ███████ ██ ██████ ███████ ██████ ███████
8001+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
8002+// ███████ █████ ██ ██████ █████ ██████ ███████
8003+// ██ ██ ██ ██ ██ ██ ██ ██ ██
8004+// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████
8005+//
8006+// >>helpers
8007+
8008+// helper macros
8009+#define _sg_def(val, def) (((val) == 0) ? (def) : (val))
8010+#define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val))
8011+#define _sg_min(a,b) (((a)<(b))?(a):(b))
8012+#define _sg_max(a,b) (((a)>(b))?(a):(b))
8013+#define _sg_clamp(v,v0,v1) (((v)<(v0))?(v0):(((v)>(v1))?(v1):(v)))
8014+#define _sg_fequal(val,cmp,delta) ((((val)-(cmp))> -(delta))&&(((val)-(cmp))<(delta)))
8015+#define _sg_ispow2(val) ((val&(val-1))==0)
8016+#define _sg_stats_add(key,val) {if(_sg.stats_enabled){ _sg.stats.cur_frame.key+=val;}}
8017+#define _sg_stats_inc(key) {if(_sg.stats_enabled){ _sg.stats.cur_frame.key++;}}
8018+#define _sg_resource_stats_inc(key) {if(_sg.stats_enabled){ _sg.stats.cur_frame.key++; _sg.stats.total.key++;}}
8019+
8020+_SOKOL_PRIVATE void _sg_update_alive_free_resource_stats(sg_total_resource_stats* stats, const _sg_pool_t* pool) {
8021+ SOKOL_ASSERT(stats && pool);
8022+ stats->alive = (uint32_t) ((pool->size - 1) - pool->queue_top);
8023+ stats->free = (uint32_t) pool->queue_top;
8024+}
8025+
8026+_SOKOL_PRIVATE void _sg_update_stats(void) {
8027+ _sg.stats.cur_frame.frame_index = _sg.frame_index;
8028+ _sg.stats.prev_frame = _sg.stats.cur_frame;
8029+ _sg_clear(&_sg.stats.cur_frame, sizeof(_sg.stats.cur_frame));
8030+}
8031+
8032+_SOKOL_PRIVATE uint32_t _sg_align_u32(uint32_t val, uint32_t align) {
8033+ SOKOL_ASSERT((align > 0) && ((align & (align - 1)) == 0));
8034+ return (val + (align - 1)) & ~(align - 1);
8035+}
8036+
8037+_SOKOL_PRIVATE _sg_recti_t _sg_clipi(int x, int y, int w, int h, int clip_width, int clip_height) {
8038+ x = _sg_min(_sg_max(0, x), clip_width-1);
8039+ y = _sg_min(_sg_max(0, y), clip_height-1);
8040+ if ((x + w) > clip_width) {
8041+ w = clip_width - x;
8042+ }
8043+ if ((y + h) > clip_height) {
8044+ h = clip_height - y;
8045+ }
8046+ w = _sg_max(w, 1);
8047+ h = _sg_max(h, 1);
8048+ const _sg_recti_t res = { x, y, w, h };
8049+ return res;
8050+}
8051+
8052+// return size of a mipmap level
8053+_SOKOL_PRIVATE int _sg_miplevel_dim(int base_dim, int mip_level) {
8054+ return _sg_max(base_dim >> mip_level, 1);
8055+}
8056+
8057+_SOKOL_PRIVATE bool _sg_image_view_alive(const _sg_view_t* view) {
8058+ return view && _sg_image_ref_alive(&view->cmn.img.ref);
8059+}
8060+
8061+_SOKOL_PRIVATE _sg_dimi_t _sg_image_view_dim(const _sg_view_t* view) {
8062+ SOKOL_ASSERT(view);
8063+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
8064+ SOKOL_ASSERT((img->cmn.width > 0) && (img->cmn.height > 0));
8065+ _SG_STRUCT(_sg_dimi_t, res);
8066+ res.width = _sg_miplevel_dim(img->cmn.width, view->cmn.img.mip_level);
8067+ res.height = _sg_miplevel_dim(img->cmn.height, view->cmn.img.mip_level);
8068+ return res;
8069+}
8070+
8071+_SOKOL_PRIVATE bool _sg_attachments_empty(const sg_attachments* atts) {
8072+ SOKOL_ASSERT(atts);
8073+ for (size_t i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) {
8074+ if (atts->colors[i].id != SG_INVALID_ID) {
8075+ return false;
8076+ }
8077+ if (atts->resolves[i].id != SG_INVALID_ID) {
8078+ return false;
8079+ }
8080+ }
8081+ if (atts->depth_stencil.id != SG_INVALID_ID) {
8082+ return false;
8083+ }
8084+ return true;
8085+}
8086+
8087+_SOKOL_PRIVATE _sg_attachments_ptrs_t _sg_attachments_ptrs(const sg_attachments* atts) {
8088+ SOKOL_ASSERT(atts);
8089+ _SG_STRUCT(_sg_attachments_ptrs_t, res);
8090+ res.empty = true;
8091+ for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) {
8092+ if (atts->colors[i].id != SG_INVALID_ID) {
8093+ res.empty = false;
8094+ res.num_color_views += 1;
8095+ res.color_views[i] = _sg_lookup_view(atts->colors[i].id);
8096+ }
8097+ if (atts->resolves[i].id != SG_INVALID_ID) {
8098+ SOKOL_ASSERT(atts->colors[i].id != SG_INVALID_ID);
8099+ res.empty = false;
8100+ res.resolve_views[i] = _sg_lookup_view(atts->resolves[i].id);
8101+ }
8102+ }
8103+ if (atts->depth_stencil.id != SG_INVALID_ID) {
8104+ res.empty = false;
8105+ res.ds_view = _sg_lookup_view(atts->depth_stencil.id);
8106+ }
8107+ return res;
8108+}
8109+
8110+_SOKOL_PRIVATE _sg_dimi_t _sg_attachments_dim(const _sg_attachments_ptrs_t* atts_ptrs) {
8111+ if (atts_ptrs->ds_view) {
8112+ return _sg_image_view_dim(atts_ptrs->ds_view);
8113+ } else {
8114+ SOKOL_ASSERT(atts_ptrs->color_views[0]);
8115+ return _sg_image_view_dim(atts_ptrs->color_views[0]);
8116+ }
8117+}
8118+
8119+_SOKOL_PRIVATE bool _sg_attachments_alive(const _sg_attachments_ptrs_t* atts_ptrs) {
8120+ for (int i = 0; i < atts_ptrs->num_color_views; i++) {
8121+ if (!_sg_image_view_alive(atts_ptrs->color_views[i])) {
8122+ return false;
8123+ }
8124+ if (atts_ptrs->resolve_views[i] && !_sg_image_view_alive(atts_ptrs->resolve_views[i])) {
8125+ return false;
8126+ }
8127+ }
8128+ if (atts_ptrs->ds_view && !_sg_image_view_alive(atts_ptrs->ds_view)) {
8129+ return false;
8130+ }
8131+ return true;
8132+}
8133+
8134+_SOKOL_PRIVATE bool _sg_is_dualsource_blendfactor(sg_blend_factor f) {
8135+ switch (f) {
8136+ case SG_BLENDFACTOR_SRC1_COLOR:
8137+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR:
8138+ case SG_BLENDFACTOR_SRC1_ALPHA:
8139+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA:
8140+ return true;
8141+ default:
8142+ return false;
8143+ }
8144+}
8145+
8146+_SOKOL_PRIVATE void _sg_buffer_common_init(_sg_buffer_common_t* cmn, const sg_buffer_desc* desc) {
8147+ cmn->size = (int)desc->size;
8148+ cmn->append_pos = 0;
8149+ cmn->append_overflow = false;
8150+ cmn->update_frame_index = 0;
8151+ cmn->append_frame_index = 0;
8152+ cmn->num_slots = desc->usage.immutable ? 1 : SG_NUM_INFLIGHT_FRAMES;
8153+ cmn->active_slot = 0;
8154+ cmn->usage = desc->usage;
8155+}
8156+
8157+_SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_image_desc* desc) {
8158+ cmn->upd_frame_index = 0;
8159+ cmn->num_slots = desc->usage.immutable ? 1 : SG_NUM_INFLIGHT_FRAMES;
8160+ cmn->active_slot = 0;
8161+ cmn->type = desc->type;
8162+ cmn->width = desc->width;
8163+ cmn->height = desc->height;
8164+ cmn->num_slices = desc->num_slices;
8165+ cmn->num_mipmaps = desc->num_mipmaps;
8166+ cmn->usage = desc->usage;
8167+ cmn->pixel_format = desc->pixel_format;
8168+ cmn->sample_count = desc->sample_count;
8169+}
8170+
8171+_SOKOL_PRIVATE void _sg_sampler_common_init(_sg_sampler_common_t* cmn, const sg_sampler_desc* desc) {
8172+ cmn->min_filter = desc->min_filter;
8173+ cmn->mag_filter = desc->mag_filter;
8174+ cmn->mipmap_filter = desc->mipmap_filter;
8175+ cmn->wrap_u = desc->wrap_u;
8176+ cmn->wrap_v = desc->wrap_v;
8177+ cmn->wrap_w = desc->wrap_w;
8178+ cmn->min_lod = desc->min_lod;
8179+ cmn->max_lod = desc->max_lod;
8180+ cmn->border_color = desc->border_color;
8181+ cmn->compare = desc->compare;
8182+ cmn->max_anisotropy = desc->max_anisotropy;
8183+}
8184+
8185+_SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_shader_desc* desc) {
8186+ cmn->is_compute = desc->compute_func.source || desc->compute_func.bytecode.ptr;
8187+ for (size_t i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) {
8188+ cmn->attrs[i].base_type = desc->attrs[i].base_type;
8189+ }
8190+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
8191+ const sg_shader_uniform_block* src = &desc->uniform_blocks[i];
8192+ _sg_shader_uniform_block_t* dst = &cmn->uniform_blocks[i];
8193+ if (src->stage != SG_SHADERSTAGE_NONE) {
8194+ cmn->required_bindings_and_uniforms |= (1 << i);
8195+ dst->stage = src->stage;
8196+ dst->size = src->size;
8197+ }
8198+ }
8199+ const uint32_t required_bindings_flag = (1 << SG_MAX_UNIFORMBLOCK_BINDSLOTS);
8200+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
8201+ _sg_shader_view_t* dst = &cmn->views[i];
8202+ if (desc->views[i].texture.stage != SG_SHADERSTAGE_NONE) {
8203+ const sg_shader_texture_view* src = &desc->views[i].texture;
8204+ dst->stage = src->stage;
8205+ dst->view_type = SG_VIEWTYPE_TEXTURE;
8206+ dst->image_type = src->image_type;
8207+ dst->sample_type = src->sample_type;
8208+ dst->multisampled = src->multisampled;
8209+ } else if (desc->views[i].storage_buffer.stage != SG_SHADERSTAGE_NONE) {
8210+ const sg_shader_storage_buffer_view* src = &desc->views[i].storage_buffer;
8211+ cmn->required_bindings_and_uniforms |= required_bindings_flag;
8212+ dst->stage = src->stage;
8213+ dst->view_type = SG_VIEWTYPE_STORAGEBUFFER;
8214+ dst->sbuf_readonly = src->readonly;
8215+ } else if (desc->views[i].storage_image.stage != SG_SHADERSTAGE_NONE) {
8216+ const sg_shader_storage_image_view* src = &desc->views[i].storage_image;
8217+ cmn->required_bindings_and_uniforms |= required_bindings_flag;
8218+ dst->stage = src->stage;
8219+ dst->view_type = SG_VIEWTYPE_STORAGEIMAGE;
8220+ dst->image_type = src->image_type;
8221+ dst->access_format = src->access_format;
8222+ dst->simg_writeonly = src->writeonly;
8223+ }
8224+ }
8225+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
8226+ const sg_shader_sampler* src = &desc->samplers[i];
8227+ _sg_shader_sampler_t* dst = &cmn->samplers[i];
8228+ if (src->stage != SG_SHADERSTAGE_NONE) {
8229+ cmn->required_bindings_and_uniforms |= required_bindings_flag;
8230+ dst->stage = src->stage;
8231+ dst->sampler_type = src->sampler_type;
8232+ }
8233+ }
8234+ for (size_t i = 0; i < SG_MAX_TEXTURE_SAMPLER_PAIRS; i++) {
8235+ const sg_shader_texture_sampler_pair* src = &desc->texture_sampler_pairs[i];
8236+ _sg_shader_texture_sampler_t* dst = &cmn->texture_samplers[i];
8237+ if (src->stage != SG_SHADERSTAGE_NONE) {
8238+ dst->stage = src->stage;
8239+ SOKOL_ASSERT((src->view_slot >= 0) && (src->view_slot < SG_MAX_VIEW_BINDSLOTS));
8240+ SOKOL_ASSERT(cmn->views[src->view_slot].view_type == SG_VIEWTYPE_TEXTURE);
8241+ SOKOL_ASSERT(cmn->views[src->view_slot].stage == src->stage);
8242+ dst->view_slot = src->view_slot;
8243+ SOKOL_ASSERT((src->sampler_slot >= 0) && (src->sampler_slot < SG_MAX_SAMPLER_BINDSLOTS));
8244+ SOKOL_ASSERT(desc->samplers[src->sampler_slot].stage == src->stage);
8245+ dst->sampler_slot = src->sampler_slot;
8246+ }
8247+ }
8248+}
8249+
8250+_SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const sg_pipeline_desc* desc, _sg_shader_t* shd) {
8251+ SOKOL_ASSERT((desc->color_count >= 0) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS));
8252+
8253+ // FIXME: most of this isn't needed for compute pipelines
8254+
8255+ const uint32_t required_bindings_flag = (1 << SG_MAX_UNIFORMBLOCK_BINDSLOTS);
8256+ for (size_t attr_idx = 0; attr_idx < SG_MAX_VERTEX_ATTRIBUTES; attr_idx++) {
8257+ const sg_vertex_attr_state* attr_state = &desc->layout.attrs[attr_idx];
8258+ if (attr_state->format != SG_VERTEXFORMAT_INVALID) {
8259+ SOKOL_ASSERT((attr_state->buffer_index >= 0) && (attr_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS));
8260+ cmn->vertex_buffer_layout_active[attr_state->buffer_index] = true;
8261+ cmn->required_bindings_and_uniforms |= required_bindings_flag;
8262+ }
8263+ }
8264+ cmn->use_instanced_draw = false;
8265+ for (size_t vbuf_idx = 0; vbuf_idx < SG_MAX_VERTEXBUFFER_BINDSLOTS; vbuf_idx++) {
8266+ const sg_vertex_buffer_layout_state* vbuf_state = &desc->layout.buffers[vbuf_idx];
8267+ if (vbuf_state->step_func == SG_VERTEXSTEP_PER_INSTANCE) {
8268+ cmn->use_instanced_draw = true;
8269+ }
8270+ }
8271+ cmn->is_compute = desc->compute;
8272+ cmn->shader = _sg_shader_ref(shd);
8273+ cmn->layout = desc->layout;
8274+ cmn->depth = desc->depth;
8275+ cmn->stencil = desc->stencil;
8276+ cmn->color_count = desc->color_count;
8277+ for (int i = 0; i < desc->color_count; i++) {
8278+ cmn->colors[i] = desc->colors[i];
8279+ }
8280+ cmn->primitive_type = desc->primitive_type;
8281+ cmn->index_type = desc->index_type;
8282+ if (cmn->index_type != SG_INDEXTYPE_NONE) {
8283+ cmn->required_bindings_and_uniforms |= required_bindings_flag;
8284+ }
8285+ cmn->cull_mode = desc->cull_mode;
8286+ cmn->face_winding = desc->face_winding;
8287+ cmn->sample_count = desc->sample_count;
8288+ cmn->blend_color = desc->blend_color;
8289+ cmn->alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled;
8290+}
8291+
8292+_SOKOL_PRIVATE void _sg_buffer_view_common_init(_sg_buffer_view_common_t* cmn, const sg_buffer_view_desc* desc, _sg_buffer_t* buf) {
8293+ SOKOL_ASSERT(SG_RESOURCESTATE_VALID == buf->slot.state);
8294+ cmn->ref = _sg_buffer_ref(buf);
8295+ cmn->offset = desc->offset;
8296+}
8297+
8298+_SOKOL_PRIVATE void _sg_texture_view_common_init(_sg_image_view_common_t* cmn, const sg_texture_view_desc* desc, _sg_image_t* img) {
8299+ SOKOL_ASSERT(SG_RESOURCESTATE_VALID == img->slot.state);
8300+ cmn->ref = _sg_image_ref(img);
8301+ cmn->mip_level = desc->mip_levels.base;
8302+ cmn->mip_level_count = _sg_def(desc->mip_levels.count, img->cmn.num_mipmaps - cmn->mip_level);
8303+ cmn->slice = desc->slices.base;
8304+ switch (img->cmn.type) {
8305+ case SG_IMAGETYPE_2D:
8306+ cmn->slice_count = 1;
8307+ break;
8308+ case SG_IMAGETYPE_CUBE:
8309+ cmn->slice_count = 6;
8310+ break;
8311+ case SG_IMAGETYPE_3D:
8312+ cmn->slice_count = 1;
8313+ break;
8314+ case SG_IMAGETYPE_ARRAY:
8315+ cmn->slice_count = _sg_def(desc->slices.count, img->cmn.num_slices - cmn->slice);
8316+ break;
8317+ default:
8318+ SOKOL_UNREACHABLE;
8319+ }
8320+}
8321+
8322+_SOKOL_PRIVATE void _sg_image_view_common_init(_sg_image_view_common_t* cmn, const sg_image_view_desc* desc, _sg_image_t* img) {
8323+ SOKOL_ASSERT(SG_RESOURCESTATE_VALID == img->slot.state);
8324+ cmn->ref = _sg_image_ref(img);
8325+ cmn->mip_level = desc->mip_level;
8326+ cmn->mip_level_count = 1;
8327+ cmn->slice = desc->slice;
8328+ cmn->slice_count = 1;
8329+}
8330+
8331+_SOKOL_PRIVATE void _sg_view_common_init(_sg_view_common_t* cmn, const sg_view_desc* desc, _sg_buffer_t* buf, _sg_image_t* img) {
8332+ if (desc->texture.image.id != SG_INVALID_ID) {
8333+ SOKOL_ASSERT(img);
8334+ cmn->type = SG_VIEWTYPE_TEXTURE;
8335+ _sg_texture_view_common_init(&cmn->img, &desc->texture, img);
8336+ } else if (desc->storage_buffer.buffer.id != SG_INVALID_ID) {
8337+ SOKOL_ASSERT(buf);
8338+ cmn->type = SG_VIEWTYPE_STORAGEBUFFER;
8339+ _sg_buffer_view_common_init(&cmn->buf, &desc->storage_buffer, buf);
8340+ } else if (desc->storage_image.image.id != SG_INVALID_ID) {
8341+ SOKOL_ASSERT(img);
8342+ cmn->type = SG_VIEWTYPE_STORAGEIMAGE;
8343+ _sg_image_view_common_init(&cmn->img, &desc->storage_image, img);
8344+ } else if (desc->color_attachment.image.id != SG_INVALID_ID) {
8345+ SOKOL_ASSERT(img);
8346+ cmn->type = SG_VIEWTYPE_COLORATTACHMENT;
8347+ _sg_image_view_common_init(&cmn->img, &desc->color_attachment, img);
8348+ } else if (desc->resolve_attachment.image.id != SG_INVALID_ID) {
8349+ SOKOL_ASSERT(img);
8350+ cmn->type = SG_VIEWTYPE_RESOLVEATTACHMENT;
8351+ _sg_image_view_common_init(&cmn->img, &desc->resolve_attachment, img);
8352+ } else if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) {
8353+ SOKOL_ASSERT(img);
8354+ cmn->type = SG_VIEWTYPE_DEPTHSTENCILATTACHMENT;
8355+ _sg_image_view_common_init(&cmn->img, &desc->depth_stencil_attachment, img);
8356+ } else {
8357+ SOKOL_UNREACHABLE;
8358+ }
8359+}
8360+
8361+_SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) {
8362+ switch (fmt) {
8363+ case SG_VERTEXFORMAT_FLOAT: return 4;
8364+ case SG_VERTEXFORMAT_FLOAT2: return 8;
8365+ case SG_VERTEXFORMAT_FLOAT3: return 12;
8366+ case SG_VERTEXFORMAT_FLOAT4: return 16;
8367+ case SG_VERTEXFORMAT_INT: return 4;
8368+ case SG_VERTEXFORMAT_INT2: return 8;
8369+ case SG_VERTEXFORMAT_INT3: return 12;
8370+ case SG_VERTEXFORMAT_INT4: return 16;
8371+ case SG_VERTEXFORMAT_UINT: return 4;
8372+ case SG_VERTEXFORMAT_UINT2: return 8;
8373+ case SG_VERTEXFORMAT_UINT3: return 12;
8374+ case SG_VERTEXFORMAT_UINT4: return 16;
8375+ case SG_VERTEXFORMAT_BYTE4: return 4;
8376+ case SG_VERTEXFORMAT_BYTE4N: return 4;
8377+ case SG_VERTEXFORMAT_UBYTE4: return 4;
8378+ case SG_VERTEXFORMAT_UBYTE4N: return 4;
8379+ case SG_VERTEXFORMAT_SHORT2: return 4;
8380+ case SG_VERTEXFORMAT_SHORT2N: return 4;
8381+ case SG_VERTEXFORMAT_USHORT2: return 4;
8382+ case SG_VERTEXFORMAT_USHORT2N: return 4;
8383+ case SG_VERTEXFORMAT_SHORT4: return 8;
8384+ case SG_VERTEXFORMAT_SHORT4N: return 8;
8385+ case SG_VERTEXFORMAT_USHORT4: return 8;
8386+ case SG_VERTEXFORMAT_USHORT4N: return 8;
8387+ case SG_VERTEXFORMAT_INT10_N2: return 4;
8388+ case SG_VERTEXFORMAT_UINT10_N2: return 4;
8389+ case SG_VERTEXFORMAT_HALF2: return 4;
8390+ case SG_VERTEXFORMAT_HALF4: return 8;
8391+ case SG_VERTEXFORMAT_INVALID: return 0;
8392+ default:
8393+ SOKOL_UNREACHABLE;
8394+ return -1;
8395+ }
8396+}
8397+
8398+_SOKOL_PRIVATE const char* _sg_vertexformat_to_string(sg_vertex_format fmt) {
8399+ switch (fmt) {
8400+ case SG_VERTEXFORMAT_FLOAT: return "FLOAT";
8401+ case SG_VERTEXFORMAT_FLOAT2: return "FLOAT2";
8402+ case SG_VERTEXFORMAT_FLOAT3: return "FLOAT3";
8403+ case SG_VERTEXFORMAT_FLOAT4: return "FLOAT4";
8404+ case SG_VERTEXFORMAT_INT: return "INT";
8405+ case SG_VERTEXFORMAT_INT2: return "INT2";
8406+ case SG_VERTEXFORMAT_INT3: return "INT3";
8407+ case SG_VERTEXFORMAT_INT4: return "INT4";
8408+ case SG_VERTEXFORMAT_UINT: return "UINT";
8409+ case SG_VERTEXFORMAT_UINT2: return "UINT2";
8410+ case SG_VERTEXFORMAT_UINT3: return "UINT3";
8411+ case SG_VERTEXFORMAT_UINT4: return "UINT4";
8412+ case SG_VERTEXFORMAT_BYTE4: return "BYTE4";
8413+ case SG_VERTEXFORMAT_BYTE4N: return "BYTE4N";
8414+ case SG_VERTEXFORMAT_UBYTE4: return "UBYTE4";
8415+ case SG_VERTEXFORMAT_UBYTE4N: return "UBYTE4N";
8416+ case SG_VERTEXFORMAT_SHORT2: return "SHORT2";
8417+ case SG_VERTEXFORMAT_SHORT2N: return "SHORT2N";
8418+ case SG_VERTEXFORMAT_USHORT2: return "USHORT2";
8419+ case SG_VERTEXFORMAT_USHORT2N: return "USHORT2N";
8420+ case SG_VERTEXFORMAT_SHORT4: return "SHORT4";
8421+ case SG_VERTEXFORMAT_SHORT4N: return "SHORT4N";
8422+ case SG_VERTEXFORMAT_USHORT4: return "USHORT4";
8423+ case SG_VERTEXFORMAT_USHORT4N: return "USHORT4N";
8424+ case SG_VERTEXFORMAT_INT10_N2: return "INT10_N2";
8425+ case SG_VERTEXFORMAT_UINT10_N2: return "UINT10_N2";
8426+ case SG_VERTEXFORMAT_HALF2: return "HALF2";
8427+ case SG_VERTEXFORMAT_HALF4: return "HALF4";
8428+ default:
8429+ SOKOL_UNREACHABLE;
8430+ return "INVALID";
8431+ }
8432+}
8433+
8434+_SOKOL_PRIVATE const char* _sg_shaderattrbasetype_to_string(sg_shader_attr_base_type b) {
8435+ switch (b) {
8436+ case SG_SHADERATTRBASETYPE_UNDEFINED: return "UNDEFINED";
8437+ case SG_SHADERATTRBASETYPE_FLOAT: return "FLOAT";
8438+ case SG_SHADERATTRBASETYPE_SINT: return "SINT";
8439+ case SG_SHADERATTRBASETYPE_UINT: return "UINT";
8440+ default:
8441+ SOKOL_UNREACHABLE;
8442+ return "INVALID";
8443+ }
8444+}
8445+
8446+_SOKOL_PRIVATE sg_shader_attr_base_type _sg_vertexformat_basetype(sg_vertex_format fmt) {
8447+ switch (fmt) {
8448+ case SG_VERTEXFORMAT_FLOAT:
8449+ case SG_VERTEXFORMAT_FLOAT2:
8450+ case SG_VERTEXFORMAT_FLOAT3:
8451+ case SG_VERTEXFORMAT_FLOAT4:
8452+ case SG_VERTEXFORMAT_HALF2:
8453+ case SG_VERTEXFORMAT_HALF4:
8454+ case SG_VERTEXFORMAT_BYTE4N:
8455+ case SG_VERTEXFORMAT_UBYTE4N:
8456+ case SG_VERTEXFORMAT_SHORT2N:
8457+ case SG_VERTEXFORMAT_USHORT2N:
8458+ case SG_VERTEXFORMAT_SHORT4N:
8459+ case SG_VERTEXFORMAT_USHORT4N:
8460+ case SG_VERTEXFORMAT_INT10_N2:
8461+ case SG_VERTEXFORMAT_UINT10_N2:
8462+ return SG_SHADERATTRBASETYPE_FLOAT;
8463+ case SG_VERTEXFORMAT_INT:
8464+ case SG_VERTEXFORMAT_INT2:
8465+ case SG_VERTEXFORMAT_INT3:
8466+ case SG_VERTEXFORMAT_INT4:
8467+ case SG_VERTEXFORMAT_BYTE4:
8468+ case SG_VERTEXFORMAT_SHORT2:
8469+ case SG_VERTEXFORMAT_SHORT4:
8470+ return SG_SHADERATTRBASETYPE_SINT;
8471+ case SG_VERTEXFORMAT_UINT:
8472+ case SG_VERTEXFORMAT_UINT2:
8473+ case SG_VERTEXFORMAT_UINT3:
8474+ case SG_VERTEXFORMAT_UINT4:
8475+ case SG_VERTEXFORMAT_UBYTE4:
8476+ case SG_VERTEXFORMAT_USHORT2:
8477+ case SG_VERTEXFORMAT_USHORT4:
8478+ return SG_SHADERATTRBASETYPE_UINT;
8479+ default:
8480+ SOKOL_UNREACHABLE;
8481+ return SG_SHADERATTRBASETYPE_UNDEFINED;
8482+ }
8483+}
8484+
8485+_SOKOL_PRIVATE uint32_t _sg_uniform_alignment(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) {
8486+ if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) {
8487+ return 1;
8488+ } else {
8489+ SOKOL_ASSERT(array_count > 0);
8490+ if (array_count == 1) {
8491+ switch (type) {
8492+ case SG_UNIFORMTYPE_FLOAT:
8493+ case SG_UNIFORMTYPE_INT:
8494+ return 4;
8495+ case SG_UNIFORMTYPE_FLOAT2:
8496+ case SG_UNIFORMTYPE_INT2:
8497+ return 8;
8498+ case SG_UNIFORMTYPE_FLOAT3:
8499+ case SG_UNIFORMTYPE_FLOAT4:
8500+ case SG_UNIFORMTYPE_INT3:
8501+ case SG_UNIFORMTYPE_INT4:
8502+ return 16;
8503+ case SG_UNIFORMTYPE_MAT4:
8504+ return 16;
8505+ default:
8506+ SOKOL_UNREACHABLE;
8507+ return 1;
8508+ }
8509+ } else {
8510+ return 16;
8511+ }
8512+ }
8513+}
8514+
8515+_SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) {
8516+ SOKOL_ASSERT(array_count > 0);
8517+ if (array_count == 1) {
8518+ switch (type) {
8519+ case SG_UNIFORMTYPE_FLOAT:
8520+ case SG_UNIFORMTYPE_INT:
8521+ return 4;
8522+ case SG_UNIFORMTYPE_FLOAT2:
8523+ case SG_UNIFORMTYPE_INT2:
8524+ return 8;
8525+ case SG_UNIFORMTYPE_FLOAT3:
8526+ case SG_UNIFORMTYPE_INT3:
8527+ return 12;
8528+ case SG_UNIFORMTYPE_FLOAT4:
8529+ case SG_UNIFORMTYPE_INT4:
8530+ return 16;
8531+ case SG_UNIFORMTYPE_MAT4:
8532+ return 64;
8533+ default:
8534+ SOKOL_UNREACHABLE;
8535+ return 0;
8536+ }
8537+ } else {
8538+ if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) {
8539+ switch (type) {
8540+ case SG_UNIFORMTYPE_FLOAT:
8541+ case SG_UNIFORMTYPE_INT:
8542+ return 4 * (uint32_t)array_count;
8543+ case SG_UNIFORMTYPE_FLOAT2:
8544+ case SG_UNIFORMTYPE_INT2:
8545+ return 8 * (uint32_t)array_count;
8546+ case SG_UNIFORMTYPE_FLOAT3:
8547+ case SG_UNIFORMTYPE_INT3:
8548+ return 12 * (uint32_t)array_count;
8549+ case SG_UNIFORMTYPE_FLOAT4:
8550+ case SG_UNIFORMTYPE_INT4:
8551+ return 16 * (uint32_t)array_count;
8552+ case SG_UNIFORMTYPE_MAT4:
8553+ return 64 * (uint32_t)array_count;
8554+ default:
8555+ SOKOL_UNREACHABLE;
8556+ return 0;
8557+ }
8558+ } else {
8559+ switch (type) {
8560+ case SG_UNIFORMTYPE_FLOAT:
8561+ case SG_UNIFORMTYPE_FLOAT2:
8562+ case SG_UNIFORMTYPE_FLOAT3:
8563+ case SG_UNIFORMTYPE_FLOAT4:
8564+ case SG_UNIFORMTYPE_INT:
8565+ case SG_UNIFORMTYPE_INT2:
8566+ case SG_UNIFORMTYPE_INT3:
8567+ case SG_UNIFORMTYPE_INT4:
8568+ return 16 * (uint32_t)array_count;
8569+ case SG_UNIFORMTYPE_MAT4:
8570+ return 64 * (uint32_t)array_count;
8571+ default:
8572+ SOKOL_UNREACHABLE;
8573+ return 0;
8574+ }
8575+ }
8576+ }
8577+}
8578+
8579+_SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) {
8580+ switch (fmt) {
8581+ case SG_PIXELFORMAT_BC1_RGBA:
8582+ case SG_PIXELFORMAT_BC2_RGBA:
8583+ case SG_PIXELFORMAT_BC3_RGBA:
8584+ case SG_PIXELFORMAT_BC3_SRGBA:
8585+ case SG_PIXELFORMAT_BC4_R:
8586+ case SG_PIXELFORMAT_BC4_RSN:
8587+ case SG_PIXELFORMAT_BC5_RG:
8588+ case SG_PIXELFORMAT_BC5_RGSN:
8589+ case SG_PIXELFORMAT_BC6H_RGBF:
8590+ case SG_PIXELFORMAT_BC6H_RGBUF:
8591+ case SG_PIXELFORMAT_BC7_RGBA:
8592+ case SG_PIXELFORMAT_BC7_SRGBA:
8593+ case SG_PIXELFORMAT_ETC2_RGB8:
8594+ case SG_PIXELFORMAT_ETC2_SRGB8:
8595+ case SG_PIXELFORMAT_ETC2_RGB8A1:
8596+ case SG_PIXELFORMAT_ETC2_RGBA8:
8597+ case SG_PIXELFORMAT_ETC2_SRGB8A8:
8598+ case SG_PIXELFORMAT_EAC_R11:
8599+ case SG_PIXELFORMAT_EAC_R11SN:
8600+ case SG_PIXELFORMAT_EAC_RG11:
8601+ case SG_PIXELFORMAT_EAC_RG11SN:
8602+ case SG_PIXELFORMAT_ASTC_4x4_RGBA:
8603+ case SG_PIXELFORMAT_ASTC_4x4_SRGBA:
8604+ return true;
8605+ default:
8606+ return false;
8607+ }
8608+}
8609+
8610+_SOKOL_PRIVATE bool _sg_is_valid_attachment_color_format(sg_pixel_format fmt) {
8611+ const int fmt_index = (int) fmt;
8612+ SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM));
8613+ return _sg.formats[fmt_index].render && !_sg.formats[fmt_index].depth;
8614+}
8615+
8616+_SOKOL_PRIVATE bool _sg_is_valid_attachment_depth_format(sg_pixel_format fmt) {
8617+ const int fmt_index = (int) fmt;
8618+ SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM));
8619+ return _sg.formats[fmt_index].render && _sg.formats[fmt_index].depth;
8620+}
8621+
8622+_SOKOL_PRIVATE bool _sg_is_valid_storage_image_format(sg_pixel_format fmt) {
8623+ const int fmt_index = (int) fmt;
8624+ SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM));
8625+ return _sg.formats[fmt_index].read || _sg.formats[fmt_index].write;
8626+}
8627+
8628+_SOKOL_PRIVATE bool _sg_is_depth_or_depth_stencil_format(sg_pixel_format fmt) {
8629+ return (SG_PIXELFORMAT_DEPTH == fmt) || (SG_PIXELFORMAT_DEPTH_STENCIL == fmt);
8630+}
8631+
8632+_SOKOL_PRIVATE bool _sg_is_depth_stencil_format(sg_pixel_format fmt) {
8633+ return (SG_PIXELFORMAT_DEPTH_STENCIL == fmt);
8634+}
8635+
8636+_SOKOL_PRIVATE bool _sg_is_srgb_format(sg_pixel_format fmt) {
8637+ switch (fmt) {
8638+ case SG_PIXELFORMAT_SRGB8A8:
8639+ case SG_PIXELFORMAT_SBGR8A8:
8640+ return true;
8641+ default:
8642+ return false;
8643+ }
8644+}
8645+
8646+_SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) {
8647+ switch (fmt) {
8648+ case SG_PIXELFORMAT_R8:
8649+ case SG_PIXELFORMAT_R8SN:
8650+ case SG_PIXELFORMAT_R8UI:
8651+ case SG_PIXELFORMAT_R8SI:
8652+ return 1;
8653+ case SG_PIXELFORMAT_R16:
8654+ case SG_PIXELFORMAT_R16SN:
8655+ case SG_PIXELFORMAT_R16UI:
8656+ case SG_PIXELFORMAT_R16SI:
8657+ case SG_PIXELFORMAT_R16F:
8658+ case SG_PIXELFORMAT_RG8:
8659+ case SG_PIXELFORMAT_RG8SN:
8660+ case SG_PIXELFORMAT_RG8UI:
8661+ case SG_PIXELFORMAT_RG8SI:
8662+ return 2;
8663+ case SG_PIXELFORMAT_R32UI:
8664+ case SG_PIXELFORMAT_R32SI:
8665+ case SG_PIXELFORMAT_R32F:
8666+ case SG_PIXELFORMAT_RG16:
8667+ case SG_PIXELFORMAT_RG16SN:
8668+ case SG_PIXELFORMAT_RG16UI:
8669+ case SG_PIXELFORMAT_RG16SI:
8670+ case SG_PIXELFORMAT_RG16F:
8671+ case SG_PIXELFORMAT_RGBA8:
8672+ case SG_PIXELFORMAT_SRGB8A8:
8673+ case SG_PIXELFORMAT_RGBA8SN:
8674+ case SG_PIXELFORMAT_RGBA8UI:
8675+ case SG_PIXELFORMAT_RGBA8SI:
8676+ case SG_PIXELFORMAT_BGRA8:
8677+ case SG_PIXELFORMAT_SBGR8A8:
8678+ case SG_PIXELFORMAT_RGB10A2:
8679+ case SG_PIXELFORMAT_RG11B10F:
8680+ case SG_PIXELFORMAT_RGB9E5:
8681+ return 4;
8682+ case SG_PIXELFORMAT_RG32UI:
8683+ case SG_PIXELFORMAT_RG32SI:
8684+ case SG_PIXELFORMAT_RG32F:
8685+ case SG_PIXELFORMAT_RGBA16:
8686+ case SG_PIXELFORMAT_RGBA16SN:
8687+ case SG_PIXELFORMAT_RGBA16UI:
8688+ case SG_PIXELFORMAT_RGBA16SI:
8689+ case SG_PIXELFORMAT_RGBA16F:
8690+ return 8;
8691+ case SG_PIXELFORMAT_RGBA32UI:
8692+ case SG_PIXELFORMAT_RGBA32SI:
8693+ case SG_PIXELFORMAT_RGBA32F:
8694+ return 16;
8695+ case SG_PIXELFORMAT_DEPTH:
8696+ case SG_PIXELFORMAT_DEPTH_STENCIL:
8697+ return 4;
8698+ default:
8699+ SOKOL_UNREACHABLE;
8700+ return 0;
8701+ }
8702+}
8703+
8704+// return the texture block width/height of an image format
8705+_SOKOL_PRIVATE int _sg_block_dim(sg_pixel_format fmt) {
8706+ if (_sg_is_compressed_pixel_format(fmt)) {
8707+ return 4;
8708+ } else {
8709+ return 1;
8710+ }
8711+}
8712+
8713+// return texture block size in bytes
8714+_SOKOL_PRIVATE int _sg_block_bytesize(sg_pixel_format fmt) {
8715+ switch (fmt) {
8716+ case SG_PIXELFORMAT_BC1_RGBA:
8717+ case SG_PIXELFORMAT_BC4_R:
8718+ case SG_PIXELFORMAT_BC4_RSN:
8719+ case SG_PIXELFORMAT_ETC2_RGB8:
8720+ case SG_PIXELFORMAT_ETC2_SRGB8:
8721+ case SG_PIXELFORMAT_ETC2_RGB8A1:
8722+ case SG_PIXELFORMAT_EAC_R11:
8723+ case SG_PIXELFORMAT_EAC_R11SN:
8724+ return 8;
8725+ case SG_PIXELFORMAT_BC2_RGBA:
8726+ case SG_PIXELFORMAT_BC3_RGBA:
8727+ case SG_PIXELFORMAT_BC3_SRGBA:
8728+ case SG_PIXELFORMAT_BC5_RG:
8729+ case SG_PIXELFORMAT_BC5_RGSN:
8730+ case SG_PIXELFORMAT_BC6H_RGBF:
8731+ case SG_PIXELFORMAT_BC6H_RGBUF:
8732+ case SG_PIXELFORMAT_BC7_RGBA:
8733+ case SG_PIXELFORMAT_BC7_SRGBA:
8734+ case SG_PIXELFORMAT_ETC2_RGBA8:
8735+ case SG_PIXELFORMAT_ETC2_SRGB8A8:
8736+ case SG_PIXELFORMAT_EAC_RG11:
8737+ case SG_PIXELFORMAT_EAC_RG11SN:
8738+ case SG_PIXELFORMAT_ASTC_4x4_RGBA:
8739+ case SG_PIXELFORMAT_ASTC_4x4_SRGBA:
8740+ return 16;
8741+ default:
8742+ return _sg_pixelformat_bytesize(fmt);
8743+ }
8744+}
8745+
8746+/* return row pitch for an image
8747+
8748+ see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp
8749+*/
8750+_SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) {
8751+ const int block_dim = _sg_block_dim(fmt);
8752+ const int num_blocks_in_row = (width + (block_dim-1)) / block_dim;
8753+ const int block_num_bytes = _sg_block_bytesize(fmt);
8754+ int pitch = num_blocks_in_row * block_num_bytes;
8755+ pitch = (pitch < block_num_bytes) ? block_num_bytes : pitch;
8756+ pitch = _sg_roundup(pitch, row_align);
8757+ return pitch;
8758+}
8759+
8760+// compute the number of rows in a surface depending on pixel format
8761+_SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) {
8762+ const int block_dim = _sg_block_dim(fmt);
8763+ int num_rows = (height + (block_dim-1)) / block_dim;
8764+ if (num_rows < 1) {
8765+ num_rows = 1;
8766+ }
8767+ return num_rows;
8768+}
8769+
8770+/* return pitch of a 2D subimage / texture slice
8771+ see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp
8772+*/
8773+_SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align) {
8774+ int num_rows = _sg_num_rows(fmt, height);
8775+ return num_rows * _sg_row_pitch(fmt, width, row_align);
8776+}
8777+
8778+// capability table pixel format helper functions
8779+_SOKOL_PRIVATE void _sg_pixelformat_all(_sg_pixelformat_info_t* pfi) {
8780+ pfi->sample = true;
8781+ pfi->filter = true;
8782+ pfi->blend = true;
8783+ pfi->render = true;
8784+ pfi->msaa = true;
8785+}
8786+
8787+_SOKOL_PRIVATE void _sg_pixelformat_s(_sg_pixelformat_info_t* pfi) {
8788+ pfi->sample = true;
8789+}
8790+
8791+_SOKOL_PRIVATE void _sg_pixelformat_sf(_sg_pixelformat_info_t* pfi) {
8792+ pfi->sample = true;
8793+ pfi->filter = true;
8794+}
8795+
8796+_SOKOL_PRIVATE void _sg_pixelformat_sr(_sg_pixelformat_info_t* pfi) {
8797+ pfi->sample = true;
8798+ pfi->render = true;
8799+}
8800+
8801+_SOKOL_PRIVATE void _sg_pixelformat_sfr(_sg_pixelformat_info_t* pfi) {
8802+ pfi->sample = true;
8803+ pfi->filter = true;
8804+ pfi->render = true;
8805+}
8806+
8807+_SOKOL_PRIVATE void _sg_pixelformat_srmd(_sg_pixelformat_info_t* pfi) {
8808+ pfi->sample = true;
8809+ pfi->render = true;
8810+ pfi->msaa = true;
8811+ pfi->depth = true;
8812+}
8813+
8814+_SOKOL_PRIVATE void _sg_pixelformat_srm(_sg_pixelformat_info_t* pfi) {
8815+ pfi->sample = true;
8816+ pfi->render = true;
8817+ pfi->msaa = true;
8818+}
8819+
8820+_SOKOL_PRIVATE void _sg_pixelformat_sfrm(_sg_pixelformat_info_t* pfi) {
8821+ pfi->sample = true;
8822+ pfi->filter = true;
8823+ pfi->render = true;
8824+ pfi->msaa = true;
8825+}
8826+_SOKOL_PRIVATE void _sg_pixelformat_sbrm(_sg_pixelformat_info_t* pfi) {
8827+ pfi->sample = true;
8828+ pfi->blend = true;
8829+ pfi->render = true;
8830+ pfi->msaa = true;
8831+}
8832+
8833+_SOKOL_PRIVATE void _sg_pixelformat_sbr(_sg_pixelformat_info_t* pfi) {
8834+ pfi->sample = true;
8835+ pfi->blend = true;
8836+ pfi->render = true;
8837+}
8838+
8839+_SOKOL_PRIVATE void _sg_pixelformat_sfbr(_sg_pixelformat_info_t* pfi) {
8840+ pfi->sample = true;
8841+ pfi->filter = true;
8842+ pfi->blend = true;
8843+ pfi->render = true;
8844+}
8845+
8846+_SOKOL_PRIVATE void _sg_pixelformat_compute_all(_sg_pixelformat_info_t* pfi) {
8847+ pfi->read = true;
8848+ pfi->write = true;
8849+}
8850+
8851+_SOKOL_PRIVATE void _sg_pixelformat_compute_writeonly(_sg_pixelformat_info_t* pfi) {
8852+ pfi->read = false;
8853+ pfi->write = true;
8854+}
8855+
8856+_SOKOL_PRIVATE sg_pass_action _sg_pass_action_defaults(const sg_pass_action* action) {
8857+ SOKOL_ASSERT(action);
8858+ sg_pass_action res = *action;
8859+ for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) {
8860+ if (res.colors[i].load_action == _SG_LOADACTION_DEFAULT) {
8861+ res.colors[i].load_action = SG_LOADACTION_CLEAR;
8862+ res.colors[i].clear_value.r = SG_DEFAULT_CLEAR_RED;
8863+ res.colors[i].clear_value.g = SG_DEFAULT_CLEAR_GREEN;
8864+ res.colors[i].clear_value.b = SG_DEFAULT_CLEAR_BLUE;
8865+ res.colors[i].clear_value.a = SG_DEFAULT_CLEAR_ALPHA;
8866+ }
8867+ if (res.colors[i].store_action == _SG_STOREACTION_DEFAULT) {
8868+ res.colors[i].store_action = SG_STOREACTION_STORE;
8869+ }
8870+ }
8871+ if (res.depth.load_action == _SG_LOADACTION_DEFAULT) {
8872+ res.depth.load_action = SG_LOADACTION_CLEAR;
8873+ res.depth.clear_value = SG_DEFAULT_CLEAR_DEPTH;
8874+ }
8875+ if (res.depth.store_action == _SG_STOREACTION_DEFAULT) {
8876+ res.depth.store_action = SG_STOREACTION_DONTCARE;
8877+ }
8878+ if (res.stencil.load_action == _SG_LOADACTION_DEFAULT) {
8879+ res.stencil.load_action = SG_LOADACTION_CLEAR;
8880+ res.stencil.clear_value = SG_DEFAULT_CLEAR_STENCIL;
8881+ }
8882+ if (res.stencil.store_action == _SG_STOREACTION_DEFAULT) {
8883+ res.stencil.store_action = SG_STOREACTION_DONTCARE;
8884+ }
8885+ return res;
8886+}
8887+
8888+// ██████ ██ ██ ███ ███ ███ ███ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████
8889+// ██ ██ ██ ██ ████ ████ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██
8890+// ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ████ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██
8891+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
8892+// ██████ ██████ ██ ██ ██ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████
8893+//
8894+// >>dummy backend
8895+#if defined(SOKOL_DUMMY_BACKEND)
8896+
8897+_SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) {
8898+ SOKOL_ASSERT(desc);
8899+ _SOKOL_UNUSED(desc);
8900+ _sg.backend = SG_BACKEND_DUMMY;
8901+ for (int i = SG_PIXELFORMAT_R8; i < SG_PIXELFORMAT_BC1_RGBA; i++) {
8902+ _sg.formats[i].sample = true;
8903+ _sg.formats[i].filter = true;
8904+ _sg.formats[i].render = true;
8905+ _sg.formats[i].blend = true;
8906+ _sg.formats[i].msaa = true;
8907+ }
8908+ _sg.formats[SG_PIXELFORMAT_DEPTH].depth = true;
8909+ _sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL].depth = true;
8910+ _sg.limits.max_image_size_2d = 1024;
8911+ _sg.limits.max_image_size_cube = 1024;
8912+ _sg.limits.max_image_size_3d = 1024;
8913+ _sg.limits.max_image_size_array = 1024;
8914+ _sg.limits.max_image_array_layers = 1024;
8915+ _sg.limits.max_vertex_attrs = 16;
8916+ _sg.limits.max_color_attachments = SG_MAX_PORTABLE_COLOR_ATTACHMENTS;
8917+ _sg.limits.max_texture_bindings_per_stage = SG_MAX_PORTABLE_TEXTURE_BINDINGS_PER_STAGE;
8918+ _sg.limits.max_storage_buffer_bindings_per_stage = SG_MAX_PORTABLE_STORAGEBUFFER_BINDINGS_PER_STAGE;
8919+ _sg.limits.max_storage_image_bindings_per_stage = SG_MAX_PORTABLE_STORAGEIMAGE_BINDINGS_PER_STAGE;
8920+}
8921+
8922+_SOKOL_PRIVATE void _sg_dummy_discard_backend(void) {
8923+ // empty
8924+}
8925+
8926+_SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) {
8927+ // empty
8928+}
8929+
8930+_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) {
8931+ SOKOL_ASSERT(buf && desc);
8932+ _SOKOL_UNUSED(buf);
8933+ _SOKOL_UNUSED(desc);
8934+ return SG_RESOURCESTATE_VALID;
8935+}
8936+
8937+_SOKOL_PRIVATE void _sg_dummy_discard_buffer(_sg_buffer_t* buf) {
8938+ SOKOL_ASSERT(buf);
8939+ _SOKOL_UNUSED(buf);
8940+}
8941+
8942+_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_image(_sg_image_t* img, const sg_image_desc* desc) {
8943+ SOKOL_ASSERT(img && desc);
8944+ _SOKOL_UNUSED(img);
8945+ _SOKOL_UNUSED(desc);
8946+ return SG_RESOURCESTATE_VALID;
8947+}
8948+
8949+_SOKOL_PRIVATE void _sg_dummy_discard_image(_sg_image_t* img) {
8950+ SOKOL_ASSERT(img);
8951+ _SOKOL_UNUSED(img);
8952+}
8953+
8954+_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) {
8955+ SOKOL_ASSERT(smp && desc);
8956+ _SOKOL_UNUSED(smp);
8957+ _SOKOL_UNUSED(desc);
8958+ return SG_RESOURCESTATE_VALID;
8959+}
8960+
8961+_SOKOL_PRIVATE void _sg_dummy_discard_sampler(_sg_sampler_t* smp) {
8962+ SOKOL_ASSERT(smp);
8963+ _SOKOL_UNUSED(smp);
8964+}
8965+
8966+_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) {
8967+ SOKOL_ASSERT(shd && desc);
8968+ _SOKOL_UNUSED(shd);
8969+ _SOKOL_UNUSED(desc);
8970+ return SG_RESOURCESTATE_VALID;
8971+}
8972+
8973+_SOKOL_PRIVATE void _sg_dummy_discard_shader(_sg_shader_t* shd) {
8974+ SOKOL_ASSERT(shd);
8975+ _SOKOL_UNUSED(shd);
8976+}
8977+
8978+_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) {
8979+ SOKOL_ASSERT(pip && desc);
8980+ _SOKOL_UNUSED(pip);
8981+ _SOKOL_UNUSED(desc);
8982+ return SG_RESOURCESTATE_VALID;
8983+}
8984+
8985+_SOKOL_PRIVATE void _sg_dummy_discard_pipeline(_sg_pipeline_t* pip) {
8986+ SOKOL_ASSERT(pip);
8987+ _SOKOL_UNUSED(pip);
8988+}
8989+
8990+_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_view(_sg_view_t* view, const sg_view_desc* desc) {
8991+ SOKOL_ASSERT(view && desc);
8992+ _SOKOL_UNUSED(view);
8993+ _SOKOL_UNUSED(desc);
8994+ return SG_RESOURCESTATE_VALID;
8995+}
8996+
8997+_SOKOL_PRIVATE void _sg_dummy_discard_view(_sg_view_t* view) {
8998+ SOKOL_ASSERT(view);
8999+ _SOKOL_UNUSED(view);
9000+}
9001+
9002+_SOKOL_PRIVATE void _sg_dummy_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) {
9003+ SOKOL_ASSERT(pass && atts);
9004+ _SOKOL_UNUSED(pass);
9005+ _SOKOL_UNUSED(atts);
9006+}
9007+
9008+_SOKOL_PRIVATE void _sg_dummy_end_pass(const _sg_attachments_ptrs_t* atts) {
9009+ SOKOL_ASSERT(atts);
9010+ _SOKOL_UNUSED(atts);
9011+}
9012+
9013+_SOKOL_PRIVATE void _sg_dummy_commit(void) {
9014+ // empty
9015+}
9016+
9017+_SOKOL_PRIVATE void _sg_dummy_apply_viewport(int x, int y, int w, int h, bool origin_top_left) {
9018+ _SOKOL_UNUSED(x);
9019+ _SOKOL_UNUSED(y);
9020+ _SOKOL_UNUSED(w);
9021+ _SOKOL_UNUSED(h);
9022+ _SOKOL_UNUSED(origin_top_left);
9023+}
9024+
9025+_SOKOL_PRIVATE void _sg_dummy_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) {
9026+ _SOKOL_UNUSED(x);
9027+ _SOKOL_UNUSED(y);
9028+ _SOKOL_UNUSED(w);
9029+ _SOKOL_UNUSED(h);
9030+ _SOKOL_UNUSED(origin_top_left);
9031+}
9032+
9033+_SOKOL_PRIVATE void _sg_dummy_apply_pipeline(_sg_pipeline_t* pip) {
9034+ SOKOL_ASSERT(pip);
9035+ _SOKOL_UNUSED(pip);
9036+}
9037+
9038+_SOKOL_PRIVATE bool _sg_dummy_apply_bindings(_sg_bindings_ptrs_t* bnd) {
9039+ SOKOL_ASSERT(bnd);
9040+ SOKOL_ASSERT(bnd->pip);
9041+ _SOKOL_UNUSED(bnd);
9042+ return true;
9043+}
9044+
9045+_SOKOL_PRIVATE void _sg_dummy_apply_uniforms(int ub_slot, const sg_range* data) {
9046+ _SOKOL_UNUSED(ub_slot);
9047+ _SOKOL_UNUSED(data);
9048+}
9049+
9050+_SOKOL_PRIVATE void _sg_dummy_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) {
9051+ _SOKOL_UNUSED(base_element);
9052+ _SOKOL_UNUSED(num_elements);
9053+ _SOKOL_UNUSED(num_instances);
9054+ _SOKOL_UNUSED(base_vertex);
9055+ _SOKOL_UNUSED(base_instance);
9056+}
9057+
9058+_SOKOL_PRIVATE void _sg_dummy_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) {
9059+ _SOKOL_UNUSED(num_groups_x);
9060+ _SOKOL_UNUSED(num_groups_y);
9061+ _SOKOL_UNUSED(num_groups_z);
9062+}
9063+
9064+_SOKOL_PRIVATE void _sg_dummy_update_buffer(_sg_buffer_t* buf, const sg_range* data) {
9065+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
9066+ _SOKOL_UNUSED(data);
9067+ if (++buf->cmn.active_slot >= buf->cmn.num_slots) {
9068+ buf->cmn.active_slot = 0;
9069+ }
9070+}
9071+
9072+_SOKOL_PRIVATE bool _sg_dummy_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) {
9073+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
9074+ _SOKOL_UNUSED(data);
9075+ if (new_frame) {
9076+ if (++buf->cmn.active_slot >= buf->cmn.num_slots) {
9077+ buf->cmn.active_slot = 0;
9078+ }
9079+ }
9080+ return true;
9081+}
9082+
9083+_SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data* data) {
9084+ SOKOL_ASSERT(img && data);
9085+ _SOKOL_UNUSED(data);
9086+ if (++img->cmn.active_slot >= img->cmn.num_slots) {
9087+ img->cmn.active_slot = 0;
9088+ }
9089+}
9090+
9091+// ██████ ██████ ███████ ███ ██ ██████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████
9092+// ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██
9093+// ██ ██ ██████ █████ ██ ██ ██ ██ ███ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██
9094+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
9095+// ██████ ██ ███████ ██ ████ ██████ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████
9096+//
9097+// >>opengl backend
9098+#elif defined(_SOKOL_ANY_GL)
9099+
9100+// optional GL loader for win32
9101+#if defined(_SOKOL_USE_WIN32_GL_LOADER)
9102+
9103+#ifndef SG_GL_FUNCS_EXT
9104+#define SG_GL_FUNCS_EXT
9105+#endif
9106+
9107+// X Macro list of GL function names and signatures
9108+#define _SG_GL_FUNCS \
9109+ SG_GL_FUNCS_EXT \
9110+ _SG_XMACRO(glBindVertexArray, void, (GLuint array)) \
9111+ _SG_XMACRO(glFramebufferTextureLayer, void, (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)) \
9112+ _SG_XMACRO(glGenFramebuffers, void, (GLsizei n, GLuint * framebuffers)) \
9113+ _SG_XMACRO(glBindFramebuffer, void, (GLenum target, GLuint framebuffer)) \
9114+ _SG_XMACRO(glBindRenderbuffer, void, (GLenum target, GLuint renderbuffer)) \
9115+ _SG_XMACRO(glGetStringi, const GLubyte *, (GLenum name, GLuint index)) \
9116+ _SG_XMACRO(glClearBufferfi, void, (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)) \
9117+ _SG_XMACRO(glClearBufferfv, void, (GLenum buffer, GLint drawbuffer, const GLfloat * value)) \
9118+ _SG_XMACRO(glClearBufferuiv, void, (GLenum buffer, GLint drawbuffer, const GLuint * value)) \
9119+ _SG_XMACRO(glClearBufferiv, void, (GLenum buffer, GLint drawbuffer, const GLint * value)) \
9120+ _SG_XMACRO(glDeleteRenderbuffers, void, (GLsizei n, const GLuint * renderbuffers)) \
9121+ _SG_XMACRO(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat * value)) \
9122+ _SG_XMACRO(glUniform2fv, void, (GLint location, GLsizei count, const GLfloat * value)) \
9123+ _SG_XMACRO(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat * value)) \
9124+ _SG_XMACRO(glUniform4fv, void, (GLint location, GLsizei count, const GLfloat * value)) \
9125+ _SG_XMACRO(glUniform1iv, void, (GLint location, GLsizei count, const GLint * value)) \
9126+ _SG_XMACRO(glUniform2iv, void, (GLint location, GLsizei count, const GLint * value)) \
9127+ _SG_XMACRO(glUniform3iv, void, (GLint location, GLsizei count, const GLint * value)) \
9128+ _SG_XMACRO(glUniform4iv, void, (GLint location, GLsizei count, const GLint * value)) \
9129+ _SG_XMACRO(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)) \
9130+ _SG_XMACRO(glUseProgram, void, (GLuint program)) \
9131+ _SG_XMACRO(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length)) \
9132+ _SG_XMACRO(glLinkProgram, void, (GLuint program)) \
9133+ _SG_XMACRO(glGetUniformLocation, GLint, (GLuint program, const GLchar * name)) \
9134+ _SG_XMACRO(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint * params)) \
9135+ _SG_XMACRO(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \
9136+ _SG_XMACRO(glGetAttribLocation, GLint, (GLuint program, const GLchar * name)) \
9137+ _SG_XMACRO(glDisableVertexAttribArray, void, (GLuint index)) \
9138+ _SG_XMACRO(glDeleteShader, void, (GLuint shader)) \
9139+ _SG_XMACRO(glDeleteProgram, void, (GLuint program)) \
9140+ _SG_XMACRO(glCompileShader, void, (GLuint shader)) \
9141+ _SG_XMACRO(glStencilFuncSeparate, void, (GLenum face, GLenum func, GLint ref, GLuint mask)) \
9142+ _SG_XMACRO(glStencilOpSeparate, void, (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)) \
9143+ _SG_XMACRO(glRenderbufferStorageMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)) \
9144+ _SG_XMACRO(glDrawBuffers, void, (GLsizei n, const GLenum * bufs)) \
9145+ _SG_XMACRO(glVertexAttribDivisor, void, (GLuint index, GLuint divisor)) \
9146+ _SG_XMACRO(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void * data)) \
9147+ _SG_XMACRO(glGenBuffers, void, (GLsizei n, GLuint * buffers)) \
9148+ _SG_XMACRO(glCheckFramebufferStatus, GLenum, (GLenum target)) \
9149+ _SG_XMACRO(glFramebufferRenderbuffer, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) \
9150+ _SG_XMACRO(glCompressedTexImage2D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data)) \
9151+ _SG_XMACRO(glCompressedTexImage3D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data)) \
9152+ _SG_XMACRO(glActiveTexture, void, (GLenum texture)) \
9153+ _SG_XMACRO(glTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels)) \
9154+ _SG_XMACRO(glRenderbufferStorage, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) \
9155+ _SG_XMACRO(glGenTextures, void, (GLsizei n, GLuint * textures)) \
9156+ _SG_XMACRO(glPolygonOffset, void, (GLfloat factor, GLfloat units)) \
9157+ _SG_XMACRO(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const void * indices)) \
9158+ _SG_XMACRO(glDeleteFramebuffers, void, (GLsizei n, const GLuint * framebuffers)) \
9159+ _SG_XMACRO(glBlendEquationSeparate, void, (GLenum modeRGB, GLenum modeAlpha)) \
9160+ _SG_XMACRO(glDeleteTextures, void, (GLsizei n, const GLuint * textures)) \
9161+ _SG_XMACRO(glGetProgramiv, void, (GLuint program, GLenum pname, GLint * params)) \
9162+ _SG_XMACRO(glBindTexture, void, (GLenum target, GLuint texture)) \
9163+ _SG_XMACRO(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels)) \
9164+ _SG_XMACRO(glCreateShader, GLuint, (GLenum type)) \
9165+ _SG_XMACRO(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels)) \
9166+ _SG_XMACRO(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \
9167+ _SG_XMACRO(glCreateProgram, GLuint, (void)) \
9168+ _SG_XMACRO(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \
9169+ _SG_XMACRO(glDeleteBuffers, void, (GLsizei n, const GLuint * buffers)) \
9170+ _SG_XMACRO(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) \
9171+ _SG_XMACRO(glDrawElementsInstanced, void, (GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount)) \
9172+ _SG_XMACRO(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer)) \
9173+ _SG_XMACRO(glVertexAttribIPointer, void, (GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer)) \
9174+ _SG_XMACRO(glUniform1i, void, (GLint location, GLint v0)) \
9175+ _SG_XMACRO(glDisable, void, (GLenum cap)) \
9176+ _SG_XMACRO(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \
9177+ _SG_XMACRO(glColorMaski, void, (GLuint buf, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \
9178+ _SG_XMACRO(glBindBuffer, void, (GLenum target, GLuint buffer)) \
9179+ _SG_XMACRO(glDeleteVertexArrays, void, (GLsizei n, const GLuint * arrays)) \
9180+ _SG_XMACRO(glDepthMask, void, (GLboolean flag)) \
9181+ _SG_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \
9182+ _SG_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \
9183+ _SG_XMACRO(glGenRenderbuffers, void, (GLsizei n, GLuint * renderbuffers)) \
9184+ _SG_XMACRO(glBufferData, void, (GLenum target, GLsizeiptr size, const void * data, GLenum usage)) \
9185+ _SG_XMACRO(glBlendFuncSeparate, void, (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)) \
9186+ _SG_XMACRO(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) \
9187+ _SG_XMACRO(glGetIntegerv, void, (GLenum pname, GLint * data)) \
9188+ _SG_XMACRO(glEnable, void, (GLenum cap)) \
9189+ _SG_XMACRO(glBlitFramebuffer, void, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) \
9190+ _SG_XMACRO(glStencilMask, void, (GLuint mask)) \
9191+ _SG_XMACRO(glAttachShader, void, (GLuint program, GLuint shader)) \
9192+ _SG_XMACRO(glGetError, GLenum, (void)) \
9193+ _SG_XMACRO(glBlendColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \
9194+ _SG_XMACRO(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) \
9195+ _SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, const GLfloat* params)) \
9196+ _SG_XMACRO(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \
9197+ _SG_XMACRO(glDepthFunc, void, (GLenum func)) \
9198+ _SG_XMACRO(glStencilOp , void, (GLenum fail, GLenum zfail, GLenum zpass)) \
9199+ _SG_XMACRO(glStencilFunc, void, (GLenum func, GLint ref, GLuint mask)) \
9200+ _SG_XMACRO(glEnableVertexAttribArray, void, (GLuint index)) \
9201+ _SG_XMACRO(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \
9202+ _SG_XMACRO(glReadBuffer, void, (GLenum src)) \
9203+ _SG_XMACRO(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels)) \
9204+ _SG_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \
9205+ _SG_XMACRO(glFrontFace, void, (GLenum mode)) \
9206+ _SG_XMACRO(glCullFace, void, (GLenum mode)) \
9207+ _SG_XMACRO(glPixelStorei, void, (GLenum pname, GLint param)) \
9208+ _SG_XMACRO(glBindSampler, void, (GLuint unit, GLuint sampler)) \
9209+ _SG_XMACRO(glGenSamplers, void, (GLsizei n, GLuint* samplers)) \
9210+ _SG_XMACRO(glSamplerParameteri, void, (GLuint sampler, GLenum pname, GLint param)) \
9211+ _SG_XMACRO(glSamplerParameterf, void, (GLuint sampler, GLenum pname, GLfloat param)) \
9212+ _SG_XMACRO(glSamplerParameterfv, void, (GLuint sampler, GLenum pname, const GLfloat* params)) \
9213+ _SG_XMACRO(glDeleteSamplers, void, (GLsizei n, const GLuint* samplers)) \
9214+ _SG_XMACRO(glBindBufferBase, void, (GLenum target, GLuint index, GLuint buffer)) \
9215+ _SG_XMACRO(glBindBufferRange, void, (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size)) \
9216+ _SG_XMACRO(glTexImage2DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)) \
9217+ _SG_XMACRO(glTexImage3DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations)) \
9218+ _SG_XMACRO(glDispatchCompute, void, (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z)) \
9219+ _SG_XMACRO(glMemoryBarrier, void, (GLbitfield barriers)) \
9220+ _SG_XMACRO(glBindImageTexture, void, (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format)) \
9221+ _SG_XMACRO(glTexStorage2DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)) \
9222+ _SG_XMACRO(glTexStorage2D, void, (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)) \
9223+ _SG_XMACRO(glTexStorage3DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations)) \
9224+ _SG_XMACRO(glTexStorage3D, void, (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)) \
9225+ _SG_XMACRO(glCompressedTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data)) \
9226+ _SG_XMACRO(glCompressedTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data)) \
9227+ _SG_XMACRO(glTextureView, void, (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers)) \
9228+ _SG_XMACRO(glDrawElementsBaseVertex, void, (GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex)) \
9229+ _SG_XMACRO(glDrawElementsInstancedBaseVertex, void, (GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex)) \
9230+ _SG_XMACRO(glDrawElementsInstancedBaseVertexBaseInstance, void, (GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance)) \
9231+ _SG_XMACRO(glDrawArraysInstancedBaseInstance, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance))
9232+
9233+// generate GL function pointer typedefs
9234+#define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args;
9235+_SG_GL_FUNCS
9236+#undef _SG_XMACRO
9237+
9238+// generate GL function pointers
9239+#define _SG_XMACRO(name, ret, args) static PFN_ ## name name;
9240+_SG_GL_FUNCS
9241+#undef _SG_XMACRO
9242+
9243+// helper function to lookup GL functions in GL DLL
9244+typedef PROC (WINAPI * _sg_wglGetProcAddress)(LPCSTR);
9245+_SOKOL_PRIVATE void* _sg_gl_getprocaddr(const char* name, _sg_wglGetProcAddress wgl_getprocaddress) {
9246+ void* proc_addr = (void*) wgl_getprocaddress(name);
9247+ if (0 == proc_addr) {
9248+ proc_addr = (void*) GetProcAddress(_sg.gl.opengl32_dll, name);
9249+ }
9250+ SOKOL_ASSERT(proc_addr);
9251+ return proc_addr;
9252+}
9253+
9254+// populate GL function pointers
9255+_SOKOL_PRIVATE void _sg_gl_load_opengl(void) {
9256+ SOKOL_ASSERT(0 == _sg.gl.opengl32_dll);
9257+ _sg.gl.opengl32_dll = LoadLibraryA("opengl32.dll");
9258+ SOKOL_ASSERT(_sg.gl.opengl32_dll);
9259+ _sg_wglGetProcAddress wgl_getprocaddress = (_sg_wglGetProcAddress) GetProcAddress(_sg.gl.opengl32_dll, "wglGetProcAddress");
9260+ SOKOL_ASSERT(wgl_getprocaddress);
9261+ #define _SG_XMACRO(name, ret, args) name = (PFN_ ## name) _sg_gl_getprocaddr(#name, wgl_getprocaddress);
9262+ _SG_GL_FUNCS
9263+ #undef _SG_XMACRO
9264+}
9265+
9266+_SOKOL_PRIVATE void _sg_gl_unload_opengl(void) {
9267+ SOKOL_ASSERT(_sg.gl.opengl32_dll);
9268+ FreeLibrary(_sg.gl.opengl32_dll);
9269+ _sg.gl.opengl32_dll = 0;
9270+}
9271+#endif // _SOKOL_USE_WIN32_GL_LOADER
9272+
9273+//-- type translation ----------------------------------------------------------
9274+_SOKOL_PRIVATE GLenum _sg_gl_buffer_target(const sg_buffer_usage* usg) {
9275+ // NOTE: the buffer target returned here is only used for the bind point
9276+ // to copy data into the buffer, expect for WebGL2, the bind point doesn't
9277+ // need to match the later usage of the buffer (but because of the WebGL2
9278+ // restriction we cannot simply select a random bind point, because in WebGL2
9279+ // a buffer cannot 'switch' bind points later.
9280+ if (usg->vertex_buffer) {
9281+ return GL_ARRAY_BUFFER;
9282+ } else if (usg->index_buffer) {
9283+ return GL_ELEMENT_ARRAY_BUFFER;
9284+ } else if (usg->storage_buffer) {
9285+ return GL_SHADER_STORAGE_BUFFER;
9286+ } else {
9287+ SOKOL_UNREACHABLE; return 0;
9288+ }
9289+}
9290+
9291+_SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t, int sample_count) {
9292+ #if defined(SOKOL_GLCORE)
9293+ const bool msaa = sample_count > 1;
9294+ if (msaa) {
9295+ switch (t) {
9296+ case SG_IMAGETYPE_2D: return GL_TEXTURE_2D_MULTISAMPLE;
9297+ case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
9298+ default: SOKOL_UNREACHABLE; return 0;
9299+ }
9300+ } else {
9301+ switch (t) {
9302+ case SG_IMAGETYPE_2D: return GL_TEXTURE_2D;
9303+ case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP;
9304+ case SG_IMAGETYPE_3D: return GL_TEXTURE_3D;
9305+ case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY;
9306+ default: SOKOL_UNREACHABLE; return 0;
9307+ }
9308+ }
9309+ #else
9310+ SOKOL_ASSERT(sample_count == 1); _SOKOL_UNUSED(sample_count);
9311+ switch (t) {
9312+ case SG_IMAGETYPE_2D: return GL_TEXTURE_2D;
9313+ case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP;
9314+ case SG_IMAGETYPE_3D: return GL_TEXTURE_3D;
9315+ case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY;
9316+ default: SOKOL_UNREACHABLE; return 0;
9317+ }
9318+ #endif
9319+}
9320+
9321+_SOKOL_PRIVATE GLenum _sg_gl_buffer_usage(const sg_buffer_usage* usg) {
9322+ if (usg->immutable) {
9323+ return GL_STATIC_DRAW;
9324+ } else if (usg->dynamic_update) {
9325+ return GL_DYNAMIC_DRAW;
9326+ } else if (usg->stream_update) {
9327+ return GL_STREAM_DRAW;
9328+ } else {
9329+ SOKOL_UNREACHABLE; return 0;
9330+ }
9331+}
9332+
9333+_SOKOL_PRIVATE GLenum _sg_gl_shader_stage(sg_shader_stage stage) {
9334+ switch (stage) {
9335+ case SG_SHADERSTAGE_VERTEX: return GL_VERTEX_SHADER;
9336+ case SG_SHADERSTAGE_FRAGMENT: return GL_FRAGMENT_SHADER;
9337+ case SG_SHADERSTAGE_COMPUTE: return GL_COMPUTE_SHADER;
9338+ default: SOKOL_UNREACHABLE; return 0;
9339+ }
9340+}
9341+
9342+_SOKOL_PRIVATE GLint _sg_gl_vertexformat_size(sg_vertex_format fmt) {
9343+ switch (fmt) {
9344+ case SG_VERTEXFORMAT_FLOAT: return 1;
9345+ case SG_VERTEXFORMAT_FLOAT2: return 2;
9346+ case SG_VERTEXFORMAT_FLOAT3: return 3;
9347+ case SG_VERTEXFORMAT_FLOAT4: return 4;
9348+ case SG_VERTEXFORMAT_INT: return 1;
9349+ case SG_VERTEXFORMAT_INT2: return 2;
9350+ case SG_VERTEXFORMAT_INT3: return 3;
9351+ case SG_VERTEXFORMAT_INT4: return 4;
9352+ case SG_VERTEXFORMAT_UINT: return 1;
9353+ case SG_VERTEXFORMAT_UINT2: return 2;
9354+ case SG_VERTEXFORMAT_UINT3: return 3;
9355+ case SG_VERTEXFORMAT_UINT4: return 4;
9356+ case SG_VERTEXFORMAT_BYTE4: return 4;
9357+ case SG_VERTEXFORMAT_BYTE4N: return 4;
9358+ case SG_VERTEXFORMAT_UBYTE4: return 4;
9359+ case SG_VERTEXFORMAT_UBYTE4N: return 4;
9360+ case SG_VERTEXFORMAT_SHORT2: return 2;
9361+ case SG_VERTEXFORMAT_SHORT2N: return 2;
9362+ case SG_VERTEXFORMAT_USHORT2: return 2;
9363+ case SG_VERTEXFORMAT_USHORT2N: return 2;
9364+ case SG_VERTEXFORMAT_SHORT4: return 4;
9365+ case SG_VERTEXFORMAT_SHORT4N: return 4;
9366+ case SG_VERTEXFORMAT_USHORT4: return 4;
9367+ case SG_VERTEXFORMAT_USHORT4N: return 4;
9368+ case SG_VERTEXFORMAT_INT10_N2: return 4;
9369+ case SG_VERTEXFORMAT_UINT10_N2: return 4;
9370+ case SG_VERTEXFORMAT_HALF2: return 2;
9371+ case SG_VERTEXFORMAT_HALF4: return 4;
9372+ default: SOKOL_UNREACHABLE; return 0;
9373+ }
9374+}
9375+
9376+_SOKOL_PRIVATE GLenum _sg_gl_vertexformat_type(sg_vertex_format fmt) {
9377+ switch (fmt) {
9378+ case SG_VERTEXFORMAT_FLOAT:
9379+ case SG_VERTEXFORMAT_FLOAT2:
9380+ case SG_VERTEXFORMAT_FLOAT3:
9381+ case SG_VERTEXFORMAT_FLOAT4:
9382+ return GL_FLOAT;
9383+ case SG_VERTEXFORMAT_INT:
9384+ case SG_VERTEXFORMAT_INT2:
9385+ case SG_VERTEXFORMAT_INT3:
9386+ case SG_VERTEXFORMAT_INT4:
9387+ return GL_INT;
9388+ case SG_VERTEXFORMAT_UINT:
9389+ case SG_VERTEXFORMAT_UINT2:
9390+ case SG_VERTEXFORMAT_UINT3:
9391+ case SG_VERTEXFORMAT_UINT4:
9392+ return GL_UNSIGNED_INT;
9393+ case SG_VERTEXFORMAT_BYTE4:
9394+ case SG_VERTEXFORMAT_BYTE4N:
9395+ return GL_BYTE;
9396+ case SG_VERTEXFORMAT_UBYTE4:
9397+ case SG_VERTEXFORMAT_UBYTE4N:
9398+ return GL_UNSIGNED_BYTE;
9399+ case SG_VERTEXFORMAT_SHORT2:
9400+ case SG_VERTEXFORMAT_SHORT2N:
9401+ case SG_VERTEXFORMAT_SHORT4:
9402+ case SG_VERTEXFORMAT_SHORT4N:
9403+ return GL_SHORT;
9404+ case SG_VERTEXFORMAT_USHORT2:
9405+ case SG_VERTEXFORMAT_USHORT2N:
9406+ case SG_VERTEXFORMAT_USHORT4:
9407+ case SG_VERTEXFORMAT_USHORT4N:
9408+ return GL_UNSIGNED_SHORT;
9409+ case SG_VERTEXFORMAT_INT10_N2:
9410+ return GL_INT_2_10_10_10_REV;
9411+ case SG_VERTEXFORMAT_UINT10_N2:
9412+ return GL_UNSIGNED_INT_2_10_10_10_REV;
9413+ case SG_VERTEXFORMAT_HALF2:
9414+ case SG_VERTEXFORMAT_HALF4:
9415+ return GL_HALF_FLOAT;
9416+ default:
9417+ SOKOL_UNREACHABLE; return 0;
9418+ }
9419+}
9420+
9421+_SOKOL_PRIVATE GLboolean _sg_gl_vertexformat_normalized(sg_vertex_format fmt) {
9422+ switch (fmt) {
9423+ case SG_VERTEXFORMAT_BYTE4N:
9424+ case SG_VERTEXFORMAT_UBYTE4N:
9425+ case SG_VERTEXFORMAT_SHORT2N:
9426+ case SG_VERTEXFORMAT_USHORT2N:
9427+ case SG_VERTEXFORMAT_SHORT4N:
9428+ case SG_VERTEXFORMAT_USHORT4N:
9429+ case SG_VERTEXFORMAT_INT10_N2:
9430+ case SG_VERTEXFORMAT_UINT10_N2:
9431+ return GL_TRUE;
9432+ default:
9433+ return GL_FALSE;
9434+ }
9435+}
9436+
9437+_SOKOL_PRIVATE GLenum _sg_gl_primitive_type(sg_primitive_type t) {
9438+ switch (t) {
9439+ case SG_PRIMITIVETYPE_POINTS: return GL_POINTS;
9440+ case SG_PRIMITIVETYPE_LINES: return GL_LINES;
9441+ case SG_PRIMITIVETYPE_LINE_STRIP: return GL_LINE_STRIP;
9442+ case SG_PRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES;
9443+ case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
9444+ default: SOKOL_UNREACHABLE; return 0;
9445+ }
9446+}
9447+
9448+_SOKOL_PRIVATE GLenum _sg_gl_index_type(sg_index_type t) {
9449+ switch (t) {
9450+ case SG_INDEXTYPE_NONE: return 0;
9451+ case SG_INDEXTYPE_UINT16: return GL_UNSIGNED_SHORT;
9452+ case SG_INDEXTYPE_UINT32: return GL_UNSIGNED_INT;
9453+ default: SOKOL_UNREACHABLE; return 0;
9454+ }
9455+}
9456+
9457+_SOKOL_PRIVATE GLenum _sg_gl_compare_func(sg_compare_func cmp) {
9458+ switch (cmp) {
9459+ case SG_COMPAREFUNC_NEVER: return GL_NEVER;
9460+ case SG_COMPAREFUNC_LESS: return GL_LESS;
9461+ case SG_COMPAREFUNC_EQUAL: return GL_EQUAL;
9462+ case SG_COMPAREFUNC_LESS_EQUAL: return GL_LEQUAL;
9463+ case SG_COMPAREFUNC_GREATER: return GL_GREATER;
9464+ case SG_COMPAREFUNC_NOT_EQUAL: return GL_NOTEQUAL;
9465+ case SG_COMPAREFUNC_GREATER_EQUAL: return GL_GEQUAL;
9466+ case SG_COMPAREFUNC_ALWAYS: return GL_ALWAYS;
9467+ default: SOKOL_UNREACHABLE; return 0;
9468+ }
9469+}
9470+
9471+_SOKOL_PRIVATE GLenum _sg_gl_stencil_op(sg_stencil_op op) {
9472+ switch (op) {
9473+ case SG_STENCILOP_KEEP: return GL_KEEP;
9474+ case SG_STENCILOP_ZERO: return GL_ZERO;
9475+ case SG_STENCILOP_REPLACE: return GL_REPLACE;
9476+ case SG_STENCILOP_INCR_CLAMP: return GL_INCR;
9477+ case SG_STENCILOP_DECR_CLAMP: return GL_DECR;
9478+ case SG_STENCILOP_INVERT: return GL_INVERT;
9479+ case SG_STENCILOP_INCR_WRAP: return GL_INCR_WRAP;
9480+ case SG_STENCILOP_DECR_WRAP: return GL_DECR_WRAP;
9481+ default: SOKOL_UNREACHABLE; return 0;
9482+ }
9483+}
9484+
9485+_SOKOL_PRIVATE GLenum _sg_gl_blend_factor(sg_blend_factor f) {
9486+ switch (f) {
9487+ case SG_BLENDFACTOR_ZERO: return GL_ZERO;
9488+ case SG_BLENDFACTOR_ONE: return GL_ONE;
9489+ case SG_BLENDFACTOR_SRC_COLOR: return GL_SRC_COLOR;
9490+ case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR;
9491+ case SG_BLENDFACTOR_SRC_ALPHA: return GL_SRC_ALPHA;
9492+ case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA;
9493+ case SG_BLENDFACTOR_DST_COLOR: return GL_DST_COLOR;
9494+ case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR;
9495+ case SG_BLENDFACTOR_DST_ALPHA: return GL_DST_ALPHA;
9496+ case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA;
9497+ case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return GL_SRC_ALPHA_SATURATE;
9498+ case SG_BLENDFACTOR_BLEND_COLOR: return GL_CONSTANT_COLOR;
9499+ case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR;
9500+ case SG_BLENDFACTOR_BLEND_ALPHA: return GL_CONSTANT_ALPHA;
9501+ case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA;
9502+ #if defined(_SOKOL_GL_HAS_DUALSOURCEBLENDING)
9503+ case SG_BLENDFACTOR_SRC1_COLOR: return GL_SRC1_COLOR;
9504+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: return GL_ONE_MINUS_SRC1_COLOR;
9505+ case SG_BLENDFACTOR_SRC1_ALPHA: return GL_SRC1_ALPHA;
9506+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: return GL_ONE_MINUS_SRC1_ALPHA;
9507+ #endif
9508+ default: SOKOL_UNREACHABLE; return 0;
9509+ }
9510+}
9511+
9512+_SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) {
9513+ switch (op) {
9514+ case SG_BLENDOP_ADD: return GL_FUNC_ADD;
9515+ case SG_BLENDOP_SUBTRACT: return GL_FUNC_SUBTRACT;
9516+ case SG_BLENDOP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT;
9517+ case SG_BLENDOP_MIN: return GL_MIN;
9518+ case SG_BLENDOP_MAX: return GL_MAX;
9519+ default: SOKOL_UNREACHABLE; return 0;
9520+ }
9521+}
9522+
9523+_SOKOL_PRIVATE GLenum _sg_gl_min_filter(sg_filter min_f, sg_filter mipmap_f) {
9524+ if (min_f == SG_FILTER_NEAREST) {
9525+ switch (mipmap_f) {
9526+ case SG_FILTER_NEAREST: return GL_NEAREST_MIPMAP_NEAREST;
9527+ case SG_FILTER_LINEAR: return GL_NEAREST_MIPMAP_LINEAR;
9528+ default: SOKOL_UNREACHABLE; return (GLenum)0;
9529+ }
9530+ } else if (min_f == SG_FILTER_LINEAR) {
9531+ switch (mipmap_f) {
9532+ case SG_FILTER_NEAREST: return GL_LINEAR_MIPMAP_NEAREST;
9533+ case SG_FILTER_LINEAR: return GL_LINEAR_MIPMAP_LINEAR;
9534+ default: SOKOL_UNREACHABLE; return (GLenum)0;
9535+ }
9536+ } else {
9537+ SOKOL_UNREACHABLE; return (GLenum)0;
9538+ }
9539+}
9540+
9541+_SOKOL_PRIVATE GLenum _sg_gl_mag_filter(sg_filter mag_f) {
9542+ if (mag_f == SG_FILTER_NEAREST) {
9543+ return GL_NEAREST;
9544+ } else {
9545+ return GL_LINEAR;
9546+ }
9547+}
9548+
9549+_SOKOL_PRIVATE GLenum _sg_gl_wrap(sg_wrap w) {
9550+ switch (w) {
9551+ case SG_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE;
9552+ #if defined(SOKOL_GLCORE)
9553+ case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER;
9554+ #else
9555+ case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_EDGE;
9556+ #endif
9557+ case SG_WRAP_REPEAT: return GL_REPEAT;
9558+ case SG_WRAP_MIRRORED_REPEAT: return GL_MIRRORED_REPEAT;
9559+ default: SOKOL_UNREACHABLE; return 0;
9560+ }
9561+}
9562+
9563+_SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) {
9564+ switch (fmt) {
9565+ case SG_PIXELFORMAT_R8:
9566+ case SG_PIXELFORMAT_R8UI:
9567+ case SG_PIXELFORMAT_RG8:
9568+ case SG_PIXELFORMAT_RG8UI:
9569+ case SG_PIXELFORMAT_RGBA8:
9570+ case SG_PIXELFORMAT_SRGB8A8:
9571+ case SG_PIXELFORMAT_RGBA8UI:
9572+ case SG_PIXELFORMAT_BGRA8:
9573+ return GL_UNSIGNED_BYTE;
9574+ case SG_PIXELFORMAT_R8SN:
9575+ case SG_PIXELFORMAT_R8SI:
9576+ case SG_PIXELFORMAT_RG8SN:
9577+ case SG_PIXELFORMAT_RG8SI:
9578+ case SG_PIXELFORMAT_RGBA8SN:
9579+ case SG_PIXELFORMAT_RGBA8SI:
9580+ return GL_BYTE;
9581+ case SG_PIXELFORMAT_R16:
9582+ case SG_PIXELFORMAT_R16UI:
9583+ case SG_PIXELFORMAT_RG16:
9584+ case SG_PIXELFORMAT_RG16UI:
9585+ case SG_PIXELFORMAT_RGBA16:
9586+ case SG_PIXELFORMAT_RGBA16UI:
9587+ return GL_UNSIGNED_SHORT;
9588+ case SG_PIXELFORMAT_R16SN:
9589+ case SG_PIXELFORMAT_R16SI:
9590+ case SG_PIXELFORMAT_RG16SN:
9591+ case SG_PIXELFORMAT_RG16SI:
9592+ case SG_PIXELFORMAT_RGBA16SN:
9593+ case SG_PIXELFORMAT_RGBA16SI:
9594+ return GL_SHORT;
9595+ case SG_PIXELFORMAT_R16F:
9596+ case SG_PIXELFORMAT_RG16F:
9597+ case SG_PIXELFORMAT_RGBA16F:
9598+ return GL_HALF_FLOAT;
9599+ case SG_PIXELFORMAT_R32UI:
9600+ case SG_PIXELFORMAT_RG32UI:
9601+ case SG_PIXELFORMAT_RGBA32UI:
9602+ return GL_UNSIGNED_INT;
9603+ case SG_PIXELFORMAT_R32SI:
9604+ case SG_PIXELFORMAT_RG32SI:
9605+ case SG_PIXELFORMAT_RGBA32SI:
9606+ return GL_INT;
9607+ case SG_PIXELFORMAT_R32F:
9608+ case SG_PIXELFORMAT_RG32F:
9609+ case SG_PIXELFORMAT_RGBA32F:
9610+ return GL_FLOAT;
9611+ case SG_PIXELFORMAT_RGB10A2:
9612+ return GL_UNSIGNED_INT_2_10_10_10_REV;
9613+ case SG_PIXELFORMAT_RG11B10F:
9614+ return GL_UNSIGNED_INT_10F_11F_11F_REV;
9615+ case SG_PIXELFORMAT_RGB9E5:
9616+ return GL_UNSIGNED_INT_5_9_9_9_REV;
9617+ case SG_PIXELFORMAT_DEPTH:
9618+ return GL_FLOAT;
9619+ case SG_PIXELFORMAT_DEPTH_STENCIL:
9620+ return GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
9621+ default:
9622+ SOKOL_UNREACHABLE; return 0;
9623+ }
9624+}
9625+
9626+_SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) {
9627+ switch (fmt) {
9628+ case SG_PIXELFORMAT_R8:
9629+ case SG_PIXELFORMAT_R8SN:
9630+ case SG_PIXELFORMAT_R16:
9631+ case SG_PIXELFORMAT_R16SN:
9632+ case SG_PIXELFORMAT_R16F:
9633+ case SG_PIXELFORMAT_R32F:
9634+ return GL_RED;
9635+ case SG_PIXELFORMAT_R8UI:
9636+ case SG_PIXELFORMAT_R8SI:
9637+ case SG_PIXELFORMAT_R16UI:
9638+ case SG_PIXELFORMAT_R16SI:
9639+ case SG_PIXELFORMAT_R32UI:
9640+ case SG_PIXELFORMAT_R32SI:
9641+ return GL_RED_INTEGER;
9642+ case SG_PIXELFORMAT_RG8:
9643+ case SG_PIXELFORMAT_RG8SN:
9644+ case SG_PIXELFORMAT_RG16:
9645+ case SG_PIXELFORMAT_RG16SN:
9646+ case SG_PIXELFORMAT_RG16F:
9647+ case SG_PIXELFORMAT_RG32F:
9648+ return GL_RG;
9649+ case SG_PIXELFORMAT_RG8UI:
9650+ case SG_PIXELFORMAT_RG8SI:
9651+ case SG_PIXELFORMAT_RG16UI:
9652+ case SG_PIXELFORMAT_RG16SI:
9653+ case SG_PIXELFORMAT_RG32UI:
9654+ case SG_PIXELFORMAT_RG32SI:
9655+ return GL_RG_INTEGER;
9656+ case SG_PIXELFORMAT_RGBA8:
9657+ case SG_PIXELFORMAT_SRGB8A8:
9658+ case SG_PIXELFORMAT_RGBA8SN:
9659+ case SG_PIXELFORMAT_RGBA16:
9660+ case SG_PIXELFORMAT_RGBA16SN:
9661+ case SG_PIXELFORMAT_RGBA16F:
9662+ case SG_PIXELFORMAT_RGBA32F:
9663+ case SG_PIXELFORMAT_RGB10A2:
9664+ return GL_RGBA;
9665+ case SG_PIXELFORMAT_RGBA8UI:
9666+ case SG_PIXELFORMAT_RGBA8SI:
9667+ case SG_PIXELFORMAT_RGBA16UI:
9668+ case SG_PIXELFORMAT_RGBA16SI:
9669+ case SG_PIXELFORMAT_RGBA32UI:
9670+ case SG_PIXELFORMAT_RGBA32SI:
9671+ return GL_RGBA_INTEGER;
9672+ case SG_PIXELFORMAT_RG11B10F:
9673+ case SG_PIXELFORMAT_RGB9E5:
9674+ return GL_RGB;
9675+ case SG_PIXELFORMAT_DEPTH:
9676+ return GL_DEPTH_COMPONENT;
9677+ case SG_PIXELFORMAT_DEPTH_STENCIL:
9678+ return GL_DEPTH_STENCIL;
9679+ case SG_PIXELFORMAT_BC1_RGBA:
9680+ return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
9681+ case SG_PIXELFORMAT_BC2_RGBA:
9682+ return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
9683+ case SG_PIXELFORMAT_BC3_RGBA:
9684+ return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
9685+ case SG_PIXELFORMAT_BC3_SRGBA:
9686+ return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
9687+ case SG_PIXELFORMAT_BC4_R:
9688+ return GL_COMPRESSED_RED_RGTC1;
9689+ case SG_PIXELFORMAT_BC4_RSN:
9690+ return GL_COMPRESSED_SIGNED_RED_RGTC1;
9691+ case SG_PIXELFORMAT_BC5_RG:
9692+ return GL_COMPRESSED_RED_GREEN_RGTC2;
9693+ case SG_PIXELFORMAT_BC5_RGSN:
9694+ return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2;
9695+ case SG_PIXELFORMAT_BC6H_RGBF:
9696+ return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB;
9697+ case SG_PIXELFORMAT_BC6H_RGBUF:
9698+ return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB;
9699+ case SG_PIXELFORMAT_BC7_RGBA:
9700+ return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
9701+ case SG_PIXELFORMAT_BC7_SRGBA:
9702+ return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB;
9703+ case SG_PIXELFORMAT_ETC2_RGB8:
9704+ return GL_COMPRESSED_RGB8_ETC2;
9705+ case SG_PIXELFORMAT_ETC2_SRGB8:
9706+ return GL_COMPRESSED_SRGB8_ETC2;
9707+ case SG_PIXELFORMAT_ETC2_RGB8A1:
9708+ return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
9709+ case SG_PIXELFORMAT_ETC2_RGBA8:
9710+ return GL_COMPRESSED_RGBA8_ETC2_EAC;
9711+ case SG_PIXELFORMAT_ETC2_SRGB8A8:
9712+ return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;
9713+ case SG_PIXELFORMAT_EAC_R11:
9714+ return GL_COMPRESSED_R11_EAC;
9715+ case SG_PIXELFORMAT_EAC_R11SN:
9716+ return GL_COMPRESSED_SIGNED_R11_EAC;
9717+ case SG_PIXELFORMAT_EAC_RG11:
9718+ return GL_COMPRESSED_RG11_EAC;
9719+ case SG_PIXELFORMAT_EAC_RG11SN:
9720+ return GL_COMPRESSED_SIGNED_RG11_EAC;
9721+ case SG_PIXELFORMAT_ASTC_4x4_RGBA:
9722+ return GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
9723+ case SG_PIXELFORMAT_ASTC_4x4_SRGBA:
9724+ return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR;
9725+ default:
9726+ SOKOL_UNREACHABLE; return 0;
9727+ }
9728+}
9729+
9730+_SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) {
9731+ switch (fmt) {
9732+ case SG_PIXELFORMAT_R8: return GL_R8;
9733+ case SG_PIXELFORMAT_R8SN: return GL_R8_SNORM;
9734+ case SG_PIXELFORMAT_R8UI: return GL_R8UI;
9735+ case SG_PIXELFORMAT_R8SI: return GL_R8I;
9736+ #if !defined(SOKOL_GLES3)
9737+ case SG_PIXELFORMAT_R16: return GL_R16;
9738+ case SG_PIXELFORMAT_R16SN: return GL_R16_SNORM;
9739+ #endif
9740+ case SG_PIXELFORMAT_R16UI: return GL_R16UI;
9741+ case SG_PIXELFORMAT_R16SI: return GL_R16I;
9742+ case SG_PIXELFORMAT_R16F: return GL_R16F;
9743+ case SG_PIXELFORMAT_RG8: return GL_RG8;
9744+ case SG_PIXELFORMAT_RG8SN: return GL_RG8_SNORM;
9745+ case SG_PIXELFORMAT_RG8UI: return GL_RG8UI;
9746+ case SG_PIXELFORMAT_RG8SI: return GL_RG8I;
9747+ case SG_PIXELFORMAT_R32UI: return GL_R32UI;
9748+ case SG_PIXELFORMAT_R32SI: return GL_R32I;
9749+ case SG_PIXELFORMAT_R32F: return GL_R32F;
9750+ #if !defined(SOKOL_GLES3)
9751+ case SG_PIXELFORMAT_RG16: return GL_RG16;
9752+ case SG_PIXELFORMAT_RG16SN: return GL_RG16_SNORM;
9753+ #endif
9754+ case SG_PIXELFORMAT_RG16UI: return GL_RG16UI;
9755+ case SG_PIXELFORMAT_RG16SI: return GL_RG16I;
9756+ case SG_PIXELFORMAT_RG16F: return GL_RG16F;
9757+ case SG_PIXELFORMAT_RGBA8: return GL_RGBA8;
9758+ case SG_PIXELFORMAT_SRGB8A8: return GL_SRGB8_ALPHA8;
9759+ case SG_PIXELFORMAT_RGBA8SN: return GL_RGBA8_SNORM;
9760+ case SG_PIXELFORMAT_RGBA8UI: return GL_RGBA8UI;
9761+ case SG_PIXELFORMAT_RGBA8SI: return GL_RGBA8I;
9762+ case SG_PIXELFORMAT_RGB10A2: return GL_RGB10_A2;
9763+ case SG_PIXELFORMAT_RG11B10F: return GL_R11F_G11F_B10F;
9764+ case SG_PIXELFORMAT_RGB9E5: return GL_RGB9_E5;
9765+ case SG_PIXELFORMAT_RG32UI: return GL_RG32UI;
9766+ case SG_PIXELFORMAT_RG32SI: return GL_RG32I;
9767+ case SG_PIXELFORMAT_RG32F: return GL_RG32F;
9768+ #if !defined(SOKOL_GLES3)
9769+ case SG_PIXELFORMAT_RGBA16: return GL_RGBA16;
9770+ case SG_PIXELFORMAT_RGBA16SN: return GL_RGBA16_SNORM;
9771+ #endif
9772+ case SG_PIXELFORMAT_RGBA16UI: return GL_RGBA16UI;
9773+ case SG_PIXELFORMAT_RGBA16SI: return GL_RGBA16I;
9774+ case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F;
9775+ case SG_PIXELFORMAT_RGBA32UI: return GL_RGBA32UI;
9776+ case SG_PIXELFORMAT_RGBA32SI: return GL_RGBA32I;
9777+ case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F;
9778+ case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT32F;
9779+ case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH32F_STENCIL8;
9780+ case SG_PIXELFORMAT_BC1_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
9781+ case SG_PIXELFORMAT_BC2_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
9782+ case SG_PIXELFORMAT_BC3_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
9783+ case SG_PIXELFORMAT_BC3_SRGBA: return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
9784+ case SG_PIXELFORMAT_BC4_R: return GL_COMPRESSED_RED_RGTC1;
9785+ case SG_PIXELFORMAT_BC4_RSN: return GL_COMPRESSED_SIGNED_RED_RGTC1;
9786+ case SG_PIXELFORMAT_BC5_RG: return GL_COMPRESSED_RED_GREEN_RGTC2;
9787+ case SG_PIXELFORMAT_BC5_RGSN: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2;
9788+ case SG_PIXELFORMAT_BC6H_RGBF: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB;
9789+ case SG_PIXELFORMAT_BC6H_RGBUF: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB;
9790+ case SG_PIXELFORMAT_BC7_RGBA: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
9791+ case SG_PIXELFORMAT_BC7_SRGBA: return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB;
9792+ case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2;
9793+ case SG_PIXELFORMAT_ETC2_SRGB8: return GL_COMPRESSED_SRGB8_ETC2;
9794+ case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
9795+ case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC;
9796+ case SG_PIXELFORMAT_ETC2_SRGB8A8: return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;
9797+ case SG_PIXELFORMAT_EAC_R11: return GL_COMPRESSED_R11_EAC;
9798+ case SG_PIXELFORMAT_EAC_R11SN: return GL_COMPRESSED_SIGNED_R11_EAC;
9799+ case SG_PIXELFORMAT_EAC_RG11: return GL_COMPRESSED_RG11_EAC;
9800+ case SG_PIXELFORMAT_EAC_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC;
9801+ case SG_PIXELFORMAT_ASTC_4x4_RGBA: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
9802+ case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR;
9803+ default: SOKOL_UNREACHABLE; return 0;
9804+ }
9805+}
9806+
9807+_SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) {
9808+ switch (face_index) {
9809+ case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X;
9810+ case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
9811+ case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
9812+ case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
9813+ case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
9814+ case 5: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
9815+ default: SOKOL_UNREACHABLE; return 0;
9816+ }
9817+}
9818+
9819+// see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml
9820+_SOKOL_PRIVATE void _sg_gl_init_pixelformats(bool has_bgra) {
9821+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]);
9822+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]);
9823+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]);
9824+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]);
9825+ #if !defined(SOKOL_GLES3)
9826+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]);
9827+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]);
9828+ #endif
9829+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]);
9830+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]);
9831+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]);
9832+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]);
9833+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]);
9834+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]);
9835+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]);
9836+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]);
9837+ #if !defined(SOKOL_GLES3)
9838+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]);
9839+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]);
9840+ #endif
9841+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]);
9842+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]);
9843+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]);
9844+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]);
9845+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]);
9846+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]);
9847+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]);
9848+ if (has_bgra) {
9849+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]);
9850+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SBGR8A8]);
9851+ }
9852+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]);
9853+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]);
9854+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]);
9855+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]);
9856+ #if !defined(SOKOL_GLES3)
9857+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]);
9858+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]);
9859+ #endif
9860+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]);
9861+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]);
9862+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]);
9863+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]);
9864+ _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]);
9865+ _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]);
9866+}
9867+
9868+// FIXME: OES_half_float_blend
9869+_SOKOL_PRIVATE void _sg_gl_init_pixelformats_half_float(bool has_colorbuffer_half_float) {
9870+ if (has_colorbuffer_half_float) {
9871+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]);
9872+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]);
9873+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]);
9874+ } else {
9875+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]);
9876+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG16F]);
9877+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]);
9878+ }
9879+}
9880+
9881+_SOKOL_PRIVATE void _sg_gl_init_pixelformats_float(bool has_colorbuffer_float, bool has_texture_float_linear, bool has_float_blend) {
9882+ if (has_texture_float_linear) {
9883+ if (has_colorbuffer_float) {
9884+ if (has_float_blend) {
9885+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]);
9886+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]);
9887+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
9888+ } else {
9889+ _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_R32F]);
9890+ _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG32F]);
9891+ _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
9892+ }
9893+ _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG11B10F]);
9894+ } else {
9895+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]);
9896+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG32F]);
9897+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
9898+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]);
9899+ }
9900+ } else {
9901+ if (has_colorbuffer_float) {
9902+ _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R32F]);
9903+ _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG32F]);
9904+ _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
9905+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG11B10F]);
9906+ } else {
9907+ _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]);
9908+ _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG32F]);
9909+ _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
9910+ _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG11B10F]);
9911+ }
9912+ }
9913+}
9914+
9915+_SOKOL_PRIVATE void _sg_gl_init_pixelformats_s3tc(void) {
9916+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]);
9917+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]);
9918+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]);
9919+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]);
9920+}
9921+
9922+_SOKOL_PRIVATE void _sg_gl_init_pixelformats_rgtc(void) {
9923+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]);
9924+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]);
9925+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]);
9926+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]);
9927+}
9928+
9929+_SOKOL_PRIVATE void _sg_gl_init_pixelformats_bptc(void) {
9930+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]);
9931+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]);
9932+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]);
9933+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]);
9934+}
9935+
9936+_SOKOL_PRIVATE void _sg_gl_init_pixelformats_etc2(void) {
9937+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]);
9938+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]);
9939+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]);
9940+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]);
9941+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]);
9942+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]);
9943+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]);
9944+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]);
9945+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]);
9946+}
9947+
9948+_SOKOL_PRIVATE void _sg_gl_init_pixelformats_astc(void) {
9949+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]);
9950+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]);
9951+}
9952+
9953+_SOKOL_PRIVATE void _sg_gl_init_limits(void) {
9954+ _SG_GL_CHECK_ERROR();
9955+
9956+ GLint gl_int;
9957+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_int);
9958+ _SG_GL_CHECK_ERROR();
9959+
9960+ _sg.limits.max_image_size_2d = gl_int;
9961+ _sg.limits.max_image_size_array = gl_int;
9962+
9963+ glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gl_int);
9964+ _SG_GL_CHECK_ERROR();
9965+ _sg.limits.max_image_size_cube = gl_int;
9966+
9967+ glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int);
9968+ _SG_GL_CHECK_ERROR();
9969+ _sg.limits.max_image_size_3d = gl_int;
9970+
9971+ glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gl_int);
9972+ _SG_GL_CHECK_ERROR();
9973+ _sg.limits.max_image_array_layers = gl_int;
9974+
9975+ glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_int);
9976+ _SG_GL_CHECK_ERROR();
9977+ _sg.limits.max_vertex_attrs = _sg_min(gl_int, SG_MAX_VERTEX_ATTRIBUTES);
9978+
9979+ glGetIntegerv(GL_MAX_DRAW_BUFFERS, &gl_int);
9980+ _SG_GL_CHECK_ERROR();
9981+ _sg.limits.max_color_attachments = _sg_min(gl_int, SG_MAX_COLOR_ATTACHMENTS);
9982+
9983+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gl_int);
9984+ _SG_GL_CHECK_ERROR();
9985+ _sg.limits.max_texture_bindings_per_stage = _sg_min(gl_int, SG_MAX_VIEW_BINDSLOTS);
9986+
9987+ #if defined(_SOKOL_GL_HAS_COMPUTE)
9988+ if (_sg.features.compute) {
9989+ glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &gl_int);
9990+ _SG_GL_CHECK_ERROR();
9991+ _sg.limits.max_storage_buffer_bindings_per_stage = _sg_min(gl_int, SG_MAX_VIEW_BINDSLOTS);
9992+
9993+ glGetIntegerv(GL_MAX_IMAGE_UNITS, &gl_int);
9994+ _SG_GL_CHECK_ERROR();
9995+ _sg.limits.max_storage_image_bindings_per_stage = _sg_min(gl_int, SG_MAX_VIEW_BINDSLOTS);
9996+ }
9997+ #endif
9998+
9999+ glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &gl_int);
10000+ _SG_GL_CHECK_ERROR();
10001+ _sg.limits.gl_max_vertex_uniform_components = gl_int;
10002+
10003+ if (_sg.gl.ext_anisotropic) {
10004+ glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_int);
10005+ _SG_GL_CHECK_ERROR();
10006+ _sg.gl.max_anisotropy = gl_int;
10007+ } else {
10008+ _sg.gl.max_anisotropy = 1;
10009+ }
10010+
10011+ glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gl_int);
10012+ _SG_GL_CHECK_ERROR();
10013+ _sg.limits.gl_max_combined_texture_image_units = gl_int;
10014+}
10015+
10016+#if defined(SOKOL_GLCORE)
10017+_SOKOL_PRIVATE void _sg_gl_init_caps_glcore(void) {
10018+ _sg.backend = SG_BACKEND_GLCORE;
10019+
10020+ GLint major_version = 0;
10021+ GLint minor_version = 0;
10022+ glGetIntegerv(GL_MAJOR_VERSION, &major_version);
10023+ glGetIntegerv(GL_MINOR_VERSION, &minor_version);
10024+ const int version = major_version * 100 + minor_version * 10;
10025+ _sg.features.origin_top_left = false;
10026+ _sg.features.image_clamp_to_border = true;
10027+ _sg.features.mrt_independent_blend_state = false;
10028+ _sg.features.mrt_independent_write_mask = true;
10029+ _sg.features.compute = version >= 430;
10030+ _sg.features.gl_texture_views = version >= 430;
10031+ #if defined(__APPLE__)
10032+ _sg.features.msaa_texture_bindings = false;
10033+ #else
10034+ _sg.features.msaa_texture_bindings = true;
10035+ #endif
10036+ _sg.features.draw_base_vertex = version >= 320;
10037+ _sg.features.draw_base_instance = version >= 420;
10038+ _sg.features.dual_source_blending = version >= 330;
10039+ _sg.features.vertexformat_int10_n2 = true;
10040+
10041+ // scan extensions
10042+ bool has_s3tc = false; // BC1..BC3
10043+ bool has_rgtc = false; // BC4 and BC5
10044+ bool has_bptc = false; // BC6H and BC7
10045+ bool has_etc2 = false;
10046+ bool has_astc = false;
10047+ GLint num_ext = 0;
10048+ glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext);
10049+ for (int i = 0; i < num_ext; i++) {
10050+ const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i);
10051+ if (ext) {
10052+ if (strstr(ext, "_texture_compression_s3tc")) {
10053+ has_s3tc = true;
10054+ } else if (strstr(ext, "_texture_compression_rgtc")) {
10055+ has_rgtc = true;
10056+ } else if (strstr(ext, "_texture_compression_bptc")) {
10057+ has_bptc = true;
10058+ } else if (strstr(ext, "_ES3_compatibility")) {
10059+ has_etc2 = true;
10060+ } else if (strstr(ext, "_texture_filter_anisotropic")) {
10061+ _sg.gl.ext_anisotropic = true;
10062+ } else if (strstr(ext, "_texture_compression_astc_ldr")) {
10063+ has_astc = true;
10064+ }
10065+ }
10066+ }
10067+
10068+ // limits
10069+ _sg_gl_init_limits();
10070+
10071+ // pixel formats
10072+ const bool has_bgra = false; // not a bug
10073+ const bool has_colorbuffer_float = true;
10074+ const bool has_colorbuffer_half_float = true;
10075+ const bool has_texture_float_linear = true; // FIXME???
10076+ const bool has_float_blend = true;
10077+ _sg_gl_init_pixelformats(has_bgra);
10078+ _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend);
10079+ _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float);
10080+ if (has_s3tc) {
10081+ _sg_gl_init_pixelformats_s3tc();
10082+ }
10083+ if (has_rgtc) {
10084+ _sg_gl_init_pixelformats_rgtc();
10085+ }
10086+ if (has_bptc) {
10087+ _sg_gl_init_pixelformats_bptc();
10088+ }
10089+ if (has_etc2) {
10090+ _sg_gl_init_pixelformats_etc2();
10091+ }
10092+ if (has_astc) {
10093+ _sg_gl_init_pixelformats_astc();
10094+ }
10095+ if (_sg.features.compute) {
10096+ // see: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glBindImageTexture.xhtml
10097+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
10098+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]);
10099+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32F]);
10100+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG16F]);
10101+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG11B10F]);
10102+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]);
10103+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]);
10104+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32F]);
10105+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16F]);
10106+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]);
10107+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32UI]);
10108+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG16UI]);
10109+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG8UI]);
10110+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32UI]);
10111+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16UI]);
10112+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R8UI]);
10113+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]);
10114+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]);
10115+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]);
10116+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32SI]);
10117+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG16SI]);
10118+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG8SI]);
10119+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32SI]);
10120+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16SI]);
10121+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R8SI]);
10122+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]);
10123+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]);
10124+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]);
10125+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG16]);
10126+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG8]);
10127+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16]);
10128+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R8]);
10129+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]);
10130+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]);
10131+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]);
10132+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG8SN]);
10133+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16SN]);
10134+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R8SN]);
10135+ }
10136+}
10137+#endif
10138+
10139+#if defined(SOKOL_GLES3)
10140+_SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) {
10141+ _sg.backend = SG_BACKEND_GLES3;
10142+
10143+ GLint major_version = 0;
10144+ GLint minor_version = 0;
10145+ glGetIntegerv(GL_MAJOR_VERSION, &major_version);
10146+ glGetIntegerv(GL_MINOR_VERSION, &minor_version);
10147+ const int version = major_version * 100 + minor_version * 10;
10148+ _sg.features.origin_top_left = false;
10149+ _sg.features.image_clamp_to_border = false;
10150+ _sg.features.mrt_independent_blend_state = false;
10151+ #if defined(_SOKOL_GL_HAS_COLORMASKI)
10152+ _sg.features.mrt_independent_write_mask = version >= 320;
10153+ #else
10154+ _sg.features.mrt_independent_write_mask = false;
10155+ #endif
10156+ _sg.features.compute = version >= 310;
10157+ _sg.features.msaa_texture_bindings = false;
10158+ _sg.features.gl_texture_views = version >= 430;
10159+ #if defined(__EMSCRIPTEN__)
10160+ _sg.features.separate_buffer_types = true;
10161+ #else
10162+ _sg.features.separate_buffer_types = false;
10163+ #endif
10164+ _sg.features.draw_base_vertex = version >= 320;
10165+ _sg.features.draw_base_instance = false;
10166+ _sg.features.dual_source_blending = false;
10167+ _sg.features.vertexformat_int10_n2 = true;
10168+
10169+ bool has_s3tc = false; // BC1..BC3
10170+ bool has_rgtc = false; // BC4 and BC5
10171+ bool has_bptc = false; // BC6H and BC7
10172+ #if defined(__EMSCRIPTEN__)
10173+ bool has_etc2 = false;
10174+ #else
10175+ bool has_etc2 = true;
10176+ #endif
10177+ bool has_astc = false;
10178+ bool has_colorbuffer_float = false;
10179+ bool has_colorbuffer_half_float = false;
10180+ bool has_texture_float_linear = false;
10181+ bool has_float_blend = false;
10182+ GLint num_ext = 0;
10183+ glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext);
10184+ for (int i = 0; i < num_ext; i++) {
10185+ const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i);
10186+ if (ext) {
10187+ if (strstr(ext, "_texture_compression_s3tc")) {
10188+ has_s3tc = true;
10189+ } else if (strstr(ext, "_compressed_texture_s3tc")) {
10190+ has_s3tc = true;
10191+ } else if (strstr(ext, "_texture_compression_rgtc")) {
10192+ has_rgtc = true;
10193+ } else if (strstr(ext, "_texture_compression_bptc")) {
10194+ has_bptc = true;
10195+ } else if (strstr(ext, "_compressed_texture_etc")) {
10196+ has_etc2 = true;
10197+ } else if (strstr(ext, "_compressed_texture_astc")) {
10198+ has_astc = true;
10199+ } else if (strstr(ext, "_color_buffer_float")) {
10200+ has_colorbuffer_float = true;
10201+ } else if (strstr(ext, "_color_buffer_half_float")) {
10202+ has_colorbuffer_half_float = true;
10203+ } else if (strstr(ext, "_texture_float_linear")) {
10204+ has_texture_float_linear = true;
10205+ } else if (strstr(ext, "_float_blend")) {
10206+ has_float_blend = true;
10207+ } else if (strstr(ext, "_texture_filter_anisotropic")) {
10208+ _sg.gl.ext_anisotropic = true;
10209+ }
10210+ }
10211+ }
10212+
10213+ /* on WebGL2, color_buffer_float also includes 16-bit formats
10214+ see: https://developer.mozilla.org/en-US/docs/Web/API/EXT_color_buffer_float
10215+ */
10216+ #if defined(__EMSCRIPTEN__)
10217+ if (!has_colorbuffer_half_float && has_colorbuffer_float) {
10218+ has_colorbuffer_half_float = has_colorbuffer_float;
10219+ }
10220+ #endif
10221+
10222+ // limits
10223+ _sg_gl_init_limits();
10224+
10225+ // pixel formats
10226+ const bool has_bgra = false; // not a bug
10227+ _sg_gl_init_pixelformats(has_bgra);
10228+ _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend);
10229+ _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float);
10230+ if (has_s3tc) {
10231+ _sg_gl_init_pixelformats_s3tc();
10232+ }
10233+ if (has_rgtc) {
10234+ _sg_gl_init_pixelformats_rgtc();
10235+ }
10236+ if (has_bptc) {
10237+ _sg_gl_init_pixelformats_bptc();
10238+ }
10239+ if (has_etc2) {
10240+ _sg_gl_init_pixelformats_etc2();
10241+ }
10242+ if (has_astc) {
10243+ _sg_gl_init_pixelformats_astc();
10244+ }
10245+ if (_sg.features.compute) {
10246+ // see https://registry.khronos.org/OpenGL-Refpages/es3.1/html/glBindImageTexture.xhtml
10247+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
10248+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]);
10249+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32F]);
10250+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]);
10251+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]);
10252+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]);
10253+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32UI]);
10254+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]);
10255+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]);
10256+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]);
10257+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32SI]);
10258+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]);
10259+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]);
10260+ }
10261+}
10262+#endif
10263+
10264+//-- state cache implementation ------------------------------------------------
10265+_SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) {
10266+ if (force || (_sg.gl.cache.vertex_buffer != 0)) {
10267+ glBindBuffer(GL_ARRAY_BUFFER, 0);
10268+ _sg.gl.cache.vertex_buffer = 0;
10269+ _sg_stats_inc(gl.num_bind_buffer);
10270+ }
10271+ if (force || (_sg.gl.cache.index_buffer != 0)) {
10272+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
10273+ _sg.gl.cache.index_buffer = 0;
10274+ _sg_stats_inc(gl.num_bind_buffer);
10275+ }
10276+ if (force || (_sg.gl.cache.storage_buffer != 0)) {
10277+ if (_sg.features.compute) {
10278+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
10279+ }
10280+ _sg.gl.cache.storage_buffer = 0;
10281+ _sg_stats_inc(gl.num_bind_buffer);
10282+ }
10283+ for (int i = 0; i < _SG_GL_MAX_SBUF_BINDINGS; i++) {
10284+ if (force || (_sg.gl.cache.storage_buffers[i] != 0)) {
10285+ if (_sg.features.compute && (i < _sg.limits.max_storage_buffer_bindings_per_stage)) {
10286+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, (GLuint)i, 0);
10287+ }
10288+ _sg.gl.cache.storage_buffers[i] = 0;
10289+ _sg_stats_inc(gl.num_bind_buffer);
10290+ }
10291+ }
10292+}
10293+
10294+_SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) {
10295+ SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target) || (GL_SHADER_STORAGE_BUFFER == target));
10296+ if (target == GL_ARRAY_BUFFER) {
10297+ if (_sg.gl.cache.vertex_buffer != buffer) {
10298+ _sg.gl.cache.vertex_buffer = buffer;
10299+ glBindBuffer(target, buffer);
10300+ _sg_stats_inc(gl.num_bind_buffer);
10301+ }
10302+ } else if (target == GL_ELEMENT_ARRAY_BUFFER) {
10303+ if (_sg.gl.cache.index_buffer != buffer) {
10304+ _sg.gl.cache.index_buffer = buffer;
10305+ glBindBuffer(target, buffer);
10306+ _sg_stats_inc(gl.num_bind_buffer);
10307+ }
10308+ } else if (target == GL_SHADER_STORAGE_BUFFER) {
10309+ if (_sg.gl.cache.storage_buffer != buffer) {
10310+ _sg.gl.cache.storage_buffer = buffer;
10311+ if (_sg.features.compute) {
10312+ glBindBuffer(target, buffer);
10313+ }
10314+ _sg_stats_inc(gl.num_bind_buffer);
10315+ }
10316+ } else {
10317+ SOKOL_UNREACHABLE;
10318+ }
10319+}
10320+
10321+_SOKOL_PRIVATE void _sg_gl_cache_bind_storage_buffer(uint8_t glsl_binding_n, GLuint buffer, int offset, int buf_size) {
10322+ SOKOL_ASSERT(glsl_binding_n < _SG_GL_MAX_SBUF_BINDINGS);
10323+ SOKOL_ASSERT(offset < buf_size);
10324+ const bool buf_neql = _sg.gl.cache.storage_buffers[glsl_binding_n] != buffer;
10325+ const bool off_neql = _sg.gl.cache.storage_buffer_offsets[glsl_binding_n] != offset;
10326+ if (buf_neql || off_neql) {
10327+ _sg.gl.cache.storage_buffers[glsl_binding_n] = buffer;
10328+ _sg.gl.cache.storage_buffer_offsets[glsl_binding_n] = offset;
10329+ _sg.gl.cache.storage_buffer = buffer; // not a bug
10330+ if (_sg.features.compute) {
10331+ SOKOL_ASSERT(glsl_binding_n < _sg.limits.max_storage_buffer_bindings_per_stage);
10332+ glBindBufferRange(GL_SHADER_STORAGE_BUFFER, glsl_binding_n, buffer, offset, buf_size - offset);
10333+ }
10334+ _sg_stats_inc(gl.num_bind_buffer);
10335+ }
10336+}
10337+
10338+_SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) {
10339+ if (target == GL_ARRAY_BUFFER) {
10340+ _sg.gl.cache.stored_vertex_buffer = _sg.gl.cache.vertex_buffer;
10341+ } else if (target == GL_ELEMENT_ARRAY_BUFFER) {
10342+ _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer;
10343+ } else if (target == GL_SHADER_STORAGE_BUFFER) {
10344+ _sg.gl.cache.stored_storage_buffer = _sg.gl.cache.storage_buffer;
10345+ } else {
10346+ SOKOL_UNREACHABLE;
10347+ }
10348+}
10349+
10350+_SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) {
10351+ if (target == GL_ARRAY_BUFFER) {
10352+ if (_sg.gl.cache.stored_vertex_buffer != 0) {
10353+ // we only care about restoring valid ids
10354+ _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer);
10355+ _sg.gl.cache.stored_vertex_buffer = 0;
10356+ }
10357+ } else if (target == GL_ELEMENT_ARRAY_BUFFER) {
10358+ if (_sg.gl.cache.stored_index_buffer != 0) {
10359+ // we only care about restoring valid ids
10360+ _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_index_buffer);
10361+ _sg.gl.cache.stored_index_buffer = 0;
10362+ }
10363+ } else if (target == GL_SHADER_STORAGE_BUFFER) {
10364+ if (_sg.gl.cache.stored_storage_buffer != 0) {
10365+ // we only care about restoring valid ids
10366+ _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_storage_buffer);
10367+ _sg.gl.cache.stored_storage_buffer = 0;
10368+ }
10369+ } else {
10370+ SOKOL_UNREACHABLE;
10371+ }
10372+}
10373+
10374+// called from _sg_gl_discard_buffer()
10375+_SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) {
10376+ if (buf == _sg.gl.cache.vertex_buffer) {
10377+ _sg.gl.cache.vertex_buffer = 0;
10378+ glBindBuffer(GL_ARRAY_BUFFER, 0);
10379+ _sg_stats_inc(gl.num_bind_buffer);
10380+ }
10381+ if (buf == _sg.gl.cache.index_buffer) {
10382+ _sg.gl.cache.index_buffer = 0;
10383+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
10384+ _sg_stats_inc(gl.num_bind_buffer);
10385+ }
10386+ if (buf == _sg.gl.cache.storage_buffer) {
10387+ _sg.gl.cache.storage_buffer = 0;
10388+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
10389+ _sg_stats_inc(gl.num_bind_buffer);
10390+ }
10391+ for (int i = 0; i < _SG_GL_MAX_SBUF_BINDINGS; i++) {
10392+ if (buf == _sg.gl.cache.storage_buffers[i]) {
10393+ _sg.gl.cache.storage_buffers[i] = 0;
10394+ _sg.gl.cache.storage_buffer = 0; // not a bug!
10395+ if (_sg.features.compute && (i < _sg.limits.max_storage_buffer_bindings_per_stage)) {
10396+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, (GLuint)i, 0);
10397+ }
10398+ _sg_stats_inc(gl.num_bind_buffer);
10399+ }
10400+ }
10401+ if (buf == _sg.gl.cache.stored_vertex_buffer) {
10402+ _sg.gl.cache.stored_vertex_buffer = 0;
10403+ }
10404+ if (buf == _sg.gl.cache.stored_index_buffer) {
10405+ _sg.gl.cache.stored_index_buffer = 0;
10406+ }
10407+ if (buf == _sg.gl.cache.stored_storage_buffer) {
10408+ _sg.gl.cache.stored_storage_buffer = 0;
10409+ }
10410+ for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) {
10411+ if (buf == _sg.gl.cache.attrs[i].gl_vbuf) {
10412+ _sg.gl.cache.attrs[i].gl_vbuf = 0;
10413+ }
10414+ }
10415+}
10416+
10417+_SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) {
10418+ _SG_GL_CHECK_ERROR();
10419+ if (_sg.gl.cache.cur_active_texture != texture) {
10420+ _sg.gl.cache.cur_active_texture = texture;
10421+ glActiveTexture(texture);
10422+ _sg_stats_inc(gl.num_active_texture);
10423+ }
10424+ _SG_GL_CHECK_ERROR();
10425+}
10426+
10427+_SOKOL_PRIVATE void _sg_gl_cache_clear_texture_sampler_bindings(bool force) {
10428+ _SG_GL_CHECK_ERROR();
10429+ for (int i = 0; (i < _SG_GL_MAX_TEX_SMP_BINDINGS) && (i < _sg.limits.gl_max_combined_texture_image_units); i++) {
10430+ if (force || (_sg.gl.cache.texture_samplers[i].texture != 0)) {
10431+ GLenum gl_texture_unit = (GLenum) (GL_TEXTURE0 + i);
10432+ glActiveTexture(gl_texture_unit);
10433+ _sg_stats_inc(gl.num_active_texture);
10434+ glBindTexture(GL_TEXTURE_2D, 0);
10435+ glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
10436+ glBindTexture(GL_TEXTURE_3D, 0);
10437+ glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
10438+ _sg_stats_add(gl.num_bind_texture, 4);
10439+ glBindSampler((GLuint)i, 0);
10440+ _sg_stats_inc(gl.num_bind_sampler);
10441+ _sg.gl.cache.texture_samplers[i].target = 0;
10442+ _sg.gl.cache.texture_samplers[i].texture = 0;
10443+ _sg.gl.cache.texture_samplers[i].sampler = 0;
10444+ _sg.gl.cache.cur_active_texture = gl_texture_unit;
10445+ }
10446+ }
10447+ _SG_GL_CHECK_ERROR();
10448+}
10449+
10450+_SOKOL_PRIVATE void _sg_gl_cache_bind_texture_sampler(int8_t gl_tex_slot, GLenum target, GLuint texture, GLuint sampler) {
10451+ /* it's valid to call this function with target=0 and/or texture=0
10452+ target=0 will unbind the previous binding, texture=0 will clear
10453+ the new binding
10454+ */
10455+ SOKOL_ASSERT((gl_tex_slot >= 0) && (gl_tex_slot < _SG_GL_MAX_TEX_SMP_BINDINGS));
10456+ if (gl_tex_slot >= _sg.limits.gl_max_combined_texture_image_units) {
10457+ return;
10458+ }
10459+ _SG_GL_CHECK_ERROR();
10460+ _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[gl_tex_slot];
10461+ if ((slot->target != target) || (slot->texture != texture) || (slot->sampler != sampler)) {
10462+ _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + gl_tex_slot));
10463+ // if the target has changed, clear the previous binding on that target
10464+ if ((target != slot->target) && (slot->target != 0)) {
10465+ glBindTexture(slot->target, 0);
10466+ _SG_GL_CHECK_ERROR();
10467+ _sg_stats_inc(gl.num_bind_texture);
10468+ }
10469+ // apply new binding (can be 0 to unbind)
10470+ if (target != 0) {
10471+ glBindTexture(target, texture);
10472+ _SG_GL_CHECK_ERROR();
10473+ _sg_stats_inc(gl.num_bind_texture);
10474+ }
10475+ // apply new sampler (can be 0 to unbind)
10476+ glBindSampler((GLuint)gl_tex_slot, sampler);
10477+ _SG_GL_CHECK_ERROR();
10478+ _sg_stats_inc(gl.num_bind_sampler);
10479+
10480+ slot->target = target;
10481+ slot->texture = texture;
10482+ slot->sampler = sampler;
10483+ }
10484+}
10485+
10486+_SOKOL_PRIVATE void _sg_gl_cache_store_texture_sampler_binding(int8_t gl_tex_slot) {
10487+ SOKOL_ASSERT((gl_tex_slot >= 0) && (gl_tex_slot < _SG_GL_MAX_TEX_SMP_BINDINGS));
10488+ _sg.gl.cache.stored_texture_sampler = _sg.gl.cache.texture_samplers[gl_tex_slot];
10489+}
10490+
10491+_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_sampler_binding(int8_t gl_tex_slot) {
10492+ SOKOL_ASSERT((gl_tex_slot >= 0) && (gl_tex_slot < _SG_GL_MAX_TEX_SMP_BINDINGS));
10493+ _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.stored_texture_sampler;
10494+ if (slot->texture != 0) {
10495+ // we only care about restoring valid ids
10496+ SOKOL_ASSERT(slot->target != 0);
10497+ _sg_gl_cache_bind_texture_sampler(gl_tex_slot, slot->target, slot->texture, slot->sampler);
10498+ slot->target = 0;
10499+ slot->texture = 0;
10500+ slot->sampler = 0;
10501+ }
10502+}
10503+
10504+// called from _sg_gl_discard_texture() and _sg_gl_discard_sampler()
10505+_SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture_sampler(GLuint tex, GLuint smp) {
10506+ _SG_GL_CHECK_ERROR();
10507+ for (size_t i = 0; i < _SG_GL_MAX_TEX_SMP_BINDINGS; i++) {
10508+ _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[i];
10509+ if ((0 != slot->target) && ((tex == slot->texture) || (smp == slot->sampler))) {
10510+ _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + i));
10511+ glBindTexture(slot->target, 0);
10512+ _SG_GL_CHECK_ERROR();
10513+ _sg_stats_inc(gl.num_bind_texture);
10514+ glBindSampler((GLuint)i, 0);
10515+ _SG_GL_CHECK_ERROR();
10516+ _sg_stats_inc(gl.num_bind_sampler);
10517+ slot->target = 0;
10518+ slot->texture = 0;
10519+ slot->sampler = 0;
10520+ }
10521+ }
10522+ if ((tex == _sg.gl.cache.stored_texture_sampler.texture) || (smp == _sg.gl.cache.stored_texture_sampler.sampler)) {
10523+ _sg.gl.cache.stored_texture_sampler.target = 0;
10524+ _sg.gl.cache.stored_texture_sampler.texture = 0;
10525+ _sg.gl.cache.stored_texture_sampler.sampler = 0;
10526+ }
10527+}
10528+
10529+// called from _sg_gl_discard_shader()
10530+_SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) {
10531+ if (prog == _sg.gl.cache.prog) {
10532+ _sg.gl.cache.prog = 0;
10533+ glUseProgram(0);
10534+ _sg_stats_inc(gl.num_use_program);
10535+ }
10536+}
10537+
10538+// called from _sg_gl_discard_pipeline()
10539+_SOKOL_PRIVATE void _sg_gl_cache_invalidate_pipeline(_sg_pipeline_t* pip) {
10540+ if (_sg_sref_slot_eql(&_sg.gl.cache.cur_pip, &pip->slot)) {
10541+ _sg.gl.cache.cur_pip = _sg_sref(0);
10542+ }
10543+}
10544+
10545+_SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) {
10546+ _SG_GL_CHECK_ERROR();
10547+ glBindVertexArray(_sg.gl.vao);
10548+ _SG_GL_CHECK_ERROR();
10549+ _sg_clear(&_sg.gl.cache, sizeof(_sg.gl.cache));
10550+ _sg_gl_cache_clear_buffer_bindings(true);
10551+ _SG_GL_CHECK_ERROR();
10552+ _sg_gl_cache_clear_texture_sampler_bindings(true);
10553+ _SG_GL_CHECK_ERROR();
10554+ for (int i = 0; i < _sg.limits.max_vertex_attrs; i++) {
10555+ _sg_gl_attr_t* attr = &_sg.gl.cache.attrs[i].gl_attr;
10556+ attr->vb_index = -1;
10557+ attr->divisor = -1;
10558+ glDisableVertexAttribArray((GLuint)i);
10559+ _SG_GL_CHECK_ERROR();
10560+ _sg_stats_inc(gl.num_disable_vertex_attrib_array);
10561+ }
10562+ _sg.gl.cache.cur_primitive_type = GL_TRIANGLES;
10563+
10564+ // shader program
10565+ glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog);
10566+ _SG_GL_CHECK_ERROR();
10567+
10568+ // depth and stencil state
10569+ _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS;
10570+ _sg.gl.cache.stencil.front.compare = SG_COMPAREFUNC_ALWAYS;
10571+ _sg.gl.cache.stencil.front.fail_op = SG_STENCILOP_KEEP;
10572+ _sg.gl.cache.stencil.front.depth_fail_op = SG_STENCILOP_KEEP;
10573+ _sg.gl.cache.stencil.front.pass_op = SG_STENCILOP_KEEP;
10574+ _sg.gl.cache.stencil.back.compare = SG_COMPAREFUNC_ALWAYS;
10575+ _sg.gl.cache.stencil.back.fail_op = SG_STENCILOP_KEEP;
10576+ _sg.gl.cache.stencil.back.depth_fail_op = SG_STENCILOP_KEEP;
10577+ _sg.gl.cache.stencil.back.pass_op = SG_STENCILOP_KEEP;
10578+ glEnable(GL_DEPTH_TEST);
10579+ glDepthFunc(GL_ALWAYS);
10580+ glDepthMask(GL_FALSE);
10581+ glDisable(GL_STENCIL_TEST);
10582+ glStencilFunc(GL_ALWAYS, 0, 0);
10583+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
10584+ glStencilMask(0);
10585+ _sg_stats_add(gl.num_render_state, 7);
10586+
10587+ // blend state
10588+ _sg.gl.cache.blend.src_factor_rgb = SG_BLENDFACTOR_ONE;
10589+ _sg.gl.cache.blend.dst_factor_rgb = SG_BLENDFACTOR_ZERO;
10590+ _sg.gl.cache.blend.op_rgb = SG_BLENDOP_ADD;
10591+ _sg.gl.cache.blend.src_factor_alpha = SG_BLENDFACTOR_ONE;
10592+ _sg.gl.cache.blend.dst_factor_alpha = SG_BLENDFACTOR_ZERO;
10593+ _sg.gl.cache.blend.op_alpha = SG_BLENDOP_ADD;
10594+ glDisable(GL_BLEND);
10595+ glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
10596+ glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
10597+ glBlendColor(0.0f, 0.0f, 0.0f, 0.0f);
10598+ _sg_stats_add(gl.num_render_state, 4);
10599+
10600+ // standalone state
10601+ for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) {
10602+ _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA;
10603+ }
10604+ _sg.gl.cache.cull_mode = SG_CULLMODE_NONE;
10605+ _sg.gl.cache.face_winding = SG_FACEWINDING_CW;
10606+ _sg.gl.cache.sample_count = 1;
10607+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
10608+ glPolygonOffset(0.0f, 0.0f);
10609+ glDisable(GL_POLYGON_OFFSET_FILL);
10610+ glDisable(GL_CULL_FACE);
10611+ glFrontFace(GL_CW);
10612+ glCullFace(GL_BACK);
10613+ glEnable(GL_SCISSOR_TEST);
10614+ glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
10615+ glEnable(GL_DITHER);
10616+ glDisable(GL_POLYGON_OFFSET_FILL);
10617+ _sg_stats_add(gl.num_render_state, 10);
10618+ #if defined(SOKOL_GLCORE)
10619+ glEnable(GL_MULTISAMPLE);
10620+ glEnable(GL_PROGRAM_POINT_SIZE);
10621+ _sg_stats_add(gl.num_render_state, 2);
10622+ #endif
10623+}
10624+
10625+_SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) {
10626+ _SOKOL_UNUSED(desc);
10627+
10628+ // assumes that _sg.gl is already zero-initialized
10629+ _sg.gl.valid = true;
10630+
10631+ #if defined(_SOKOL_USE_WIN32_GL_LOADER)
10632+ _sg_gl_load_opengl();
10633+ #endif
10634+
10635+ // clear initial GL error state
10636+ #if defined(SOKOL_DEBUG)
10637+ while (glGetError() != GL_NO_ERROR);
10638+ #endif
10639+ #if defined(SOKOL_GLCORE)
10640+ _sg_gl_init_caps_glcore();
10641+ #elif defined(SOKOL_GLES3)
10642+ _sg_gl_init_caps_gles3();
10643+ #endif
10644+
10645+ // create and bind global vertex array object which will be mutated as needed
10646+ glGenVertexArrays(1, &_sg.gl.vao);
10647+ glBindVertexArray(_sg.gl.vao);
10648+ _SG_GL_CHECK_ERROR();
10649+
10650+ // create global framebuffer object which will be mutated as needed
10651+ glGenFramebuffers(1, &_sg.gl.fb);
10652+ _SG_GL_CHECK_ERROR();
10653+
10654+ // incoming texture data is generally expected to be packed tightly
10655+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
10656+ #if defined(SOKOL_GLCORE)
10657+ // enable seamless cubemap sampling (only desktop GL)
10658+ glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
10659+ #endif
10660+ _sg_gl_reset_state_cache();
10661+}
10662+
10663+_SOKOL_PRIVATE void _sg_gl_discard_backend(void) {
10664+ SOKOL_ASSERT(_sg.gl.valid);
10665+ if (_sg.gl.fb) {
10666+ glDeleteFramebuffers(1, &_sg.gl.fb);
10667+ }
10668+ if (_sg.gl.vao) {
10669+ glDeleteVertexArrays(1, &_sg.gl.vao);
10670+ }
10671+ #if defined(_SOKOL_USE_WIN32_GL_LOADER)
10672+ _sg_gl_unload_opengl();
10673+ #endif
10674+ _sg.gl.valid = false;
10675+}
10676+
10677+//-- GL backend resource creation and destruction ------------------------------
10678+_SOKOL_PRIVATE sg_resource_state _sg_gl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) {
10679+ SOKOL_ASSERT(buf && desc);
10680+ _SG_GL_CHECK_ERROR();
10681+ buf->gl.injected = (0 != desc->gl_buffers[0]);
10682+ const GLenum gl_target = _sg_gl_buffer_target(&buf->cmn.usage);
10683+ const GLenum gl_usage = _sg_gl_buffer_usage(&buf->cmn.usage);
10684+ for (int slot = 0; slot < buf->cmn.num_slots; slot++) {
10685+ GLuint gl_buf = 0;
10686+ if (buf->gl.injected) {
10687+ SOKOL_ASSERT(desc->gl_buffers[slot]);
10688+ gl_buf = desc->gl_buffers[slot];
10689+ } else {
10690+ glGenBuffers(1, &gl_buf);
10691+ SOKOL_ASSERT(gl_buf);
10692+ _sg_gl_cache_store_buffer_binding(gl_target);
10693+ _sg_gl_cache_bind_buffer(gl_target, gl_buf);
10694+ glBufferData(gl_target, buf->cmn.size, 0, gl_usage);
10695+ if (desc->data.ptr) {
10696+ glBufferSubData(gl_target, 0, buf->cmn.size, desc->data.ptr);
10697+ }
10698+ _sg_gl_cache_restore_buffer_binding(gl_target);
10699+ }
10700+ buf->gl.buf[slot] = gl_buf;
10701+ }
10702+ _SG_GL_CHECK_ERROR();
10703+ return SG_RESOURCESTATE_VALID;
10704+}
10705+
10706+_SOKOL_PRIVATE void _sg_gl_discard_buffer(_sg_buffer_t* buf) {
10707+ SOKOL_ASSERT(buf);
10708+ _SG_GL_CHECK_ERROR();
10709+ for (int slot = 0; slot < buf->cmn.num_slots; slot++) {
10710+ if (buf->gl.buf[slot]) {
10711+ _sg_gl_cache_invalidate_buffer(buf->gl.buf[slot]);
10712+ if (!buf->gl.injected) {
10713+ glDeleteBuffers(1, &buf->gl.buf[slot]);
10714+ }
10715+ }
10716+ }
10717+ _SG_GL_CHECK_ERROR();
10718+}
10719+
10720+_SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) {
10721+ const int fmt_index = (int) fmt;
10722+ SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM));
10723+ return _sg.formats[fmt_index].sample;
10724+}
10725+
10726+_SOKOL_PRIVATE void _sg_gl_texstorage(const _sg_image_t* img) {
10727+ const GLenum tgt = img->gl.target;
10728+ const int num_mips = img->cmn.num_mipmaps;
10729+ #if defined(_SOKOL_GL_HAS_TEXSTORAGE)
10730+ const GLenum ifmt = _sg_gl_teximage_internal_format(img->cmn.pixel_format);
10731+ const bool msaa = img->cmn.sample_count > 1;
10732+ const int w = img->cmn.width;
10733+ const int h = img->cmn.height;
10734+ if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) {
10735+ #if defined(SOKOL_GLCORE)
10736+ if (msaa) {
10737+ glTexStorage2DMultisample(tgt, img->cmn.sample_count, ifmt, w, h, GL_TRUE);
10738+ } else {
10739+ glTexStorage2D(tgt, num_mips, ifmt, w, h);
10740+ }
10741+ #else
10742+ SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa);
10743+ glTexStorage2D(tgt, num_mips, ifmt, w, h);
10744+ #endif
10745+ } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) {
10746+ const int depth = img->cmn.num_slices;
10747+ #if defined(SOKOL_GLCORE)
10748+ if (msaa) {
10749+ // NOTE: MSAA works only for array textures, not 3D textures
10750+ glTexStorage3DMultisample(tgt, img->cmn.sample_count, ifmt, w, h, depth, GL_TRUE);
10751+ } else {
10752+ glTexStorage3D(tgt, num_mips, ifmt, w, h, depth);
10753+ }
10754+ #else
10755+ SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa);
10756+ glTexStorage3D(tgt, num_mips, ifmt, w, h, depth);
10757+ #endif
10758+ }
10759+ #else
10760+ glTexParameteri(tgt, GL_TEXTURE_MAX_LEVEL, num_mips - 1);
10761+ #endif
10762+ _SG_GL_CHECK_ERROR();
10763+}
10764+
10765+_SOKOL_PRIVATE void _sg_gl_texsubimage(const _sg_image_t* img, GLenum tgt, int mip_index, int w, int h, int depth, const GLvoid* data_ptr, GLsizei data_size) {
10766+ SOKOL_ASSERT(data_ptr && (data_size > 0));
10767+ SOKOL_ASSERT(img->cmn.sample_count == 1);
10768+ const bool compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format);
10769+ if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) {
10770+ if (compressed) {
10771+ const GLenum ifmt = _sg_gl_teximage_internal_format(img->cmn.pixel_format);
10772+ glCompressedTexSubImage2D(tgt, mip_index, 0, 0, w, h, ifmt, data_size, data_ptr);
10773+ } else {
10774+ const GLenum type = _sg_gl_teximage_type(img->cmn.pixel_format);
10775+ const GLenum fmt = _sg_gl_teximage_format(img->cmn.pixel_format);
10776+ glTexSubImage2D(tgt, mip_index, 0, 0, w, h, fmt, type, data_ptr);
10777+ }
10778+ } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) {
10779+ if (compressed) {
10780+ const GLenum ifmt = _sg_gl_teximage_internal_format(img->cmn.pixel_format);
10781+ glCompressedTexSubImage3D(tgt, mip_index, 0, 0, 0, w, h, depth, ifmt, data_size, data_ptr);
10782+ } else {
10783+ const GLenum type = _sg_gl_teximage_type(img->cmn.pixel_format);
10784+ const GLenum fmt = _sg_gl_teximage_format(img->cmn.pixel_format);
10785+ glTexSubImage3D(tgt, mip_index, 0, 0, 0, w, h, depth, fmt, type, data_ptr);
10786+ }
10787+ }
10788+}
10789+
10790+_SOKOL_PRIVATE void _sg_gl_teximage(const _sg_image_t* img, GLenum tgt, int mip_index, int w, int h, int depth, const GLvoid* data_ptr, GLsizei data_size) {
10791+ #if defined(_SOKOL_GL_HAS_TEXSTORAGE)
10792+ if (data_ptr == 0) {
10793+ return;
10794+ }
10795+ _sg_gl_texsubimage(img, tgt, mip_index, w, h, depth, data_ptr, data_size);
10796+ #else
10797+ const bool compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format);
10798+ const GLenum ifmt = _sg_gl_teximage_internal_format(img->cmn.pixel_format);
10799+ const bool msaa = img->cmn.sample_count > 1;
10800+ if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) {
10801+ if (compressed) {
10802+ SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa);
10803+ glCompressedTexImage2D(tgt, mip_index, ifmt, w, h, 0, data_size, data_ptr);
10804+ } else {
10805+ const GLenum type = _sg_gl_teximage_type(img->cmn.pixel_format);
10806+ const GLenum fmt = _sg_gl_teximage_format(img->cmn.pixel_format);
10807+ #if defined(SOKOL_GLCORE) && !defined(__APPLE__)
10808+ if (msaa) {
10809+ glTexImage2DMultisample(tgt, img->cmn.sample_count, ifmt, w, h, GL_TRUE);
10810+ } else {
10811+ glTexImage2D(tgt, mip_index, (GLint)ifmt, w, h, 0, fmt, type, data_ptr);
10812+ }
10813+ #else
10814+ SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa);
10815+ glTexImage2D(tgt, mip_index, (GLint)ifmt, w, h, 0, fmt, type, data_ptr);
10816+ #endif
10817+ }
10818+ } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) {
10819+ if (compressed) {
10820+ SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa);
10821+ glCompressedTexImage3D(tgt, mip_index, ifmt, w, h, depth, 0, data_size, data_ptr);
10822+ } else {
10823+ const GLenum type = _sg_gl_teximage_type(img->cmn.pixel_format);
10824+ const GLenum fmt = _sg_gl_teximage_format(img->cmn.pixel_format);
10825+ #if defined(SOKOL_GLCORE) && !defined(__APPLE__)
10826+ if (msaa) {
10827+ // NOTE: MSAA works only for array textures, not 3D textures
10828+ glTexImage3DMultisample(tgt, img->cmn.sample_count, ifmt, w, h, depth, GL_TRUE);
10829+ } else {
10830+ glTexImage3D(tgt, mip_index, (GLint)ifmt, w, h, depth, 0, fmt, type, data_ptr);
10831+ }
10832+ #else
10833+ SOKOL_ASSERT(!msaa); _SOKOL_UNUSED(msaa);
10834+ glTexImage3D(tgt, mip_index, (GLint)ifmt, w, h, depth, 0, fmt, type, data_ptr);
10835+ #endif
10836+ }
10837+ }
10838+ #endif
10839+ _SG_GL_CHECK_ERROR();
10840+}
10841+
10842+_SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_image_desc* desc) {
10843+ SOKOL_ASSERT(img && desc);
10844+ _SG_GL_CHECK_ERROR();
10845+ img->gl.injected = (0 != desc->gl_textures[0]);
10846+
10847+ // check if texture format is support
10848+ if (!_sg_gl_supported_texture_format(img->cmn.pixel_format)) {
10849+ _SG_ERROR(GL_TEXTURE_FORMAT_NOT_SUPPORTED);
10850+ return SG_RESOURCESTATE_FAILED;
10851+ }
10852+
10853+ if (img->gl.injected) {
10854+ img->gl.target = _sg_gl_texture_target(img->cmn.type, img->cmn.sample_count);
10855+ // inject externally GL textures
10856+ for (int slot = 0; slot < img->cmn.num_slots; slot++) {
10857+ SOKOL_ASSERT(desc->gl_textures[slot]);
10858+ img->gl.tex[slot] = desc->gl_textures[slot];
10859+ }
10860+ if (desc->gl_texture_target) {
10861+ img->gl.target = (GLenum)desc->gl_texture_target;
10862+ }
10863+ } else {
10864+ // on platforms that don't support MSAA texture bindings, no actual GL
10865+ // texture object is created, instead only attachment view object can be built
10866+ const bool msaa = img->cmn.sample_count > 1;
10867+ if (msaa && !_sg.features.msaa_texture_bindings) {
10868+ if (img->cmn.usage.color_attachment || img->cmn.usage.depth_stencil_attachment) {
10869+ return SG_RESOURCESTATE_VALID;
10870+ } else {
10871+ return SG_RESOURCESTATE_FAILED;
10872+ }
10873+ }
10874+ img->gl.target = _sg_gl_texture_target(img->cmn.type, img->cmn.sample_count);
10875+ for (int slot = 0; slot < img->cmn.num_slots; slot++) {
10876+ glGenTextures(1, &img->gl.tex[slot]);
10877+ SOKOL_ASSERT(img->gl.tex[slot]);
10878+ _sg_gl_cache_store_texture_sampler_binding(0);
10879+ _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[slot], 0);
10880+ _sg_gl_texstorage(img);
10881+ for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) {
10882+ const GLvoid* data_ptr = desc->data.mip_levels[mip_index].ptr;
10883+ const GLsizei data_size = (GLsizei)desc->data.mip_levels[mip_index].size;
10884+ const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index);
10885+ const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index);
10886+ const int mip_depth = (SG_IMAGETYPE_3D == img->cmn.type) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices;
10887+ if (SG_IMAGETYPE_CUBE == img->cmn.type) {
10888+ const int surf_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1);
10889+ // NOTE: surf_ptr is allowed to be null here
10890+ const uint8_t* surf_ptr = (const uint8_t*) data_ptr;
10891+ for (int i = 0; i < 6; i++) {
10892+ const GLenum gl_img_target = _sg_gl_cubeface_target(i);
10893+ _sg_gl_teximage(img, gl_img_target, mip_index, mip_width, mip_height, mip_depth, surf_ptr, surf_pitch);
10894+ if (data_ptr) {
10895+ SOKOL_ASSERT((6 * surf_pitch) <= data_size);
10896+ surf_ptr += surf_pitch;
10897+ }
10898+ }
10899+ } else {
10900+ _sg_gl_teximage(img, img->gl.target, mip_index, mip_width, mip_height, mip_depth, data_ptr, data_size);
10901+ }
10902+ }
10903+ _sg_gl_cache_restore_texture_sampler_binding(0);
10904+ }
10905+ }
10906+ _SG_GL_CHECK_ERROR();
10907+ return SG_RESOURCESTATE_VALID;
10908+}
10909+
10910+_SOKOL_PRIVATE void _sg_gl_discard_image(_sg_image_t* img) {
10911+ SOKOL_ASSERT(img);
10912+ _SG_GL_CHECK_ERROR();
10913+ for (int slot = 0; slot < img->cmn.num_slots; slot++) {
10914+ if (img->gl.tex[slot]) {
10915+ _sg_gl_cache_invalidate_texture_sampler(img->gl.tex[slot], 0);
10916+ if (!img->gl.injected) {
10917+ glDeleteTextures(1, &img->gl.tex[slot]);
10918+ }
10919+ }
10920+ }
10921+ _SG_GL_CHECK_ERROR();
10922+}
10923+
10924+_SOKOL_PRIVATE sg_resource_state _sg_gl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) {
10925+ SOKOL_ASSERT(smp && desc);
10926+ _SG_GL_CHECK_ERROR();
10927+ smp->gl.injected = (0 != desc->gl_sampler);
10928+ if (smp->gl.injected) {
10929+ smp->gl.smp = (GLuint) desc->gl_sampler;
10930+ } else {
10931+ glGenSamplers(1, &smp->gl.smp);
10932+ SOKOL_ASSERT(smp->gl.smp);
10933+
10934+ const GLenum gl_min_filter = _sg_gl_min_filter(smp->cmn.min_filter, smp->cmn.mipmap_filter);
10935+ const GLenum gl_mag_filter = _sg_gl_mag_filter(smp->cmn.mag_filter);
10936+ glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MIN_FILTER, (GLint)gl_min_filter);
10937+ glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAG_FILTER, (GLint)gl_mag_filter);
10938+ // GL spec has strange defaults for mipmap min/max lod: -1000 to +1000
10939+ const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f);
10940+ const float max_lod = _sg_clamp(desc->max_lod, 0.0f, 1000.0f);
10941+ glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MIN_LOD, min_lod);
10942+ glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MAX_LOD, max_lod);
10943+ glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_S, (GLint)_sg_gl_wrap(smp->cmn.wrap_u));
10944+ glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(smp->cmn.wrap_v));
10945+ glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(smp->cmn.wrap_w));
10946+ #if defined(SOKOL_GLCORE)
10947+ float border[4];
10948+ switch (smp->cmn.border_color) {
10949+ case SG_BORDERCOLOR_TRANSPARENT_BLACK:
10950+ border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 0.0f;
10951+ break;
10952+ case SG_BORDERCOLOR_OPAQUE_WHITE:
10953+ border[0] = 1.0f; border[1] = 1.0f; border[2] = 1.0f; border[3] = 1.0f;
10954+ break;
10955+ default:
10956+ border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f;
10957+ break;
10958+ }
10959+ glSamplerParameterfv(smp->gl.smp, GL_TEXTURE_BORDER_COLOR, border);
10960+ #endif
10961+ if (smp->cmn.compare != SG_COMPAREFUNC_NEVER) {
10962+ glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
10963+ glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_FUNC, (GLint)_sg_gl_compare_func(smp->cmn.compare));
10964+ } else {
10965+ glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_NONE);
10966+ }
10967+ if (_sg.gl.ext_anisotropic && (smp->cmn.max_anisotropy > 1)) {
10968+ GLint max_aniso = (GLint) smp->cmn.max_anisotropy;
10969+ if (max_aniso > _sg.gl.max_anisotropy) {
10970+ max_aniso = _sg.gl.max_anisotropy;
10971+ }
10972+ glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso);
10973+ }
10974+ }
10975+ _SG_GL_CHECK_ERROR();
10976+ return SG_RESOURCESTATE_VALID;
10977+}
10978+
10979+_SOKOL_PRIVATE void _sg_gl_discard_sampler(_sg_sampler_t* smp) {
10980+ SOKOL_ASSERT(smp);
10981+ _SG_GL_CHECK_ERROR();
10982+ _sg_gl_cache_invalidate_texture_sampler(0, smp->gl.smp);
10983+ if (!smp->gl.injected) {
10984+ glDeleteSamplers(1, &smp->gl.smp);
10985+ }
10986+ _SG_GL_CHECK_ERROR();
10987+}
10988+
10989+_SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* src) {
10990+ SOKOL_ASSERT(src);
10991+ _SG_GL_CHECK_ERROR();
10992+ GLuint gl_shd = glCreateShader(_sg_gl_shader_stage(stage));
10993+ glShaderSource(gl_shd, 1, &src, 0);
10994+ glCompileShader(gl_shd);
10995+ GLint compile_status = 0;
10996+ glGetShaderiv(gl_shd, GL_COMPILE_STATUS, &compile_status);
10997+ if (!compile_status) {
10998+ // compilation failed, log error and delete shader
10999+ GLint log_len = 0;
11000+ glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len);
11001+ if (log_len > 0) {
11002+ GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len);
11003+ glGetShaderInfoLog(gl_shd, log_len, &log_len, log_buf);
11004+ _SG_ERROR(GL_SHADER_COMPILATION_FAILED);
11005+ _SG_LOGMSG(GL_SHADER_COMPILATION_FAILED, log_buf);
11006+ _sg_free(log_buf);
11007+ }
11008+ glDeleteShader(gl_shd);
11009+ gl_shd = 0;
11010+ }
11011+ _SG_GL_CHECK_ERROR();
11012+ return gl_shd;
11013+}
11014+
11015+// NOTE: this is an out-of-range check for GLSL bindslots that's also active in release mode
11016+_SOKOL_PRIVATE bool _sg_gl_ensure_glsl_bindslot_ranges(const sg_shader_desc* desc) {
11017+ SOKOL_ASSERT(desc); _SOKOL_UNUSED(desc);
11018+ #if defined(_SOKOL_GL_HAS_COMPUTE)
11019+ SOKOL_ASSERT(_sg.limits.max_storage_buffer_bindings_per_stage <= _SG_GL_MAX_SBUF_BINDINGS);
11020+ SOKOL_ASSERT(_sg.limits.max_storage_image_bindings_per_stage <= _SG_GL_MAX_SIMG_BINDINGS);
11021+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
11022+ const sg_shader_view* view = &desc->views[i];
11023+ if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) {
11024+ if (view->storage_buffer.glsl_binding_n >= _sg.limits.max_storage_buffer_bindings_per_stage) {
11025+ _SG_ERROR(GL_STORAGEBUFFER_GLSL_BINDING_OUT_OF_RANGE);
11026+ return false;
11027+ }
11028+ }
11029+ if (view->storage_image.stage != SG_SHADERSTAGE_NONE) {
11030+ if (view->storage_image.glsl_binding_n >= _sg.limits.max_storage_image_bindings_per_stage) {
11031+ _SG_ERROR(GL_STORAGEIMAGE_GLSL_BINDING_OUT_OF_RANGE);
11032+ return false;
11033+ }
11034+ }
11035+ }
11036+ #endif
11037+ return true;
11038+}
11039+
11040+_SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) {
11041+ SOKOL_ASSERT(shd && desc);
11042+ SOKOL_ASSERT(!shd->gl.prog);
11043+ _SG_GL_CHECK_ERROR();
11044+
11045+ // perform a fatal range-check on GLSL bindslots that's also active
11046+ // in release mode to avoid potential out-of-bounds array accesses
11047+ if (!_sg_gl_ensure_glsl_bindslot_ranges(desc)) {
11048+ return SG_RESOURCESTATE_FAILED;
11049+ }
11050+
11051+ // copy the optional vertex attribute names over
11052+ for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) {
11053+ _sg_strcpy(&shd->gl.attrs[i].name, desc->attrs[i].glsl_name);
11054+ }
11055+
11056+ const bool has_vs = desc->vertex_func.source;
11057+ const bool has_fs = desc->fragment_func.source;
11058+ const bool has_cs = desc->compute_func.source;
11059+ SOKOL_ASSERT((has_vs && has_fs) || has_cs);
11060+ GLuint gl_prog = glCreateProgram();
11061+ if (has_vs && has_fs) {
11062+ GLuint gl_vs = _sg_gl_compile_shader(SG_SHADERSTAGE_VERTEX, desc->vertex_func.source);
11063+ GLuint gl_fs = _sg_gl_compile_shader(SG_SHADERSTAGE_FRAGMENT, desc->fragment_func.source);
11064+ if (!(gl_vs && gl_fs)) {
11065+ glDeleteProgram(gl_prog);
11066+ if (gl_vs) { glDeleteShader(gl_vs); }
11067+ if (gl_fs) { glDeleteShader(gl_fs); }
11068+ return SG_RESOURCESTATE_FAILED;
11069+ }
11070+ glAttachShader(gl_prog, gl_vs);
11071+ glAttachShader(gl_prog, gl_fs);
11072+ glLinkProgram(gl_prog);
11073+ glDeleteShader(gl_vs);
11074+ glDeleteShader(gl_fs);
11075+ _SG_GL_CHECK_ERROR();
11076+ } else if (has_cs) {
11077+ GLuint gl_cs = _sg_gl_compile_shader(SG_SHADERSTAGE_COMPUTE, desc->compute_func.source);
11078+ if (!gl_cs) {
11079+ glDeleteProgram(gl_prog);
11080+ return SG_RESOURCESTATE_FAILED;
11081+ }
11082+ glAttachShader(gl_prog, gl_cs);
11083+ glLinkProgram(gl_prog);
11084+ glDeleteShader(gl_cs);
11085+ _SG_GL_CHECK_ERROR();
11086+ } else {
11087+ SOKOL_UNREACHABLE;
11088+ }
11089+ GLint link_status;
11090+ glGetProgramiv(gl_prog, GL_LINK_STATUS, &link_status);
11091+ if (!link_status) {
11092+ GLint log_len = 0;
11093+ glGetProgramiv(gl_prog, GL_INFO_LOG_LENGTH, &log_len);
11094+ if (log_len > 0) {
11095+ GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len);
11096+ glGetProgramInfoLog(gl_prog, log_len, &log_len, log_buf);
11097+ _SG_ERROR(GL_SHADER_LINKING_FAILED);
11098+ _SG_LOGMSG(GL_SHADER_LINKING_FAILED, log_buf);
11099+ _sg_free(log_buf);
11100+ }
11101+ glDeleteProgram(gl_prog);
11102+ return SG_RESOURCESTATE_FAILED;
11103+ }
11104+ shd->gl.prog = gl_prog;
11105+
11106+ // resolve uniforms
11107+ _SG_GL_CHECK_ERROR();
11108+ for (size_t ub_index = 0; ub_index < SG_MAX_UNIFORMBLOCK_BINDSLOTS; ub_index++) {
11109+ const sg_shader_uniform_block* ub_desc = &desc->uniform_blocks[ub_index];
11110+ if (ub_desc->stage == SG_SHADERSTAGE_NONE) {
11111+ continue;
11112+ }
11113+ SOKOL_ASSERT(ub_desc->size > 0);
11114+ _sg_gl_uniform_block_t* ub = &shd->gl.uniform_blocks[ub_index];
11115+ SOKOL_ASSERT(ub->num_uniforms == 0);
11116+ uint32_t cur_uniform_offset = 0;
11117+ for (int u_index = 0; u_index < SG_MAX_UNIFORMBLOCK_MEMBERS; u_index++) {
11118+ const sg_glsl_shader_uniform* u_desc = &ub_desc->glsl_uniforms[u_index];
11119+ if (u_desc->type == SG_UNIFORMTYPE_INVALID) {
11120+ break;
11121+ }
11122+ const uint32_t u_align = _sg_uniform_alignment(u_desc->type, u_desc->array_count, ub_desc->layout);
11123+ const uint32_t u_size = _sg_uniform_size(u_desc->type, u_desc->array_count, ub_desc->layout);
11124+ cur_uniform_offset = _sg_align_u32(cur_uniform_offset, u_align);
11125+ _sg_gl_uniform_t* u = &ub->uniforms[u_index];
11126+ u->type = u_desc->type;
11127+ u->count = (uint16_t) u_desc->array_count;
11128+ u->offset = (uint16_t) cur_uniform_offset;
11129+ SOKOL_ASSERT(u_desc->glsl_name);
11130+ u->gl_loc = glGetUniformLocation(gl_prog, u_desc->glsl_name);
11131+ if (u->gl_loc == -1) {
11132+ _SG_WARN(GL_UNIFORMBLOCK_NAME_NOT_FOUND_IN_SHADER);
11133+ _SG_LOGMSG(GL_UNIFORMBLOCK_NAME_NOT_FOUND_IN_SHADER, u_desc->glsl_name);
11134+ }
11135+ cur_uniform_offset += u_size;
11136+ ub->num_uniforms++;
11137+ }
11138+ if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) {
11139+ cur_uniform_offset = _sg_align_u32(cur_uniform_offset, 16);
11140+ }
11141+ SOKOL_ASSERT(ub_desc->size == (size_t)cur_uniform_offset);
11142+ _SOKOL_UNUSED(cur_uniform_offset);
11143+ }
11144+
11145+ // copy resource bindslot mappings
11146+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
11147+ const sg_shader_view* view = &desc->views[i];
11148+ SOKOL_ASSERT(0 == shd->gl.sbuf_binding[i]);
11149+ SOKOL_ASSERT(0 == shd->gl.simg_binding[i]);
11150+ if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) {
11151+ shd->gl.sbuf_binding[i] = view->storage_buffer.glsl_binding_n;
11152+ } else if (view->storage_image.stage != SG_SHADERSTAGE_NONE) {
11153+ shd->gl.simg_binding[i] = view->storage_image.glsl_binding_n;
11154+ }
11155+ }
11156+
11157+ // record image sampler location in shader program
11158+ _SG_GL_CHECK_ERROR();
11159+ GLuint cur_prog = 0;
11160+ glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&cur_prog);
11161+ glUseProgram(gl_prog);
11162+ GLint gl_tex_slot = 0;
11163+ for (size_t tex_smp_index = 0; tex_smp_index < SG_MAX_TEXTURE_SAMPLER_PAIRS; tex_smp_index++) {
11164+ const sg_shader_texture_sampler_pair* tex_smp_desc = &desc->texture_sampler_pairs[tex_smp_index];
11165+ if (tex_smp_desc->stage == SG_SHADERSTAGE_NONE) {
11166+ continue;
11167+ }
11168+ SOKOL_ASSERT(tex_smp_desc->glsl_name);
11169+ GLint gl_loc = glGetUniformLocation(gl_prog, tex_smp_desc->glsl_name);
11170+ if (gl_loc != -1) {
11171+ glUniform1i(gl_loc, gl_tex_slot);
11172+ shd->gl.tex_slot[tex_smp_index] = (int8_t)gl_tex_slot++;
11173+ } else {
11174+ shd->gl.tex_slot[tex_smp_index] = -1;
11175+ _SG_WARN(GL_IMAGE_SAMPLER_NAME_NOT_FOUND_IN_SHADER);
11176+ _SG_LOGMSG(GL_IMAGE_SAMPLER_NAME_NOT_FOUND_IN_SHADER, tex_smp_desc->glsl_name);
11177+ }
11178+ }
11179+
11180+ // it's legal to call glUseProgram with 0
11181+ glUseProgram(cur_prog);
11182+ _SG_GL_CHECK_ERROR();
11183+ return SG_RESOURCESTATE_VALID;
11184+}
11185+
11186+_SOKOL_PRIVATE void _sg_gl_discard_shader(_sg_shader_t* shd) {
11187+ SOKOL_ASSERT(shd);
11188+ _SG_GL_CHECK_ERROR();
11189+ if (shd->gl.prog) {
11190+ _sg_gl_cache_invalidate_program(shd->gl.prog);
11191+ glDeleteProgram(shd->gl.prog);
11192+ }
11193+ _SG_GL_CHECK_ERROR();
11194+}
11195+
11196+_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) {
11197+ SOKOL_ASSERT(pip && desc);
11198+ SOKOL_ASSERT(_sg.limits.max_vertex_attrs <= SG_MAX_VERTEX_ATTRIBUTES);
11199+ if (pip->cmn.is_compute) {
11200+ // shortcut for compute pipelines
11201+ return SG_RESOURCESTATE_VALID;
11202+ }
11203+ pip->gl.primitive_type = desc->primitive_type;
11204+ pip->gl.depth = desc->depth;
11205+ pip->gl.stencil = desc->stencil;
11206+ // FIXME: blend color and write mask per draw-buffer-attachment (requires GL4)
11207+ pip->gl.blend = desc->colors[0].blend;
11208+ for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) {
11209+ pip->gl.color_write_mask[i] = desc->colors[i].write_mask;
11210+ }
11211+ pip->gl.cull_mode = desc->cull_mode;
11212+ pip->gl.face_winding = desc->face_winding;
11213+ pip->gl.sample_count = desc->sample_count;
11214+ pip->gl.alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled;
11215+
11216+ // NOTE: GLSL compilers may remove unused vertex attributes so we can't rely
11217+ // on the 'prepopulated' vertex_buffer_layout_active[] state and need to
11218+ // fill this array from scratch with the actual info after GLSL compilation
11219+ for (int i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) {
11220+ pip->cmn.vertex_buffer_layout_active[i] = false;
11221+ }
11222+
11223+ // resolve vertex attributes
11224+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
11225+ SOKOL_ASSERT(shd->gl.prog);
11226+ for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) {
11227+ pip->gl.attrs[attr_index].vb_index = -1;
11228+ }
11229+ for (int attr_index = 0; attr_index < _sg.limits.max_vertex_attrs; attr_index++) {
11230+ const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index];
11231+ if (a_state->format == SG_VERTEXFORMAT_INVALID) {
11232+ break;
11233+ }
11234+ SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS);
11235+ const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index];
11236+ const sg_vertex_step step_func = l_state->step_func;
11237+ const int step_rate = l_state->step_rate;
11238+ GLint attr_loc = attr_index;
11239+ if (!_sg_strempty(&shd->gl.attrs[attr_index].name)) {
11240+ attr_loc = glGetAttribLocation(shd->gl.prog, _sg_strptr(&shd->gl.attrs[attr_index].name));
11241+ }
11242+ if (attr_loc != -1) {
11243+ SOKOL_ASSERT(attr_loc < (GLint)_sg.limits.max_vertex_attrs);
11244+ _sg_gl_attr_t* gl_attr = &pip->gl.attrs[attr_loc];
11245+ SOKOL_ASSERT(gl_attr->vb_index == -1);
11246+ gl_attr->vb_index = (int8_t) a_state->buffer_index;
11247+ if (step_func == SG_VERTEXSTEP_PER_VERTEX) {
11248+ gl_attr->divisor = 0;
11249+ } else {
11250+ gl_attr->divisor = (int8_t) step_rate;
11251+ }
11252+ SOKOL_ASSERT(l_state->stride > 0);
11253+ gl_attr->stride = (uint8_t) l_state->stride;
11254+ gl_attr->offset = a_state->offset;
11255+ gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_state->format);
11256+ gl_attr->type = _sg_gl_vertexformat_type(a_state->format);
11257+ gl_attr->normalized = _sg_gl_vertexformat_normalized(a_state->format);
11258+ gl_attr->base_type = _sg_vertexformat_basetype(a_state->format);
11259+ pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true;
11260+ } else {
11261+ _SG_WARN(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER);
11262+ _SG_LOGMSG(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, _sg_strptr(&shd->gl.attrs[attr_index].name));
11263+ }
11264+ }
11265+ return SG_RESOURCESTATE_VALID;
11266+}
11267+
11268+_SOKOL_PRIVATE void _sg_gl_discard_pipeline(_sg_pipeline_t* pip) {
11269+ SOKOL_ASSERT(pip);
11270+ _sg_gl_cache_invalidate_pipeline(pip);
11271+}
11272+
11273+_SOKOL_PRIVATE void _sg_gl_fb_attach_texture(const _sg_view_t* view, GLenum gl_att_type) {
11274+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
11275+ const GLuint gl_tex = img->gl.tex[0];
11276+ SOKOL_ASSERT(gl_tex);
11277+ const GLuint gl_target = img->gl.target;
11278+ SOKOL_ASSERT(gl_target);
11279+ const int mip_level = view->cmn.img.mip_level;
11280+ const int slice = view->cmn.img.slice;
11281+ switch (img->cmn.type) {
11282+ case SG_IMAGETYPE_2D:
11283+ glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, gl_target, gl_tex, mip_level);
11284+ break;
11285+ case SG_IMAGETYPE_CUBE:
11286+ glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, _sg_gl_cubeface_target(slice), gl_tex, mip_level);
11287+ break;
11288+ default:
11289+ glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att_type, gl_tex, mip_level, slice);
11290+ break;
11291+ }
11292+}
11293+
11294+_SOKOL_PRIVATE GLenum _sg_gl_depth_stencil_attachment_type(const _sg_image_t* ds_img) {
11295+ if (_sg_is_depth_stencil_format(ds_img->cmn.pixel_format)) {
11296+ return GL_DEPTH_STENCIL_ATTACHMENT;
11297+ } else {
11298+ return GL_DEPTH_ATTACHMENT;
11299+ }
11300+}
11301+
11302+_SOKOL_PRIVATE bool _sg_gl_check_framebuffer_status(void) {
11303+ const GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
11304+ if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
11305+ switch (fb_status) {
11306+ case GL_FRAMEBUFFER_UNDEFINED:
11307+ _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNDEFINED);
11308+ break;
11309+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
11310+ _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT);
11311+ break;
11312+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
11313+ _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT);
11314+ break;
11315+ case GL_FRAMEBUFFER_UNSUPPORTED:
11316+ _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNSUPPORTED);
11317+ break;
11318+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
11319+ _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE);
11320+ break;
11321+ default:
11322+ _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNKNOWN);
11323+ break;
11324+ }
11325+ return false;
11326+ }
11327+ return true;
11328+}
11329+
11330+_SOKOL_PRIVATE sg_resource_state _sg_gl_create_view(_sg_view_t* view, const sg_view_desc* desc) {
11331+ SOKOL_ASSERT(view && desc);
11332+ _SOKOL_UNUSED(desc);
11333+ _SG_GL_CHECK_ERROR();
11334+ if ((view->cmn.type == SG_VIEWTYPE_TEXTURE) && (_sg.features.gl_texture_views)) {
11335+ #if defined(_SOKOL_GL_HAS_TEXVIEWS)
11336+ if (_sg.features.gl_texture_views) {
11337+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
11338+ for (int slot = 0; slot < img->cmn.num_slots; slot++) {
11339+ SOKOL_ASSERT(img->gl.tex[slot] != 0);
11340+ const GLuint min_level = (GLuint)view->cmn.img.mip_level;
11341+ const GLuint num_levels = (GLuint)view->cmn.img.mip_level_count;
11342+ const GLuint min_layer = (GLuint)view->cmn.img.slice;
11343+ const GLuint num_layers = (GLuint)view->cmn.img.slice_count;
11344+ const GLenum ifmt = _sg_gl_teximage_internal_format(img->cmn.pixel_format);
11345+ glGenTextures(1, &view->gl.tex_view[slot]);
11346+ glTextureView(view->gl.tex_view[slot], img->gl.target, img->gl.tex[slot], ifmt, min_level, num_levels, min_layer, num_layers);
11347+ }
11348+ }
11349+ #endif
11350+ } else if ((view->cmn.type == SG_VIEWTYPE_COLORATTACHMENT) || (view->cmn.type == SG_VIEWTYPE_DEPTHSTENCILATTACHMENT)) {
11351+ // create MSAA render buffer if MSAA textures are not supported
11352+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
11353+ const bool msaa = img->cmn.sample_count > 1;
11354+ if (msaa && !_sg.features.msaa_texture_bindings) {
11355+ const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format);
11356+ glGenRenderbuffers(1, &view->gl.msaa_render_buffer);
11357+ glBindRenderbuffer(GL_RENDERBUFFER, view->gl.msaa_render_buffer);
11358+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_internal_format, img->cmn.width, img->cmn.height);
11359+ }
11360+ } else if (view->cmn.type == SG_VIEWTYPE_RESOLVEATTACHMENT) {
11361+ // store current framebuffer binding (restored at end of block)
11362+ GLuint gl_orig_fb;
11363+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb);
11364+
11365+ // create MSAA resolve framebuffer
11366+ glGenFramebuffers(1, &view->gl.msaa_resolve_frame_buffer);
11367+ glBindFramebuffer(GL_FRAMEBUFFER, view->gl.msaa_resolve_frame_buffer);
11368+ _sg_gl_fb_attach_texture(view, GL_COLOR_ATTACHMENT0);
11369+ if (!_sg_gl_check_framebuffer_status()) {
11370+ return SG_RESOURCESTATE_FAILED;
11371+ }
11372+ // setup color attachments for the framebuffer
11373+ static const GLenum gl_draw_buf = GL_COLOR_ATTACHMENT0;
11374+ glDrawBuffers(1, &gl_draw_buf);
11375+ // bind original framebuffer
11376+ glBindFramebuffer(GL_FRAMEBUFFER, gl_orig_fb);
11377+ }
11378+ _SG_GL_CHECK_ERROR();
11379+ return SG_RESOURCESTATE_VALID;
11380+}
11381+
11382+_SOKOL_PRIVATE void _sg_gl_discard_view(_sg_view_t* view) {
11383+ SOKOL_ASSERT(view);
11384+ _SG_GL_CHECK_ERROR();
11385+ for (size_t slot = 0; slot < SG_NUM_INFLIGHT_FRAMES; slot++) {
11386+ if (0 != view->gl.tex_view[slot]) {
11387+ // NOTE: cache invalidation also works as expected without
11388+ // GL texture view support, in that case the view's texture object
11389+ // will simply remain bound until the sg_image object is discarded
11390+ _sg_gl_cache_invalidate_texture_sampler(view->gl.tex_view[slot], 0);
11391+ glDeleteTextures(1, &view->gl.tex_view[slot]);
11392+ }
11393+ }
11394+ if (view->gl.msaa_render_buffer) {
11395+ glDeleteRenderbuffers(1, &view->gl.msaa_render_buffer);
11396+ }
11397+ if (view->gl.msaa_resolve_frame_buffer) {
11398+ glDeleteFramebuffers(1, &view->gl.msaa_resolve_frame_buffer);
11399+ }
11400+ _SG_GL_CHECK_ERROR();
11401+}
11402+
11403+#if defined(_SOKOL_GL_HAS_COMPUTE)
11404+_SOKOL_PRIVATE void _sg_gl_handle_memory_barriers(const _sg_shader_t* shd, const _sg_bindings_ptrs_t* bnd, const _sg_attachments_ptrs_t* atts) {
11405+ SOKOL_ASSERT((shd && bnd && atts == 0) || (atts && shd == 0 && bnd == 0));
11406+ if (!_sg.features.compute) {
11407+ return;
11408+ }
11409+ GLbitfield gl_barrier_bits = 0;
11410+
11411+ // if vertex-, index- or storage-buffer bindings have been written
11412+ // by a compute shader before, a barrier must be issued
11413+ if (bnd) {
11414+ for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) {
11415+ _sg_buffer_t* buf = bnd->vbs[i];
11416+ if (!buf) {
11417+ continue;
11418+ }
11419+ if (buf->gl.gpu_dirty_flags & _SG_GL_GPUDIRTY_VERTEXBUFFER) {
11420+ gl_barrier_bits |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
11421+ buf->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_VERTEXBUFFER;
11422+ }
11423+ }
11424+ if (bnd->ib) {
11425+ _sg_buffer_t* buf = bnd->ib;
11426+ if (buf->gl.gpu_dirty_flags & _SG_GL_GPUDIRTY_INDEXBUFFER) {
11427+ gl_barrier_bits |= GL_ELEMENT_ARRAY_BARRIER_BIT;
11428+ buf->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_INDEXBUFFER;
11429+ }
11430+ }
11431+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
11432+ const _sg_view_t* view = bnd->views[i];
11433+ if (!view) {
11434+ continue;
11435+ }
11436+ if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) {
11437+ _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref);
11438+ if (buf->gl.gpu_dirty_flags & _SG_GL_GPUDIRTY_STORAGEBUFFER) {
11439+ gl_barrier_bits |= GL_SHADER_STORAGE_BARRIER_BIT;
11440+ buf->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_STORAGEBUFFER;
11441+ }
11442+ } else if (view->cmn.type == SG_VIEWTYPE_TEXTURE) {
11443+ _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
11444+ if (img->gl.gpu_dirty_flags & _SG_GL_GPUDIRTY_TEXTURE) {
11445+ gl_barrier_bits |= GL_TEXTURE_FETCH_BARRIER_BIT;
11446+ img->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_TEXTURE;
11447+ }
11448+ } else if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) {
11449+ _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
11450+ if (img->gl.gpu_dirty_flags & _SG_GL_GPUDIRTY_STORAGEIMAGE) {
11451+ gl_barrier_bits |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
11452+ img->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_STORAGEIMAGE;
11453+ }
11454+ } else {
11455+ SOKOL_UNREACHABLE;
11456+ }
11457+ }
11458+ }
11459+ if (atts) {
11460+ for (int i = 0; i < atts->num_color_views; i++) {
11461+ const _sg_view_t* view = atts->color_views[i];
11462+ SOKOL_ASSERT(view);
11463+ _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
11464+ if (img->gl.gpu_dirty_flags & _SG_GL_GPUDIRTY_ATTACHMENT) {
11465+ gl_barrier_bits |= GL_FRAMEBUFFER_BARRIER_BIT;
11466+ img->gl.gpu_dirty_flags &= (uint8_t)~_SG_GL_GPUDIRTY_ATTACHMENT;
11467+ }
11468+ }
11469+ }
11470+ if (0 != gl_barrier_bits) {
11471+ glMemoryBarrier(gl_barrier_bits);
11472+ _sg_stats_inc(gl.num_memory_barriers);
11473+ }
11474+
11475+ // mark resources as dirty which will be written by compute shaders
11476+ // (don't merge this into the above loop, this would mess up the
11477+ // dirty flags if the same resource is bound multiple times)
11478+ if (bnd) {
11479+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
11480+ const _sg_view_t* view = bnd->views[i];
11481+ if (!view) {
11482+ continue;
11483+ }
11484+ if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) {
11485+ if (!shd->cmn.views[i].sbuf_readonly) {
11486+ _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref);
11487+ buf->gl.gpu_dirty_flags = _SG_GL_GPUDIRTY_BUFFER_ALL;
11488+ }
11489+ } else if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) {
11490+ // NOTE: storage image bindings are always written, otherwise
11491+ // they would be texture bindings!
11492+ _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
11493+ img->gl.gpu_dirty_flags = _SG_GL_GPUDIRTY_IMAGE_ALL;
11494+ }
11495+ }
11496+ }
11497+}
11498+#endif
11499+
11500+_SOKOL_PRIVATE void _sg_gl_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) {
11501+ SOKOL_ASSERT(pass && atts);
11502+ _SG_GL_CHECK_ERROR();
11503+
11504+ // early out if this a compute pass
11505+ if (pass->compute) {
11506+ return;
11507+ }
11508+
11509+ const sg_swapchain* swapchain = &pass->swapchain;
11510+ const sg_pass_action* action = &pass->action;
11511+ const bool is_swapchain_pass = atts->empty;
11512+ const bool is_offscreen_pass = !atts->empty;
11513+
11514+ // bind the render pass framebuffer
11515+ if (is_offscreen_pass) {
11516+
11517+ // offscreen pass, mutate the global offscreen framebuffer object
11518+ #if defined(SOKOL_GLCORE)
11519+ glEnable(GL_FRAMEBUFFER_SRGB);
11520+ #endif
11521+ glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.fb);
11522+ for (int i = 0; i < atts->num_color_views; i++) {
11523+ const _sg_view_t* view = atts->color_views[i];
11524+ const GLenum gl_att_type = (GLenum)(GL_COLOR_ATTACHMENT0 + i);
11525+ if (view->gl.msaa_render_buffer) {
11526+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, gl_att_type, GL_RENDERBUFFER, view->gl.msaa_render_buffer);
11527+ } else {
11528+ _sg_gl_fb_attach_texture(view, gl_att_type);
11529+ }
11530+ }
11531+ // explicitly detach unused color attachments
11532+ for (int i = atts->num_color_views; i < _sg.limits.max_color_attachments; i++) {
11533+ const GLenum gl_att_type = (GLenum)(GL_COLOR_ATTACHMENT0 + i);
11534+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, gl_att_type, GL_RENDERBUFFER, 0);
11535+ glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, GL_TEXTURE_2D, 0, 0);
11536+ }
11537+ if (atts->ds_view) {
11538+ // When switching between depth-only and depth-stencil attachments,
11539+ // explicitly detach BOTH attachment types first. Some GL drivers
11540+ // fail with GL_FRAMEBUFFER_UNSUPPORTED if both attachment types
11541+ // are bound to the same FBO.
11542+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
11543+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
11544+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
11545+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
11546+ const _sg_view_t* view = atts->ds_view;
11547+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
11548+ const GLenum gl_att_type = _sg_gl_depth_stencil_attachment_type(img);
11549+ if (view->gl.msaa_render_buffer) {
11550+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, gl_att_type, GL_RENDERBUFFER, view->gl.msaa_render_buffer);
11551+ } else {
11552+ _sg_gl_fb_attach_texture(view, gl_att_type);
11553+ }
11554+ } else {
11555+ // explicitly detach depth-stencil attachment if not used in this pass
11556+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
11557+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
11558+ }
11559+ if (!_sg_gl_check_framebuffer_status()) {
11560+ _sg.cur_pass.valid = false;
11561+ return;
11562+ }
11563+ GLenum gl_draw_bufs[SG_MAX_COLOR_ATTACHMENTS];
11564+ SOKOL_ASSERT(_sg.limits.max_color_attachments <= SG_MAX_COLOR_ATTACHMENTS);
11565+ for (int i = 0; i < _sg.limits.max_color_attachments; i++) {
11566+ if (i < atts->num_color_views) {
11567+ gl_draw_bufs[i] = (GLenum)(GL_COLOR_ATTACHMENT0 + i);
11568+ } else {
11569+ gl_draw_bufs[i] = GL_NONE;
11570+ }
11571+ }
11572+ glDrawBuffers(_sg.limits.max_color_attachments, gl_draw_bufs);
11573+
11574+ #if defined(_SOKOL_GL_HAS_COMPUTE)
11575+ _sg_gl_handle_memory_barriers(0, 0, atts);
11576+ _SG_GL_CHECK_ERROR();
11577+ #endif
11578+
11579+ } else {
11580+ // swapchain pass
11581+ #if defined(SOKOL_GLCORE)
11582+ if (_sg_is_srgb_format(swapchain->color_format)) {
11583+ glEnable(GL_FRAMEBUFFER_SRGB);
11584+ } else {
11585+ glDisable(GL_FRAMEBUFFER_SRGB);
11586+ }
11587+ #endif
11588+ // NOTE: on some platforms, the default framebuffer of a context
11589+ // is null, so we can't actually assert here that the
11590+ // framebuffer has been provided
11591+ glBindFramebuffer(GL_FRAMEBUFFER, swapchain->gl.framebuffer);
11592+ }
11593+ glViewport(0, 0, _sg.cur_pass.dim.width, _sg.cur_pass.dim.height);
11594+ glScissor(0, 0, _sg.cur_pass.dim.width, _sg.cur_pass.dim.height);
11595+
11596+ // number of color attachments
11597+ const int num_color_atts = is_offscreen_pass ? atts->num_color_views : 1;
11598+
11599+ // clear color and depth-stencil attachments if needed
11600+ bool clear_any_color = false;
11601+ for (int i = 0; i < num_color_atts; i++) {
11602+ if (SG_LOADACTION_CLEAR == action->colors[i].load_action) {
11603+ clear_any_color = true;
11604+ break;
11605+ }
11606+ }
11607+ const bool clear_depth = (action->depth.load_action == SG_LOADACTION_CLEAR);
11608+ const bool clear_stencil = (action->stencil.load_action == SG_LOADACTION_CLEAR);
11609+
11610+ bool need_pip_cache_flush = false;
11611+ if (clear_any_color) {
11612+ bool need_color_mask_flush = false;
11613+ // NOTE: not a bug to iterate over all possible color attachments
11614+ for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) {
11615+ if (SG_COLORMASK_RGBA != _sg.gl.cache.color_write_mask[i]) {
11616+ need_pip_cache_flush = true;
11617+ need_color_mask_flush = true;
11618+ _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA;
11619+ }
11620+ }
11621+ if (need_color_mask_flush) {
11622+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
11623+ }
11624+ }
11625+ if (clear_depth) {
11626+ if (!_sg.gl.cache.depth.write_enabled) {
11627+ need_pip_cache_flush = true;
11628+ _sg.gl.cache.depth.write_enabled = true;
11629+ glDepthMask(GL_TRUE);
11630+ }
11631+ if (_sg.gl.cache.depth.compare != SG_COMPAREFUNC_ALWAYS) {
11632+ need_pip_cache_flush = true;
11633+ _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS;
11634+ glDepthFunc(GL_ALWAYS);
11635+ }
11636+ }
11637+ if (clear_stencil) {
11638+ if (_sg.gl.cache.stencil.write_mask != 0xFF) {
11639+ need_pip_cache_flush = true;
11640+ _sg.gl.cache.stencil.write_mask = 0xFF;
11641+ glStencilMask(0xFF);
11642+ }
11643+ }
11644+ if (need_pip_cache_flush) {
11645+ // we messed with the state cache directly, need to clear cached
11646+ // pipeline to force re-evaluation in next sg_apply_pipeline()
11647+ _sg.gl.cache.cur_pip = _sg_sref(0);
11648+ }
11649+ for (int i = 0; i < num_color_atts; i++) {
11650+ if (action->colors[i].load_action == SG_LOADACTION_CLEAR) {
11651+ glClearBufferfv(GL_COLOR, i, &action->colors[i].clear_value.r);
11652+ }
11653+ }
11654+ if (is_swapchain_pass || atts->ds_view) {
11655+ if (clear_depth && clear_stencil) {
11656+ glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.clear_value, action->stencil.clear_value);
11657+ } else if (clear_depth) {
11658+ glClearBufferfv(GL_DEPTH, 0, &action->depth.clear_value);
11659+ } else if (clear_stencil) {
11660+ GLint val = (GLint) action->stencil.clear_value;
11661+ glClearBufferiv(GL_STENCIL, 0, &val);
11662+ }
11663+ }
11664+ // keep store actions for end-pass
11665+ for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) {
11666+ _sg.gl.color_store_actions[i] = action->colors[i].store_action;
11667+ }
11668+ _sg.gl.depth_store_action = action->depth.store_action;
11669+ _sg.gl.stencil_store_action = action->stencil.store_action;
11670+
11671+ _SG_GL_CHECK_ERROR();
11672+}
11673+
11674+_SOKOL_PRIVATE void _sg_gl_end_render_pass(const _sg_attachments_ptrs_t* atts) {
11675+ SOKOL_ASSERT(atts);
11676+ if (!atts->empty) {
11677+ bool fb_read_bound = false;
11678+ bool fb_draw_bound = false;
11679+ const int num_color_atts = atts->num_color_views;
11680+ for (int i = 0; i < num_color_atts; i++) {
11681+ // perform MSAA resolve if needed
11682+ const _sg_view_t* rsv_view = atts->resolve_views[i];
11683+ if (rsv_view && rsv_view->gl.msaa_resolve_frame_buffer) {
11684+ if (!fb_read_bound) {
11685+ glBindFramebuffer(GL_READ_FRAMEBUFFER, _sg.gl.fb);
11686+ fb_read_bound = true;
11687+ }
11688+ const _sg_image_t* rsv_img = _sg_image_ref_ptr(&rsv_view->cmn.img.ref);
11689+ const int w = rsv_img->cmn.width;
11690+ const int h = rsv_img->cmn.height;
11691+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rsv_view->gl.msaa_resolve_frame_buffer);
11692+ glReadBuffer((GLenum)(GL_COLOR_ATTACHMENT0 + i));
11693+ glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
11694+ fb_draw_bound = true;
11695+ }
11696+ }
11697+
11698+ // invalidate framebuffers
11699+ _SOKOL_UNUSED(fb_draw_bound);
11700+ #if defined(SOKOL_GLES3)
11701+ // need to restore framebuffer binding before invalidate if the MSAA resolve had changed the binding
11702+ if (fb_draw_bound) {
11703+ glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.fb);
11704+ }
11705+ GLenum invalidate_atts[SG_MAX_COLOR_ATTACHMENTS + 2] = { 0 };
11706+ int att_index = 0;
11707+ for (int i = 0; i < num_color_atts; i++) {
11708+ if (_sg.gl.color_store_actions[i] == SG_STOREACTION_DONTCARE) {
11709+ invalidate_atts[att_index++] = (GLenum)(GL_COLOR_ATTACHMENT0 + i);
11710+ }
11711+ }
11712+ if (!atts->ds_view) {
11713+ if (_sg.gl.depth_store_action == SG_STOREACTION_DONTCARE) {
11714+ invalidate_atts[att_index++] = GL_DEPTH_ATTACHMENT;
11715+ }
11716+ if (_sg.gl.stencil_store_action == SG_STOREACTION_DONTCARE) {
11717+ invalidate_atts[att_index++] = GL_STENCIL_ATTACHMENT;
11718+ }
11719+ }
11720+ if (att_index > 0) {
11721+ glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, att_index, invalidate_atts);
11722+ }
11723+ #endif
11724+ }
11725+}
11726+
11727+_SOKOL_PRIVATE void _sg_gl_end_pass(const _sg_attachments_ptrs_t* atts) {
11728+ _SG_GL_CHECK_ERROR();
11729+ if (!_sg.cur_pass.is_compute) {
11730+ _sg_gl_end_render_pass(atts);
11731+ }
11732+ _SG_GL_CHECK_ERROR();
11733+}
11734+
11735+_SOKOL_PRIVATE void _sg_gl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) {
11736+ y = origin_top_left ? (_sg.cur_pass.dim.height - (y+h)) : y;
11737+ glViewport(x, y, w, h);
11738+}
11739+
11740+_SOKOL_PRIVATE void _sg_gl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) {
11741+ y = origin_top_left ? (_sg.cur_pass.dim.height - (y+h)) : y;
11742+ glScissor(x, y, w, h);
11743+}
11744+
11745+_SOKOL_PRIVATE void _sg_gl_apply_render_pipeline_state(_sg_pipeline_t* pip) {
11746+ // update render pipeline state
11747+ _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->gl.primitive_type);
11748+ _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->cmn.index_type);
11749+
11750+ // update depth state
11751+ {
11752+ const sg_depth_state* state_ds = &pip->gl.depth;
11753+ sg_depth_state* cache_ds = &_sg.gl.cache.depth;
11754+ if (state_ds->compare != cache_ds->compare) {
11755+ cache_ds->compare = state_ds->compare;
11756+ glDepthFunc(_sg_gl_compare_func(state_ds->compare));
11757+ _sg_stats_inc(gl.num_render_state);
11758+ }
11759+ if (state_ds->write_enabled != cache_ds->write_enabled) {
11760+ cache_ds->write_enabled = state_ds->write_enabled;
11761+ glDepthMask(state_ds->write_enabled);
11762+ _sg_stats_inc(gl.num_render_state);
11763+ }
11764+ if (!_sg_fequal(state_ds->bias, cache_ds->bias, 0.000001f) ||
11765+ !_sg_fequal(state_ds->bias_slope_scale, cache_ds->bias_slope_scale, 0.000001f))
11766+ {
11767+ /* according to ANGLE's D3D11 backend:
11768+ D3D11 SlopeScaledDepthBias ==> GL polygonOffsetFactor
11769+ D3D11 DepthBias ==> GL polygonOffsetUnits
11770+ DepthBiasClamp has no meaning on GL
11771+ */
11772+ cache_ds->bias = state_ds->bias;
11773+ cache_ds->bias_slope_scale = state_ds->bias_slope_scale;
11774+ glPolygonOffset(state_ds->bias_slope_scale, state_ds->bias);
11775+ _sg_stats_inc(gl.num_render_state);
11776+ bool po_enabled = true;
11777+ if (_sg_fequal(state_ds->bias, 0.0f, 0.000001f) &&
11778+ _sg_fequal(state_ds->bias_slope_scale, 0.0f, 0.000001f))
11779+ {
11780+ po_enabled = false;
11781+ }
11782+ if (po_enabled != _sg.gl.cache.polygon_offset_enabled) {
11783+ _sg.gl.cache.polygon_offset_enabled = po_enabled;
11784+ if (po_enabled) {
11785+ glEnable(GL_POLYGON_OFFSET_FILL);
11786+ } else {
11787+ glDisable(GL_POLYGON_OFFSET_FILL);
11788+ }
11789+ _sg_stats_inc(gl.num_render_state);
11790+ }
11791+ }
11792+ }
11793+
11794+ // update stencil state
11795+ {
11796+ const sg_stencil_state* state_ss = &pip->gl.stencil;
11797+ sg_stencil_state* cache_ss = &_sg.gl.cache.stencil;
11798+ if (state_ss->enabled != cache_ss->enabled) {
11799+ cache_ss->enabled = state_ss->enabled;
11800+ if (state_ss->enabled) {
11801+ glEnable(GL_STENCIL_TEST);
11802+ } else {
11803+ glDisable(GL_STENCIL_TEST);
11804+ }
11805+ _sg_stats_inc(gl.num_render_state);
11806+ }
11807+ if (state_ss->write_mask != cache_ss->write_mask) {
11808+ cache_ss->write_mask = state_ss->write_mask;
11809+ glStencilMask(state_ss->write_mask);
11810+ _sg_stats_inc(gl.num_render_state);
11811+ }
11812+ for (int i = 0; i < 2; i++) {
11813+ const sg_stencil_face_state* state_sfs = (i==0)? &state_ss->front : &state_ss->back;
11814+ sg_stencil_face_state* cache_sfs = (i==0)? &cache_ss->front : &cache_ss->back;
11815+ GLenum gl_face = (i==0)? GL_FRONT : GL_BACK;
11816+ if ((state_sfs->compare != cache_sfs->compare) ||
11817+ (state_ss->read_mask != cache_ss->read_mask) ||
11818+ (state_ss->ref != cache_ss->ref))
11819+ {
11820+ cache_sfs->compare = state_sfs->compare;
11821+ glStencilFuncSeparate(gl_face,
11822+ _sg_gl_compare_func(state_sfs->compare),
11823+ state_ss->ref,
11824+ state_ss->read_mask);
11825+ _sg_stats_inc(gl.num_render_state);
11826+ }
11827+ if ((state_sfs->fail_op != cache_sfs->fail_op) ||
11828+ (state_sfs->depth_fail_op != cache_sfs->depth_fail_op) ||
11829+ (state_sfs->pass_op != cache_sfs->pass_op))
11830+ {
11831+ cache_sfs->fail_op = state_sfs->fail_op;
11832+ cache_sfs->depth_fail_op = state_sfs->depth_fail_op;
11833+ cache_sfs->pass_op = state_sfs->pass_op;
11834+ glStencilOpSeparate(gl_face,
11835+ _sg_gl_stencil_op(state_sfs->fail_op),
11836+ _sg_gl_stencil_op(state_sfs->depth_fail_op),
11837+ _sg_gl_stencil_op(state_sfs->pass_op));
11838+ _sg_stats_inc(gl.num_render_state);
11839+ }
11840+ }
11841+ cache_ss->read_mask = state_ss->read_mask;
11842+ cache_ss->ref = state_ss->ref;
11843+ }
11844+
11845+ if (pip->cmn.color_count > 0) {
11846+ // update blend state
11847+ // FIXME: separate blend state per color attachment
11848+ const sg_blend_state* state_bs = &pip->gl.blend;
11849+ sg_blend_state* cache_bs = &_sg.gl.cache.blend;
11850+ if (state_bs->enabled != cache_bs->enabled) {
11851+ cache_bs->enabled = state_bs->enabled;
11852+ if (state_bs->enabled) {
11853+ glEnable(GL_BLEND);
11854+ } else {
11855+ glDisable(GL_BLEND);
11856+ }
11857+ _sg_stats_inc(gl.num_render_state);
11858+ }
11859+ if ((state_bs->src_factor_rgb != cache_bs->src_factor_rgb) ||
11860+ (state_bs->dst_factor_rgb != cache_bs->dst_factor_rgb) ||
11861+ (state_bs->src_factor_alpha != cache_bs->src_factor_alpha) ||
11862+ (state_bs->dst_factor_alpha != cache_bs->dst_factor_alpha))
11863+ {
11864+ cache_bs->src_factor_rgb = state_bs->src_factor_rgb;
11865+ cache_bs->dst_factor_rgb = state_bs->dst_factor_rgb;
11866+ cache_bs->src_factor_alpha = state_bs->src_factor_alpha;
11867+ cache_bs->dst_factor_alpha = state_bs->dst_factor_alpha;
11868+ glBlendFuncSeparate(_sg_gl_blend_factor(state_bs->src_factor_rgb),
11869+ _sg_gl_blend_factor(state_bs->dst_factor_rgb),
11870+ _sg_gl_blend_factor(state_bs->src_factor_alpha),
11871+ _sg_gl_blend_factor(state_bs->dst_factor_alpha));
11872+ _sg_stats_inc(gl.num_render_state);
11873+ }
11874+ if ((state_bs->op_rgb != cache_bs->op_rgb) || (state_bs->op_alpha != cache_bs->op_alpha)) {
11875+ cache_bs->op_rgb = state_bs->op_rgb;
11876+ cache_bs->op_alpha = state_bs->op_alpha;
11877+ glBlendEquationSeparate(_sg_gl_blend_op(state_bs->op_rgb), _sg_gl_blend_op(state_bs->op_alpha));
11878+ _sg_stats_inc(gl.num_render_state);
11879+ }
11880+
11881+ // standalone color target state
11882+ for (GLuint i = 0; i < (GLuint)pip->cmn.color_count; i++) {
11883+ if (pip->gl.color_write_mask[i] != _sg.gl.cache.color_write_mask[i]) {
11884+ const sg_color_mask cm = pip->gl.color_write_mask[i];
11885+ _sg.gl.cache.color_write_mask[i] = cm;
11886+ if (_sg.features.mrt_independent_write_mask) {
11887+ #if defined(_SOKOL_GL_HAS_COLORMASKI)
11888+ glColorMaski(i,
11889+ (cm & SG_COLORMASK_R) != 0,
11890+ (cm & SG_COLORMASK_G) != 0,
11891+ (cm & SG_COLORMASK_B) != 0,
11892+ (cm & SG_COLORMASK_A) != 0);
11893+ #else
11894+ // can't happen
11895+ SOKOL_ASSERT(false);
11896+ #endif
11897+ } else if (0 == i) {
11898+ glColorMask((cm & SG_COLORMASK_R) != 0,
11899+ (cm & SG_COLORMASK_G) != 0,
11900+ (cm & SG_COLORMASK_B) != 0,
11901+ (cm & SG_COLORMASK_A) != 0);
11902+ }
11903+ _sg_stats_inc(gl.num_render_state);
11904+ }
11905+ }
11906+
11907+ if (!_sg_fequal(pip->cmn.blend_color.r, _sg.gl.cache.blend_color.r, 0.0001f) ||
11908+ !_sg_fequal(pip->cmn.blend_color.g, _sg.gl.cache.blend_color.g, 0.0001f) ||
11909+ !_sg_fequal(pip->cmn.blend_color.b, _sg.gl.cache.blend_color.b, 0.0001f) ||
11910+ !_sg_fequal(pip->cmn.blend_color.a, _sg.gl.cache.blend_color.a, 0.0001f))
11911+ {
11912+ sg_color c = pip->cmn.blend_color;
11913+ _sg.gl.cache.blend_color = c;
11914+ glBlendColor(c.r, c.g, c.b, c.a);
11915+ _sg_stats_inc(gl.num_render_state);
11916+ }
11917+ } // pip->cmn.color_count > 0
11918+
11919+ if (pip->gl.cull_mode != _sg.gl.cache.cull_mode) {
11920+ _sg.gl.cache.cull_mode = pip->gl.cull_mode;
11921+ if (SG_CULLMODE_NONE == pip->gl.cull_mode) {
11922+ glDisable(GL_CULL_FACE);
11923+ _sg_stats_inc(gl.num_render_state);
11924+ } else {
11925+ glEnable(GL_CULL_FACE);
11926+ GLenum gl_mode = (SG_CULLMODE_FRONT == pip->gl.cull_mode) ? GL_FRONT : GL_BACK;
11927+ glCullFace(gl_mode);
11928+ _sg_stats_add(gl.num_render_state, 2);
11929+ }
11930+ }
11931+ if (pip->gl.face_winding != _sg.gl.cache.face_winding) {
11932+ _sg.gl.cache.face_winding = pip->gl.face_winding;
11933+ GLenum gl_winding = (SG_FACEWINDING_CW == pip->gl.face_winding) ? GL_CW : GL_CCW;
11934+ glFrontFace(gl_winding);
11935+ _sg_stats_inc(gl.num_render_state);
11936+ }
11937+ if (pip->gl.alpha_to_coverage_enabled != _sg.gl.cache.alpha_to_coverage_enabled) {
11938+ _sg.gl.cache.alpha_to_coverage_enabled = pip->gl.alpha_to_coverage_enabled;
11939+ if (pip->gl.alpha_to_coverage_enabled) {
11940+ glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
11941+ } else {
11942+ glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
11943+ }
11944+ _sg_stats_inc(gl.num_render_state);
11945+ }
11946+ #ifdef SOKOL_GLCORE
11947+ if (pip->gl.sample_count != _sg.gl.cache.sample_count) {
11948+ _sg.gl.cache.sample_count = pip->gl.sample_count;
11949+ if (pip->gl.sample_count > 1) {
11950+ glEnable(GL_MULTISAMPLE);
11951+ } else {
11952+ glDisable(GL_MULTISAMPLE);
11953+ }
11954+ _sg_stats_inc(gl.num_render_state);
11955+ }
11956+ #endif
11957+}
11958+
11959+_SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) {
11960+ SOKOL_ASSERT(pip);
11961+ _SG_GL_CHECK_ERROR();
11962+ if (!_sg_sref_slot_eql(&_sg.gl.cache.cur_pip, &pip->slot)) {
11963+ _sg.gl.cache.cur_pip = _sg_sref(&pip->slot);
11964+
11965+ // bind shader program
11966+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
11967+ if (shd->gl.prog != _sg.gl.cache.prog) {
11968+ _sg.gl.cache.prog = shd->gl.prog;
11969+ glUseProgram(shd->gl.prog);
11970+ _sg_stats_inc(gl.num_use_program);
11971+ }
11972+
11973+ if (!pip->cmn.is_compute) {
11974+ _sg_gl_apply_render_pipeline_state(pip);
11975+ }
11976+ }
11977+ _SG_GL_CHECK_ERROR();
11978+}
11979+
11980+_SOKOL_PRIVATE bool _sg_gl_apply_bindings(_sg_bindings_ptrs_t* bnd) {
11981+ SOKOL_ASSERT(bnd);
11982+ SOKOL_ASSERT(bnd->pip);
11983+ _SG_GL_CHECK_ERROR();
11984+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader);
11985+
11986+ // bind combined texture-samplers
11987+ _SG_GL_CHECK_ERROR();
11988+ for (size_t tex_smp_index = 0; tex_smp_index < SG_MAX_TEXTURE_SAMPLER_PAIRS; tex_smp_index++) {
11989+ const _sg_shader_texture_sampler_t* tex_smp = &shd->cmn.texture_samplers[tex_smp_index];
11990+ if (tex_smp->stage == SG_SHADERSTAGE_NONE) {
11991+ continue;
11992+ }
11993+ const int8_t gl_tex_slot = (GLint)shd->gl.tex_slot[tex_smp_index];
11994+ if (gl_tex_slot != -1) {
11995+ SOKOL_ASSERT(tex_smp->view_slot < SG_MAX_VIEW_BINDSLOTS);
11996+ SOKOL_ASSERT(tex_smp->sampler_slot < SG_MAX_SAMPLER_BINDSLOTS);
11997+ const _sg_view_t* view = bnd->views[tex_smp->view_slot];
11998+ const _sg_sampler_t* smp = bnd->smps[tex_smp->sampler_slot];
11999+ SOKOL_ASSERT(view);
12000+ SOKOL_ASSERT(smp);
12001+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
12002+ const GLenum gl_tgt = img->gl.target;
12003+ const GLuint gl_smp = smp->gl.smp;
12004+ GLuint gl_tex;
12005+ if (_sg.features.gl_texture_views) {
12006+ gl_tex = view->gl.tex_view[img->cmn.active_slot];
12007+ } else {
12008+ gl_tex = img->gl.tex[img->cmn.active_slot];
12009+ }
12010+ _sg_gl_cache_bind_texture_sampler(gl_tex_slot, gl_tgt, gl_tex, gl_smp);
12011+ }
12012+ }
12013+ _SG_GL_CHECK_ERROR();
12014+
12015+ // bind storage buffer and images
12016+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
12017+ if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) {
12018+ continue;
12019+ }
12020+ const _sg_view_t* view = bnd->views[i];
12021+ if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) {
12022+ const _sg_buffer_t* sbuf = _sg_buffer_ref_ptr(&view->cmn.buf.ref);
12023+ const uint8_t gl_binding = shd->gl.sbuf_binding[i];
12024+ GLuint gl_sbuf = sbuf->gl.buf[sbuf->cmn.active_slot];
12025+ _sg_gl_cache_bind_storage_buffer(gl_binding, gl_sbuf, view->cmn.buf.offset, sbuf->cmn.size);
12026+ } else if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) {
12027+ #if defined(_SOKOL_GL_HAS_COMPUTE)
12028+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
12029+ const uint8_t gl_unit = shd->gl.simg_binding[i];
12030+ SOKOL_ASSERT((int)gl_unit < _sg.limits.max_storage_image_bindings_per_stage);
12031+ GLuint gl_tex = img->gl.tex[img->cmn.active_slot];
12032+ GLint level = (GLint)view->cmn.img.mip_level;
12033+ GLint layer = (GLint)view->cmn.img.slice;
12034+ // NOTE: when picking a specific layer, the 'layered' flag must be false,
12035+ // this was previously bugged
12036+ GLboolean layered = GL_FALSE;
12037+ GLenum access = shd->cmn.views[i].simg_writeonly ? GL_WRITE_ONLY : GL_READ_WRITE;
12038+ GLenum format = _sg_gl_teximage_internal_format(shd->cmn.views[i].access_format);
12039+ // NOTE: we specifically don't go through the GL cache since storage images
12040+ // are not supported on WebGL2, and on native platforms call caching isn't
12041+ // worth the hassle
12042+ glBindImageTexture(gl_unit, gl_tex, level, layered, layer, access, format);
12043+ _sg_stats_inc(gl.num_bind_image_texture);
12044+ #endif
12045+ }
12046+ }
12047+ _SG_GL_CHECK_ERROR();
12048+
12049+ if (!bnd->pip->cmn.is_compute) {
12050+ // index buffer (can be 0)
12051+ const GLuint gl_ib = bnd->ib ? bnd->ib->gl.buf[bnd->ib->cmn.active_slot] : 0;
12052+ _sg_gl_cache_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib);
12053+ _sg.gl.cache.cur_ib_offset = bnd->ib_offset;
12054+
12055+ // vertex attributes
12056+ for (GLuint attr_index = 0; attr_index < (GLuint)_sg.limits.max_vertex_attrs; attr_index++) {
12057+ _sg_gl_attr_t* attr = &bnd->pip->gl.attrs[attr_index];
12058+ _sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index];
12059+ bool cache_attr_dirty = false;
12060+ int vb_offset = 0;
12061+ GLuint gl_vb = 0;
12062+ if (attr->vb_index >= 0) {
12063+ // attribute is enabled
12064+ SOKOL_ASSERT(attr->vb_index < SG_MAX_VERTEXBUFFER_BINDSLOTS);
12065+ _sg_buffer_t* vb = bnd->vbs[attr->vb_index];
12066+ SOKOL_ASSERT(vb);
12067+ gl_vb = vb->gl.buf[vb->cmn.active_slot];
12068+ vb_offset = bnd->vb_offsets[attr->vb_index] + attr->offset;
12069+ if ((gl_vb != cache_attr->gl_vbuf) ||
12070+ (attr->size != cache_attr->gl_attr.size) ||
12071+ (attr->type != cache_attr->gl_attr.type) ||
12072+ (attr->normalized != cache_attr->gl_attr.normalized) ||
12073+ (attr->base_type != cache_attr->gl_attr.base_type) ||
12074+ (attr->stride != cache_attr->gl_attr.stride) ||
12075+ (vb_offset != cache_attr->gl_attr.offset) ||
12076+ (cache_attr->gl_attr.divisor != attr->divisor))
12077+ {
12078+ _sg_gl_cache_bind_buffer(GL_ARRAY_BUFFER, gl_vb);
12079+ if (attr->base_type == SG_SHADERATTRBASETYPE_FLOAT) {
12080+ glVertexAttribPointer(attr_index, attr->size, attr->type, attr->normalized, attr->stride, (const GLvoid*)(GLintptr)vb_offset);
12081+ } else {
12082+ glVertexAttribIPointer(attr_index, attr->size, attr->type, attr->stride, (const GLvoid*)(GLintptr)vb_offset);
12083+ }
12084+ _sg_stats_inc(gl.num_vertex_attrib_pointer);
12085+ glVertexAttribDivisor(attr_index, (GLuint)attr->divisor);
12086+ _sg_stats_inc(gl.num_vertex_attrib_divisor);
12087+ cache_attr_dirty = true;
12088+ }
12089+ if (cache_attr->gl_attr.vb_index == -1) {
12090+ glEnableVertexAttribArray(attr_index);
12091+ _sg_stats_inc(gl.num_enable_vertex_attrib_array);
12092+ cache_attr_dirty = true;
12093+ }
12094+ } else {
12095+ // attribute is disabled
12096+ if (cache_attr->gl_attr.vb_index != -1) {
12097+ glDisableVertexAttribArray(attr_index);
12098+ _sg_stats_inc(gl.num_disable_vertex_attrib_array);
12099+ cache_attr_dirty = true;
12100+ }
12101+ }
12102+ if (cache_attr_dirty) {
12103+ cache_attr->gl_attr = *attr;
12104+ cache_attr->gl_attr.offset = vb_offset;
12105+ cache_attr->gl_vbuf = gl_vb;
12106+ }
12107+ }
12108+ _SG_GL_CHECK_ERROR();
12109+ }
12110+
12111+ // take care of storage resource memory barriers (this needs to happen after the bindings are set)
12112+ #if defined(_SOKOL_GL_HAS_COMPUTE)
12113+ _sg_gl_handle_memory_barriers(shd, bnd, 0);
12114+ _SG_GL_CHECK_ERROR();
12115+ #endif
12116+
12117+ return true;
12118+}
12119+
12120+_SOKOL_PRIVATE void _sg_gl_apply_uniforms(int ub_slot, const sg_range* data) {
12121+ SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS));
12122+ const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip);
12123+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
12124+ SOKOL_ASSERT(SG_SHADERSTAGE_NONE != shd->cmn.uniform_blocks[ub_slot].stage);
12125+ SOKOL_ASSERT(data->size == shd->cmn.uniform_blocks[ub_slot].size);
12126+ const _sg_gl_uniform_block_t* gl_ub = &shd->gl.uniform_blocks[ub_slot];
12127+ for (int u_index = 0; u_index < gl_ub->num_uniforms; u_index++) {
12128+ const _sg_gl_uniform_t* u = &gl_ub->uniforms[u_index];
12129+ SOKOL_ASSERT(u->type != SG_UNIFORMTYPE_INVALID);
12130+ if (u->gl_loc == -1) {
12131+ continue;
12132+ }
12133+ _sg_stats_inc(gl.num_uniform);
12134+ GLfloat* fptr = (GLfloat*) (((uint8_t*)data->ptr) + u->offset);
12135+ GLint* iptr = (GLint*) (((uint8_t*)data->ptr) + u->offset);
12136+ switch (u->type) {
12137+ case SG_UNIFORMTYPE_INVALID:
12138+ break;
12139+ case SG_UNIFORMTYPE_FLOAT:
12140+ glUniform1fv(u->gl_loc, u->count, fptr);
12141+ break;
12142+ case SG_UNIFORMTYPE_FLOAT2:
12143+ glUniform2fv(u->gl_loc, u->count, fptr);
12144+ break;
12145+ case SG_UNIFORMTYPE_FLOAT3:
12146+ glUniform3fv(u->gl_loc, u->count, fptr);
12147+ break;
12148+ case SG_UNIFORMTYPE_FLOAT4:
12149+ glUniform4fv(u->gl_loc, u->count, fptr);
12150+ break;
12151+ case SG_UNIFORMTYPE_INT:
12152+ glUniform1iv(u->gl_loc, u->count, iptr);
12153+ break;
12154+ case SG_UNIFORMTYPE_INT2:
12155+ glUniform2iv(u->gl_loc, u->count, iptr);
12156+ break;
12157+ case SG_UNIFORMTYPE_INT3:
12158+ glUniform3iv(u->gl_loc, u->count, iptr);
12159+ break;
12160+ case SG_UNIFORMTYPE_INT4:
12161+ glUniform4iv(u->gl_loc, u->count, iptr);
12162+ break;
12163+ case SG_UNIFORMTYPE_MAT4:
12164+ glUniformMatrix4fv(u->gl_loc, u->count, GL_FALSE, fptr);
12165+ break;
12166+ default:
12167+ SOKOL_UNREACHABLE;
12168+ break;
12169+ }
12170+ }
12171+}
12172+
12173+_SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) {
12174+ const GLenum p_type = _sg.gl.cache.cur_primitive_type;
12175+ const bool use_instanced_draw = (num_instances > 1) || _sg.use_instanced_draw;
12176+ if (_sg.use_indexed_draw) {
12177+ // indexed rendering
12178+ const GLenum i_type = _sg.gl.cache.cur_index_type;
12179+ const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4;
12180+ const int ib_offset = _sg.gl.cache.cur_ib_offset;
12181+ const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset);
12182+ if (use_instanced_draw) {
12183+ if ((base_vertex == 0) && (base_instance == 0)) {
12184+ glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances);
12185+ } else if ((base_vertex != 0) && (base_instance == 0) && _sg.features.draw_base_vertex) {
12186+ #if defined(_SOKOL_GL_HAS_BASEVERTEX)
12187+ glDrawElementsInstancedBaseVertex(p_type, num_elements, i_type, indices, num_instances, base_vertex);
12188+ #endif
12189+ } else if ((base_instance != 0) && _sg.features.draw_base_instance) {
12190+ #if defined(_SOKOL_GL_HAS_BASEINSTANCE)
12191+ glDrawElementsInstancedBaseVertexBaseInstance(p_type, num_elements, i_type, indices, num_instances, base_vertex, (GLuint)base_instance);
12192+ #endif
12193+ }
12194+ } else {
12195+ if (base_vertex == 0) {
12196+ glDrawElements(p_type, num_elements, i_type, indices);
12197+ } else if (_sg.features.draw_base_vertex) {
12198+ #if defined(_SOKOL_GL_HAS_BASEVERTEX)
12199+ glDrawElementsBaseVertex(p_type, num_elements, i_type, indices, base_vertex);
12200+ #endif
12201+ }
12202+ }
12203+ } else {
12204+ // non-indexed rendering
12205+ if (use_instanced_draw) {
12206+ if (base_instance == 0) {
12207+ glDrawArraysInstanced(p_type, base_element, num_elements, num_instances);
12208+ } else if (_sg.features.draw_base_instance) {
12209+ #if defined(_SOKOL_GL_HAS_BASEINSTANCE)
12210+ glDrawArraysInstancedBaseInstance(p_type, base_element, num_elements, num_instances, (GLuint)base_instance);
12211+ #endif
12212+ }
12213+ } else {
12214+ glDrawArrays(p_type, base_element, num_elements);
12215+ }
12216+ }
12217+}
12218+
12219+_SOKOL_PRIVATE void _sg_gl_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) {
12220+ #if defined(_SOKOL_GL_HAS_COMPUTE)
12221+ if (!_sg.features.compute) {
12222+ return;
12223+ }
12224+ glDispatchCompute((GLuint)num_groups_x, (GLuint)num_groups_y, (GLuint)num_groups_z);
12225+ #else
12226+ (void)num_groups_x; (void)num_groups_y; (void)num_groups_z;
12227+ #endif
12228+}
12229+
12230+_SOKOL_PRIVATE void _sg_gl_commit(void) {
12231+ // "soft" clear bindings (only those that are actually bound)
12232+ _sg_gl_cache_clear_buffer_bindings(false);
12233+ _sg_gl_cache_clear_texture_sampler_bindings(false);
12234+}
12235+
12236+_SOKOL_PRIVATE void _sg_gl_update_buffer(_sg_buffer_t* buf, const sg_range* data) {
12237+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
12238+ // only one update per buffer per frame allowed
12239+ if (++buf->cmn.active_slot >= buf->cmn.num_slots) {
12240+ buf->cmn.active_slot = 0;
12241+ }
12242+ GLenum gl_tgt = _sg_gl_buffer_target(&buf->cmn.usage);
12243+ SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES);
12244+ GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot];
12245+ SOKOL_ASSERT(gl_buf);
12246+ _SG_GL_CHECK_ERROR();
12247+ _sg_gl_cache_store_buffer_binding(gl_tgt);
12248+ _sg_gl_cache_bind_buffer(gl_tgt, gl_buf);
12249+ glBufferSubData(gl_tgt, 0, (GLsizeiptr)data->size, data->ptr);
12250+ _sg_gl_cache_restore_buffer_binding(gl_tgt);
12251+ _SG_GL_CHECK_ERROR();
12252+}
12253+
12254+_SOKOL_PRIVATE void _sg_gl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) {
12255+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
12256+ if (new_frame) {
12257+ if (++buf->cmn.active_slot >= buf->cmn.num_slots) {
12258+ buf->cmn.active_slot = 0;
12259+ }
12260+ }
12261+ GLenum gl_tgt = _sg_gl_buffer_target(&buf->cmn.usage);
12262+ SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES);
12263+ GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot];
12264+ SOKOL_ASSERT(gl_buf);
12265+ _SG_GL_CHECK_ERROR();
12266+ _sg_gl_cache_store_buffer_binding(gl_tgt);
12267+ _sg_gl_cache_bind_buffer(gl_tgt, gl_buf);
12268+ glBufferSubData(gl_tgt, buf->cmn.append_pos, (GLsizeiptr)data->size, data->ptr);
12269+ _sg_gl_cache_restore_buffer_binding(gl_tgt);
12270+ _SG_GL_CHECK_ERROR();
12271+}
12272+
12273+_SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* data) {
12274+ SOKOL_ASSERT(img && data);
12275+ // only one update per image per frame allowed
12276+ if (++img->cmn.active_slot >= img->cmn.num_slots) {
12277+ img->cmn.active_slot = 0;
12278+ }
12279+ SOKOL_ASSERT(img->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES);
12280+ SOKOL_ASSERT(0 != img->gl.tex[img->cmn.active_slot]);
12281+ _sg_gl_cache_store_texture_sampler_binding(0);
12282+ _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[img->cmn.active_slot], 0);
12283+ const int num_mips = img->cmn.num_mipmaps;
12284+ for (int mip_index = 0; mip_index < num_mips; mip_index++) {
12285+ const GLvoid* data_ptr = data->mip_levels[mip_index].ptr;
12286+ const GLsizei data_size = (GLsizei)data->mip_levels[mip_index].size;
12287+ const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index);
12288+ const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index);
12289+ const int mip_depth = (SG_IMAGETYPE_3D == img->cmn.type) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices;
12290+ if (SG_IMAGETYPE_CUBE == img->cmn.type) {
12291+ const int surf_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1);
12292+ SOKOL_ASSERT((6 * surf_pitch) <= data_size);
12293+ const uint8_t* surf_ptr = (const uint8_t*) data_ptr;
12294+ for (int i = 0; i < 6; i++) {
12295+ const GLenum gl_img_target = _sg_gl_cubeface_target(i);
12296+ _sg_gl_texsubimage(img, gl_img_target, mip_index, mip_width, mip_height, mip_depth, surf_ptr, surf_pitch);
12297+ surf_ptr += surf_pitch;
12298+ }
12299+ } else {
12300+ _sg_gl_texsubimage(img, img->gl.target, mip_index, mip_width, mip_height, mip_depth, data_ptr, data_size);
12301+ }
12302+ }
12303+ _sg_gl_cache_restore_texture_sampler_binding(0);
12304+}
12305+
12306+// ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████
12307+// ██ ██ ██ ██ ██ ███ ███ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██
12308+// ██ ██ █████ ██ ██ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██
12309+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
12310+// ██████ ██████ ██████ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████
12311+//
12312+// >>d3d11 backend
12313+#elif defined(SOKOL_D3D11)
12314+
12315+#if defined(__cplusplus)
12316+#define _sg_d3d11_AddRef(self) (self)->AddRef()
12317+#else
12318+#define _sg_d3d11_AddRef(self) (self)->lpVtbl->AddRef(self)
12319+#endif
12320+
12321+#if defined(__cplusplus)
12322+#define _sg_d3d11_Release(self) (self)->Release()
12323+#else
12324+#define _sg_d3d11_Release(self) (self)->lpVtbl->Release(self)
12325+#endif
12326+
12327+// NOTE: This needs to be a macro since we can't use the polymorphism in C. It's called on many kinds of resources.
12328+// NOTE: Based on microsoft docs, it's fine to call this with pData=NULL if DataSize is also zero.
12329+#if defined(__cplusplus)
12330+#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->SetPrivateData(guid, DataSize, pData)
12331+#else
12332+#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->lpVtbl->SetPrivateData(self, guid, DataSize, pData)
12333+#endif
12334+
12335+#if defined(__cplusplus)
12336+#define _sg_win32_refguid(guid) guid
12337+#else
12338+#define _sg_win32_refguid(guid) &guid
12339+#endif
12340+
12341+static const GUID _sg_d3d11_WKPDID_D3DDebugObjectName = { 0x429b8c22,0x9188,0x4b0c, {0x87,0x42,0xac,0xb0,0xbf,0x85,0xc2,0x00} };
12342+
12343+#if defined(SOKOL_DEBUG)
12344+#define _sg_d3d11_setlabel(self, label) _sg_d3d11_SetPrivateData(self, _sg_win32_refguid(_sg_d3d11_WKPDID_D3DDebugObjectName), label ? (UINT)strlen(label) : 0, label)
12345+#else
12346+#define _sg_d3d11_setlabel(self, label)
12347+#endif
12348+
12349+
12350+//-- D3D11 C/C++ wrappers ------------------------------------------------------
12351+static inline HRESULT _sg_d3d11_CheckFormatSupport(ID3D11Device* self, DXGI_FORMAT Format, UINT* pFormatSupport) {
12352+ #if defined(__cplusplus)
12353+ return self->CheckFormatSupport(Format, pFormatSupport);
12354+ #else
12355+ return self->lpVtbl->CheckFormatSupport(self, Format, pFormatSupport);
12356+ #endif
12357+}
12358+
12359+static inline void _sg_d3d11_OMSetRenderTargets(ID3D11DeviceContext* self, UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView *pDepthStencilView) {
12360+ #if defined(__cplusplus)
12361+ self->OMSetRenderTargets(NumViews, ppRenderTargetViews, pDepthStencilView);
12362+ #else
12363+ self->lpVtbl->OMSetRenderTargets(self, NumViews, ppRenderTargetViews, pDepthStencilView);
12364+ #endif
12365+}
12366+
12367+static inline void _sg_d3d11_RSSetState(ID3D11DeviceContext* self, ID3D11RasterizerState* pRasterizerState) {
12368+ #if defined(__cplusplus)
12369+ self->RSSetState(pRasterizerState);
12370+ #else
12371+ self->lpVtbl->RSSetState(self, pRasterizerState);
12372+ #endif
12373+}
12374+
12375+static inline void _sg_d3d11_OMSetDepthStencilState(ID3D11DeviceContext* self, ID3D11DepthStencilState* pDepthStencilState, UINT StencilRef) {
12376+ #if defined(__cplusplus)
12377+ self->OMSetDepthStencilState(pDepthStencilState, StencilRef);
12378+ #else
12379+ self->lpVtbl->OMSetDepthStencilState(self, pDepthStencilState, StencilRef);
12380+ #endif
12381+}
12382+
12383+static inline void _sg_d3d11_OMSetBlendState(ID3D11DeviceContext* self, ID3D11BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask) {
12384+ #if defined(__cplusplus)
12385+ self->OMSetBlendState(pBlendState, BlendFactor, SampleMask);
12386+ #else
12387+ self->lpVtbl->OMSetBlendState(self, pBlendState, BlendFactor, SampleMask);
12388+ #endif
12389+}
12390+
12391+static inline void _sg_d3d11_IASetVertexBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppVertexBuffers, const UINT* pStrides, const UINT* pOffsets) {
12392+ #if defined(__cplusplus)
12393+ self->IASetVertexBuffers(StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets);
12394+ #else
12395+ self->lpVtbl->IASetVertexBuffers(self, StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets);
12396+ #endif
12397+}
12398+
12399+static inline void _sg_d3d11_IASetIndexBuffer(ID3D11DeviceContext* self, ID3D11Buffer* pIndexBuffer, DXGI_FORMAT Format, UINT Offset) {
12400+ #if defined(__cplusplus)
12401+ self->IASetIndexBuffer(pIndexBuffer, Format, Offset);
12402+ #else
12403+ self->lpVtbl->IASetIndexBuffer(self, pIndexBuffer, Format, Offset);
12404+ #endif
12405+}
12406+
12407+static inline void _sg_d3d11_IASetInputLayout(ID3D11DeviceContext* self, ID3D11InputLayout* pInputLayout) {
12408+ #if defined(__cplusplus)
12409+ self->IASetInputLayout(pInputLayout);
12410+ #else
12411+ self->lpVtbl->IASetInputLayout(self, pInputLayout);
12412+ #endif
12413+}
12414+
12415+static inline void _sg_d3d11_VSSetShader(ID3D11DeviceContext* self, ID3D11VertexShader* pVertexShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) {
12416+ #if defined(__cplusplus)
12417+ self->VSSetShader(pVertexShader, ppClassInstances, NumClassInstances);
12418+ #else
12419+ self->lpVtbl->VSSetShader(self, pVertexShader, ppClassInstances, NumClassInstances);
12420+ #endif
12421+}
12422+
12423+static inline void _sg_d3d11_PSSetShader(ID3D11DeviceContext* self, ID3D11PixelShader* pPixelShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) {
12424+ #if defined(__cplusplus)
12425+ self->PSSetShader(pPixelShader, ppClassInstances, NumClassInstances);
12426+ #else
12427+ self->lpVtbl->PSSetShader(self, pPixelShader, ppClassInstances, NumClassInstances);
12428+ #endif
12429+}
12430+
12431+static inline void _sg_d3d11_CSSetShader(ID3D11DeviceContext* self, ID3D11ComputeShader* pComputeShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) {
12432+ #if defined(__cplusplus)
12433+ self->CSSetShader(pComputeShader, ppClassInstances, NumClassInstances);
12434+ #else
12435+ self->lpVtbl->CSSetShader(self, pComputeShader, ppClassInstances, NumClassInstances);
12436+ #endif
12437+}
12438+
12439+static inline void _sg_d3d11_VSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) {
12440+ #if defined(__cplusplus)
12441+ self->VSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers);
12442+ #else
12443+ self->lpVtbl->VSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers);
12444+ #endif
12445+}
12446+
12447+static inline void _sg_d3d11_PSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) {
12448+ #if defined(__cplusplus)
12449+ self->PSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers);
12450+ #else
12451+ self->lpVtbl->PSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers);
12452+ #endif
12453+}
12454+
12455+static inline void _sg_d3d11_CSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) {
12456+ #if defined(__cplusplus)
12457+ self->CSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers);
12458+ #else
12459+ self->lpVtbl->CSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers);
12460+ #endif
12461+}
12462+
12463+static inline void _sg_d3d11_VSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) {
12464+ #if defined(__cplusplus)
12465+ self->VSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews);
12466+ #else
12467+ self->lpVtbl->VSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews);
12468+ #endif
12469+}
12470+
12471+static inline void _sg_d3d11_PSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) {
12472+ #if defined(__cplusplus)
12473+ self->PSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews);
12474+ #else
12475+ self->lpVtbl->PSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews);
12476+ #endif
12477+}
12478+
12479+static inline void _sg_d3d11_CSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) {
12480+ #if defined(__cplusplus)
12481+ self->CSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews);
12482+ #else
12483+ self->lpVtbl->CSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews);
12484+ #endif
12485+}
12486+
12487+static inline void _sg_d3d11_VSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) {
12488+ #if defined(__cplusplus)
12489+ self->VSSetSamplers(StartSlot, NumSamplers, ppSamplers);
12490+ #else
12491+ self->lpVtbl->VSSetSamplers(self, StartSlot, NumSamplers, ppSamplers);
12492+ #endif
12493+}
12494+
12495+static inline void _sg_d3d11_PSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) {
12496+ #if defined(__cplusplus)
12497+ self->PSSetSamplers(StartSlot, NumSamplers, ppSamplers);
12498+ #else
12499+ self->lpVtbl->PSSetSamplers(self, StartSlot, NumSamplers, ppSamplers);
12500+ #endif
12501+}
12502+
12503+static inline void _sg_d3d11_CSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) {
12504+ #if defined(__cplusplus)
12505+ self->CSSetSamplers(StartSlot, NumSamplers, ppSamplers);
12506+ #else
12507+ self->lpVtbl->CSSetSamplers(self, StartSlot, NumSamplers, ppSamplers);
12508+ #endif
12509+}
12510+
12511+static inline void _sg_d3d11_CSSetUnorderedAccessViews(ID3D11DeviceContext* self, UINT StartSlot, UINT NumUAVs, ID3D11UnorderedAccessView* const* ppUnorderedAccessViews, const UINT* pUAVInitialCounts) {
12512+ #if defined(__cplusplus)
12513+ self->CSSetUnorderedAccessViews(StartSlot, NumUAVs, ppUnorderedAccessViews, pUAVInitialCounts);
12514+ #else
12515+ self->lpVtbl->CSSetUnorderedAccessViews(self, StartSlot, NumUAVs, ppUnorderedAccessViews, pUAVInitialCounts);
12516+ #endif
12517+}
12518+
12519+static inline HRESULT _sg_d3d11_CreateBuffer(ID3D11Device* self, const D3D11_BUFFER_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Buffer** ppBuffer) {
12520+ #if defined(__cplusplus)
12521+ return self->CreateBuffer(pDesc, pInitialData, ppBuffer);
12522+ #else
12523+ return self->lpVtbl->CreateBuffer(self, pDesc, pInitialData, ppBuffer);
12524+ #endif
12525+}
12526+
12527+static inline HRESULT _sg_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) {
12528+ #if defined(__cplusplus)
12529+ return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D);
12530+ #else
12531+ return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D);
12532+ #endif
12533+}
12534+
12535+static inline HRESULT _sg_d3d11_CreateShaderResourceView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRView) {
12536+ #if defined(__cplusplus)
12537+ return self->CreateShaderResourceView(pResource, pDesc, ppSRView);
12538+ #else
12539+ return self->lpVtbl->CreateShaderResourceView(self, pResource, pDesc, ppSRView);
12540+ #endif
12541+}
12542+
12543+static inline HRESULT _sg_d3d11_CreateUnorderedAccessView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, ID3D11UnorderedAccessView** ppUAVView) {
12544+ #if defined(__cplusplus)
12545+ return self->CreateUnorderedAccessView(pResource, pDesc, ppUAVView);
12546+ #else
12547+ return self->lpVtbl->CreateUnorderedAccessView(self, pResource, pDesc, ppUAVView);
12548+ #endif
12549+}
12550+
12551+static inline void _sg_d3d11_GetResource(ID3D11View* self, ID3D11Resource** ppResource) {
12552+ #if defined(__cplusplus)
12553+ self->GetResource(ppResource);
12554+ #else
12555+ self->lpVtbl->GetResource(self, ppResource);
12556+ #endif
12557+}
12558+
12559+static inline HRESULT _sg_d3d11_CreateTexture3D(ID3D11Device* self, const D3D11_TEXTURE3D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D** ppTexture3D) {
12560+ #if defined(__cplusplus)
12561+ return self->CreateTexture3D(pDesc, pInitialData, ppTexture3D);
12562+ #else
12563+ return self->lpVtbl->CreateTexture3D(self, pDesc, pInitialData, ppTexture3D);
12564+ #endif
12565+}
12566+
12567+static inline HRESULT _sg_d3d11_CreateSamplerState(ID3D11Device* self, const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState) {
12568+ #if defined(__cplusplus)
12569+ return self->CreateSamplerState(pSamplerDesc, ppSamplerState);
12570+ #else
12571+ return self->lpVtbl->CreateSamplerState(self, pSamplerDesc, ppSamplerState);
12572+ #endif
12573+}
12574+
12575+static inline LPVOID _sg_d3d11_GetBufferPointer(ID3D10Blob* self) {
12576+ #if defined(__cplusplus)
12577+ return self->GetBufferPointer();
12578+ #else
12579+ return self->lpVtbl->GetBufferPointer(self);
12580+ #endif
12581+}
12582+
12583+static inline SIZE_T _sg_d3d11_GetBufferSize(ID3D10Blob* self) {
12584+ #if defined(__cplusplus)
12585+ return self->GetBufferSize();
12586+ #else
12587+ return self->lpVtbl->GetBufferSize(self);
12588+ #endif
12589+}
12590+
12591+static inline HRESULT _sg_d3d11_CreateVertexShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11VertexShader** ppVertexShader) {
12592+ #if defined(__cplusplus)
12593+ return self->CreateVertexShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader);
12594+ #else
12595+ return self->lpVtbl->CreateVertexShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader);
12596+ #endif
12597+}
12598+
12599+static inline HRESULT _sg_d3d11_CreatePixelShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11PixelShader** ppPixelShader) {
12600+ #if defined(__cplusplus)
12601+ return self->CreatePixelShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader);
12602+ #else
12603+ return self->lpVtbl->CreatePixelShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader);
12604+ #endif
12605+}
12606+
12607+static inline HRESULT _sg_d3d11_CreateComputeShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11ComputeShader** ppComputeShader) {
12608+ #if defined(__cplusplus)
12609+ return self->CreateComputeShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppComputeShader);
12610+ #else
12611+ return self->lpVtbl->CreateComputeShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppComputeShader);
12612+ #endif
12613+}
12614+
12615+static inline HRESULT _sg_d3d11_CreateInputLayout(ID3D11Device* self, const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D11InputLayout **ppInputLayout) {
12616+ #if defined(__cplusplus)
12617+ return self->CreateInputLayout(pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout);
12618+ #else
12619+ return self->lpVtbl->CreateInputLayout(self, pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout);
12620+ #endif
12621+}
12622+
12623+static inline HRESULT _sg_d3d11_CreateRasterizerState(ID3D11Device* self, const D3D11_RASTERIZER_DESC* pRasterizerDesc, ID3D11RasterizerState** ppRasterizerState) {
12624+ #if defined(__cplusplus)
12625+ return self->CreateRasterizerState(pRasterizerDesc, ppRasterizerState);
12626+ #else
12627+ return self->lpVtbl->CreateRasterizerState(self, pRasterizerDesc, ppRasterizerState);
12628+ #endif
12629+}
12630+
12631+static inline HRESULT _sg_d3d11_CreateDepthStencilState(ID3D11Device* self, const D3D11_DEPTH_STENCIL_DESC* pDepthStencilDesc, ID3D11DepthStencilState** ppDepthStencilState) {
12632+ #if defined(__cplusplus)
12633+ return self->CreateDepthStencilState(pDepthStencilDesc, ppDepthStencilState);
12634+ #else
12635+ return self->lpVtbl->CreateDepthStencilState(self, pDepthStencilDesc, ppDepthStencilState);
12636+ #endif
12637+}
12638+
12639+static inline HRESULT _sg_d3d11_CreateBlendState(ID3D11Device* self, const D3D11_BLEND_DESC* pBlendStateDesc, ID3D11BlendState** ppBlendState) {
12640+ #if defined(__cplusplus)
12641+ return self->CreateBlendState(pBlendStateDesc, ppBlendState);
12642+ #else
12643+ return self->lpVtbl->CreateBlendState(self, pBlendStateDesc, ppBlendState);
12644+ #endif
12645+}
12646+
12647+static inline HRESULT _sg_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) {
12648+ #if defined(__cplusplus)
12649+ return self->CreateRenderTargetView(pResource, pDesc, ppRTView);
12650+ #else
12651+ return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView);
12652+ #endif
12653+}
12654+
12655+static inline HRESULT _sg_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) {
12656+ #if defined(__cplusplus)
12657+ return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView);
12658+ #else
12659+ return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView);
12660+ #endif
12661+}
12662+
12663+static inline void _sg_d3d11_RSSetViewports(ID3D11DeviceContext* self, UINT NumViewports, const D3D11_VIEWPORT* pViewports) {
12664+ #if defined(__cplusplus)
12665+ self->RSSetViewports(NumViewports, pViewports);
12666+ #else
12667+ self->lpVtbl->RSSetViewports(self, NumViewports, pViewports);
12668+ #endif
12669+}
12670+
12671+static inline void _sg_d3d11_RSSetScissorRects(ID3D11DeviceContext* self, UINT NumRects, const D3D11_RECT* pRects) {
12672+ #if defined(__cplusplus)
12673+ self->RSSetScissorRects(NumRects, pRects);
12674+ #else
12675+ self->lpVtbl->RSSetScissorRects(self, NumRects, pRects);
12676+ #endif
12677+}
12678+
12679+static inline void _sg_d3d11_ClearRenderTargetView(ID3D11DeviceContext* self, ID3D11RenderTargetView* pRenderTargetView, const FLOAT ColorRGBA[4]) {
12680+ #if defined(__cplusplus)
12681+ self->ClearRenderTargetView(pRenderTargetView, ColorRGBA);
12682+ #else
12683+ self->lpVtbl->ClearRenderTargetView(self, pRenderTargetView, ColorRGBA);
12684+ #endif
12685+}
12686+
12687+static inline void _sg_d3d11_ClearDepthStencilView(ID3D11DeviceContext* self, ID3D11DepthStencilView* pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil) {
12688+ #if defined(__cplusplus)
12689+ self->ClearDepthStencilView(pDepthStencilView, ClearFlags, Depth, Stencil);
12690+ #else
12691+ self->lpVtbl->ClearDepthStencilView(self, pDepthStencilView, ClearFlags, Depth, Stencil);
12692+ #endif
12693+}
12694+
12695+static inline void _sg_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) {
12696+ #if defined(__cplusplus)
12697+ self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format);
12698+ #else
12699+ self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format);
12700+ #endif
12701+}
12702+
12703+static inline void _sg_d3d11_IASetPrimitiveTopology(ID3D11DeviceContext* self, D3D11_PRIMITIVE_TOPOLOGY Topology) {
12704+ #if defined(__cplusplus)
12705+ self->IASetPrimitiveTopology(Topology);
12706+ #else
12707+ self->lpVtbl->IASetPrimitiveTopology(self, Topology);
12708+ #endif
12709+}
12710+
12711+static inline void _sg_d3d11_UpdateSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) {
12712+ #if defined(__cplusplus)
12713+ self->UpdateSubresource(pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch);
12714+ #else
12715+ self->lpVtbl->UpdateSubresource(self, pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch);
12716+ #endif
12717+}
12718+
12719+static inline void _sg_d3d11_DrawIndexed(ID3D11DeviceContext* self, UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation) {
12720+ #if defined(__cplusplus)
12721+ self->DrawIndexed(IndexCount, StartIndexLocation, BaseVertexLocation);
12722+ #else
12723+ self->lpVtbl->DrawIndexed(self, IndexCount, StartIndexLocation, BaseVertexLocation);
12724+ #endif
12725+}
12726+
12727+static inline void _sg_d3d11_DrawIndexedInstanced(ID3D11DeviceContext* self, UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation) {
12728+ #if defined(__cplusplus)
12729+ self->DrawIndexedInstanced(IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation);
12730+ #else
12731+ self->lpVtbl->DrawIndexedInstanced(self, IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation);
12732+ #endif
12733+}
12734+
12735+static inline void _sg_d3d11_Draw(ID3D11DeviceContext* self, UINT VertexCount, UINT StartVertexLocation) {
12736+ #if defined(__cplusplus)
12737+ self->Draw(VertexCount, StartVertexLocation);
12738+ #else
12739+ self->lpVtbl->Draw(self, VertexCount, StartVertexLocation);
12740+ #endif
12741+}
12742+
12743+static inline void _sg_d3d11_DrawInstanced(ID3D11DeviceContext* self, UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation) {
12744+ #if defined(__cplusplus)
12745+ self->DrawInstanced(VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation);
12746+ #else
12747+ self->lpVtbl->DrawInstanced(self, VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation);
12748+ #endif
12749+}
12750+
12751+static inline void _sg_d3d11_Dispatch(ID3D11DeviceContext* self, UINT ThreadGroupCountX, UINT ThreadGroupCountY, UINT ThreadGroupCountZ) {
12752+ #if defined(__cplusplus)
12753+ self->Dispatch(ThreadGroupCountX, ThreadGroupCountY, ThreadGroupCountZ);
12754+ #else
12755+ self->lpVtbl->Dispatch(self, ThreadGroupCountX, ThreadGroupCountY, ThreadGroupCountZ);
12756+ #endif
12757+}
12758+
12759+static inline HRESULT _sg_d3d11_Map(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) {
12760+ #if defined(__cplusplus)
12761+ return self->Map(pResource, Subresource, MapType, MapFlags, pMappedResource);
12762+ #else
12763+ return self->lpVtbl->Map(self, pResource, Subresource, MapType, MapFlags, pMappedResource);
12764+ #endif
12765+}
12766+
12767+static inline void _sg_d3d11_Unmap(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource) {
12768+ #if defined(__cplusplus)
12769+ self->Unmap(pResource, Subresource);
12770+ #else
12771+ self->lpVtbl->Unmap(self, pResource, Subresource);
12772+ #endif
12773+}
12774+
12775+static inline void _sg_d3d11_ClearState(ID3D11DeviceContext* self) {
12776+ #if defined(__cplusplus)
12777+ self->ClearState();
12778+ #else
12779+ self->lpVtbl->ClearState(self);
12780+ #endif
12781+}
12782+
12783+static inline D3D_FEATURE_LEVEL _sg_d3d11_GetFeatureLevel(ID3D11Device* self) {
12784+ #if defined(__cplusplus)
12785+ return self->GetFeatureLevel();
12786+ #else
12787+ return self->lpVtbl->GetFeatureLevel(self);
12788+ #endif
12789+}
12790+
12791+//-- enum translation functions ------------------------------------------------
12792+_SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_image_usage(const sg_image_usage* usg) {
12793+ if (usg->immutable) {
12794+ if (usg->color_attachment ||
12795+ usg->resolve_attachment ||
12796+ usg->depth_stencil_attachment ||
12797+ usg->storage_image)
12798+ {
12799+ return D3D11_USAGE_DEFAULT;
12800+ } else {
12801+ return D3D11_USAGE_IMMUTABLE;
12802+ }
12803+ } else {
12804+ return D3D11_USAGE_DYNAMIC;
12805+ }
12806+}
12807+
12808+_SOKOL_PRIVATE UINT _sg_d3d11_image_bind_flags(const sg_image_usage* usg) {
12809+ UINT res = D3D11_BIND_SHADER_RESOURCE;
12810+ if (usg->color_attachment) {
12811+ res |= D3D11_BIND_RENDER_TARGET;
12812+ }
12813+ if (usg->depth_stencil_attachment) {
12814+ res |= D3D11_BIND_DEPTH_STENCIL;
12815+ }
12816+ if (usg->storage_image) {
12817+ res |= D3D11_BIND_UNORDERED_ACCESS;
12818+ }
12819+ return res;
12820+}
12821+
12822+_SOKOL_PRIVATE UINT _sg_d3d11_image_cpu_access_flags(const sg_image_usage* usg) {
12823+ if (usg->color_attachment ||
12824+ usg->resolve_attachment ||
12825+ usg->depth_stencil_attachment ||
12826+ usg->storage_image ||
12827+ usg->immutable)
12828+ {
12829+ return 0;
12830+ } else {
12831+ return D3D11_CPU_ACCESS_WRITE;
12832+ }
12833+}
12834+
12835+_SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_buffer_usage(const sg_buffer_usage* usg) {
12836+ if (usg->immutable) {
12837+ return usg->storage_buffer ? D3D11_USAGE_DEFAULT : D3D11_USAGE_IMMUTABLE;
12838+ } else {
12839+ return D3D11_USAGE_DYNAMIC;
12840+ }
12841+}
12842+
12843+_SOKOL_PRIVATE UINT _sg_d3d11_buffer_bind_flags(const sg_buffer_usage* usg) {
12844+ UINT res = 0;
12845+ if (usg->vertex_buffer) {
12846+ res |= D3D11_BIND_VERTEX_BUFFER;
12847+ }
12848+ if (usg->index_buffer) {
12849+ res |= D3D11_BIND_INDEX_BUFFER;
12850+ }
12851+ if (usg->storage_buffer) {
12852+ res |= D3D11_BIND_SHADER_RESOURCE;
12853+ if (usg->immutable) {
12854+ res |= D3D11_BIND_UNORDERED_ACCESS;
12855+ }
12856+ }
12857+ return res;
12858+}
12859+
12860+_SOKOL_PRIVATE UINT _sg_d3d11_buffer_misc_flags(const sg_buffer_usage* usg) {
12861+ return usg->storage_buffer ? D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS : 0;
12862+}
12863+
12864+_SOKOL_PRIVATE UINT _sg_d3d11_buffer_cpu_access_flags(const sg_buffer_usage* usg) {
12865+ return usg->immutable ? 0 : D3D11_CPU_ACCESS_WRITE;
12866+}
12867+
12868+_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_base_pixel_format(sg_pixel_format fmt) {
12869+ switch (fmt) {
12870+ case SG_PIXELFORMAT_R8: return DXGI_FORMAT_R8_UNORM;
12871+ case SG_PIXELFORMAT_R8SN: return DXGI_FORMAT_R8_SNORM;
12872+ case SG_PIXELFORMAT_R8UI: return DXGI_FORMAT_R8_UINT;
12873+ case SG_PIXELFORMAT_R8SI: return DXGI_FORMAT_R8_SINT;
12874+ case SG_PIXELFORMAT_R16: return DXGI_FORMAT_R16_UNORM;
12875+ case SG_PIXELFORMAT_R16SN: return DXGI_FORMAT_R16_SNORM;
12876+ case SG_PIXELFORMAT_R16UI: return DXGI_FORMAT_R16_UINT;
12877+ case SG_PIXELFORMAT_R16SI: return DXGI_FORMAT_R16_SINT;
12878+ case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT;
12879+ case SG_PIXELFORMAT_RG8: return DXGI_FORMAT_R8G8_UNORM;
12880+ case SG_PIXELFORMAT_RG8SN: return DXGI_FORMAT_R8G8_SNORM;
12881+ case SG_PIXELFORMAT_RG8UI: return DXGI_FORMAT_R8G8_UINT;
12882+ case SG_PIXELFORMAT_RG8SI: return DXGI_FORMAT_R8G8_SINT;
12883+ case SG_PIXELFORMAT_R32UI: return DXGI_FORMAT_R32_UINT;
12884+ case SG_PIXELFORMAT_R32SI: return DXGI_FORMAT_R32_SINT;
12885+ case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT;
12886+ case SG_PIXELFORMAT_RG16: return DXGI_FORMAT_R16G16_UNORM;
12887+ case SG_PIXELFORMAT_RG16SN: return DXGI_FORMAT_R16G16_SNORM;
12888+ case SG_PIXELFORMAT_RG16UI: return DXGI_FORMAT_R16G16_UINT;
12889+ case SG_PIXELFORMAT_RG16SI: return DXGI_FORMAT_R16G16_SINT;
12890+ case SG_PIXELFORMAT_RG16F: return DXGI_FORMAT_R16G16_FLOAT;
12891+ case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM;
12892+ case SG_PIXELFORMAT_SRGB8A8: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
12893+ case SG_PIXELFORMAT_RGBA8SN: return DXGI_FORMAT_R8G8B8A8_SNORM;
12894+ case SG_PIXELFORMAT_RGBA8UI: return DXGI_FORMAT_R8G8B8A8_UINT;
12895+ case SG_PIXELFORMAT_RGBA8SI: return DXGI_FORMAT_R8G8B8A8_SINT;
12896+ case SG_PIXELFORMAT_BGRA8: return DXGI_FORMAT_B8G8R8A8_UNORM;
12897+ case SG_PIXELFORMAT_SBGR8A8: return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
12898+ case SG_PIXELFORMAT_RGB10A2: return DXGI_FORMAT_R10G10B10A2_UNORM;
12899+ case SG_PIXELFORMAT_RG11B10F: return DXGI_FORMAT_R11G11B10_FLOAT;
12900+ case SG_PIXELFORMAT_RGB9E5: return DXGI_FORMAT_R9G9B9E5_SHAREDEXP;
12901+ case SG_PIXELFORMAT_RG32UI: return DXGI_FORMAT_R32G32_UINT;
12902+ case SG_PIXELFORMAT_RG32SI: return DXGI_FORMAT_R32G32_SINT;
12903+ case SG_PIXELFORMAT_RG32F: return DXGI_FORMAT_R32G32_FLOAT;
12904+ case SG_PIXELFORMAT_RGBA16: return DXGI_FORMAT_R16G16B16A16_UNORM;
12905+ case SG_PIXELFORMAT_RGBA16SN: return DXGI_FORMAT_R16G16B16A16_SNORM;
12906+ case SG_PIXELFORMAT_RGBA16UI: return DXGI_FORMAT_R16G16B16A16_UINT;
12907+ case SG_PIXELFORMAT_RGBA16SI: return DXGI_FORMAT_R16G16B16A16_SINT;
12908+ case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT;
12909+ case SG_PIXELFORMAT_RGBA32UI: return DXGI_FORMAT_R32G32B32A32_UINT;
12910+ case SG_PIXELFORMAT_RGBA32SI: return DXGI_FORMAT_R32G32B32A32_SINT;
12911+ case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT;
12912+ case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_R32_TYPELESS;
12913+ case SG_PIXELFORMAT_DEPTH_STENCIL: return DXGI_FORMAT_R32G8X24_TYPELESS;
12914+ case SG_PIXELFORMAT_BC1_RGBA: return DXGI_FORMAT_BC1_UNORM;
12915+ case SG_PIXELFORMAT_BC2_RGBA: return DXGI_FORMAT_BC2_UNORM;
12916+ case SG_PIXELFORMAT_BC3_RGBA: return DXGI_FORMAT_BC3_UNORM;
12917+ case SG_PIXELFORMAT_BC3_SRGBA: return DXGI_FORMAT_BC3_UNORM_SRGB;
12918+ case SG_PIXELFORMAT_BC4_R: return DXGI_FORMAT_BC4_UNORM;
12919+ case SG_PIXELFORMAT_BC4_RSN: return DXGI_FORMAT_BC4_SNORM;
12920+ case SG_PIXELFORMAT_BC5_RG: return DXGI_FORMAT_BC5_UNORM;
12921+ case SG_PIXELFORMAT_BC5_RGSN: return DXGI_FORMAT_BC5_SNORM;
12922+ case SG_PIXELFORMAT_BC6H_RGBF: return DXGI_FORMAT_BC6H_SF16;
12923+ case SG_PIXELFORMAT_BC6H_RGBUF: return DXGI_FORMAT_BC6H_UF16;
12924+ case SG_PIXELFORMAT_BC7_RGBA: return DXGI_FORMAT_BC7_UNORM;
12925+ case SG_PIXELFORMAT_BC7_SRGBA: return DXGI_FORMAT_BC7_UNORM_SRGB;
12926+ default: return DXGI_FORMAT_UNKNOWN;
12927+ };
12928+}
12929+
12930+_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) {
12931+ if (fmt == SG_PIXELFORMAT_SRGB8A8) {
12932+ return DXGI_FORMAT_R8G8B8A8_TYPELESS;
12933+ } else if (fmt == SG_PIXELFORMAT_SBGR8A8) {
12934+ return DXGI_FORMAT_B8G8R8A8_TYPELESS;
12935+ } else {
12936+ return _sg_d3d11_base_pixel_format(fmt);
12937+ }
12938+}
12939+
12940+_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_srv_pixel_format(sg_pixel_format fmt) {
12941+ if (fmt == SG_PIXELFORMAT_DEPTH) {
12942+ return DXGI_FORMAT_R32_FLOAT;
12943+ } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) {
12944+ return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
12945+ } else {
12946+ return _sg_d3d11_base_pixel_format(fmt);
12947+ }
12948+}
12949+
12950+_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_dsv_pixel_format(sg_pixel_format fmt) {
12951+ if (fmt == SG_PIXELFORMAT_DEPTH) {
12952+ return DXGI_FORMAT_D32_FLOAT;
12953+ } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) {
12954+ return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
12955+ } else {
12956+ return _sg_d3d11_base_pixel_format(fmt);
12957+ }
12958+}
12959+
12960+_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_rtv_uav_pixel_format(sg_pixel_format fmt) {
12961+ if (fmt == SG_PIXELFORMAT_DEPTH) {
12962+ return DXGI_FORMAT_R32_FLOAT;
12963+ } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) {
12964+ return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
12965+ } else {
12966+ return _sg_d3d11_base_pixel_format(fmt);
12967+ }
12968+}
12969+
12970+_SOKOL_PRIVATE D3D11_PRIMITIVE_TOPOLOGY _sg_d3d11_primitive_topology(sg_primitive_type prim_type) {
12971+ switch (prim_type) {
12972+ case SG_PRIMITIVETYPE_POINTS: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
12973+ case SG_PRIMITIVETYPE_LINES: return D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
12974+ case SG_PRIMITIVETYPE_LINE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
12975+ case SG_PRIMITIVETYPE_TRIANGLES: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
12976+ case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
12977+ default: SOKOL_UNREACHABLE; return (D3D11_PRIMITIVE_TOPOLOGY) 0;
12978+ }
12979+}
12980+
12981+_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_index_format(sg_index_type index_type) {
12982+ switch (index_type) {
12983+ case SG_INDEXTYPE_NONE: return DXGI_FORMAT_UNKNOWN;
12984+ case SG_INDEXTYPE_UINT16: return DXGI_FORMAT_R16_UINT;
12985+ case SG_INDEXTYPE_UINT32: return DXGI_FORMAT_R32_UINT;
12986+ default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0;
12987+ }
12988+}
12989+
12990+_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, sg_filter mipmap_f, bool comparison, uint32_t max_anisotropy) {
12991+ uint32_t d3d11_filter = 0;
12992+ if (max_anisotropy > 1) {
12993+ // D3D11_FILTER_ANISOTROPIC = 0x55,
12994+ d3d11_filter |= 0x55;
12995+ } else {
12996+ // D3D11_FILTER_MIN_MAG_MIP_POINT = 0,
12997+ // D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR = 0x1,
12998+ // D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x4,
12999+ // D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR = 0x5,
13000+ // D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT = 0x10,
13001+ // D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x11,
13002+ // D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14,
13003+ // D3D11_FILTER_MIN_MAG_MIP_LINEAR = 0x15,
13004+ if (mipmap_f == SG_FILTER_LINEAR) {
13005+ d3d11_filter |= 0x01;
13006+ }
13007+ if (mag_f == SG_FILTER_LINEAR) {
13008+ d3d11_filter |= 0x04;
13009+ }
13010+ if (min_f == SG_FILTER_LINEAR) {
13011+ d3d11_filter |= 0x10;
13012+ }
13013+ }
13014+ // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT = 0x80,
13015+ // D3D11_FILTER_COMPARISON_MIN_MAG_POINT_MIP_LINEAR = 0x81,
13016+ // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x84,
13017+ // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_MIP_LINEAR = 0x85,
13018+ // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT = 0x90,
13019+ // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x91,
13020+ // D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT = 0x94,
13021+ // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR = 0x95,
13022+ // D3D11_FILTER_COMPARISON_ANISOTROPIC = 0xd5,
13023+ if (comparison) {
13024+ d3d11_filter |= 0x80;
13025+ }
13026+ return (D3D11_FILTER)d3d11_filter;
13027+}
13028+
13029+_SOKOL_PRIVATE D3D11_TEXTURE_ADDRESS_MODE _sg_d3d11_address_mode(sg_wrap m) {
13030+ switch (m) {
13031+ case SG_WRAP_REPEAT: return D3D11_TEXTURE_ADDRESS_WRAP;
13032+ case SG_WRAP_CLAMP_TO_EDGE: return D3D11_TEXTURE_ADDRESS_CLAMP;
13033+ case SG_WRAP_CLAMP_TO_BORDER: return D3D11_TEXTURE_ADDRESS_BORDER;
13034+ case SG_WRAP_MIRRORED_REPEAT: return D3D11_TEXTURE_ADDRESS_MIRROR;
13035+ default: SOKOL_UNREACHABLE; return (D3D11_TEXTURE_ADDRESS_MODE) 0;
13036+ }
13037+}
13038+
13039+_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_vertex_format(sg_vertex_format fmt) {
13040+ switch (fmt) {
13041+ case SG_VERTEXFORMAT_FLOAT: return DXGI_FORMAT_R32_FLOAT;
13042+ case SG_VERTEXFORMAT_FLOAT2: return DXGI_FORMAT_R32G32_FLOAT;
13043+ case SG_VERTEXFORMAT_FLOAT3: return DXGI_FORMAT_R32G32B32_FLOAT;
13044+ case SG_VERTEXFORMAT_FLOAT4: return DXGI_FORMAT_R32G32B32A32_FLOAT;
13045+ case SG_VERTEXFORMAT_INT: return DXGI_FORMAT_R32_SINT;
13046+ case SG_VERTEXFORMAT_INT2: return DXGI_FORMAT_R32G32_SINT;
13047+ case SG_VERTEXFORMAT_INT3: return DXGI_FORMAT_R32G32B32_SINT;
13048+ case SG_VERTEXFORMAT_INT4: return DXGI_FORMAT_R32G32B32A32_SINT;
13049+ case SG_VERTEXFORMAT_UINT: return DXGI_FORMAT_R32_UINT;
13050+ case SG_VERTEXFORMAT_UINT2: return DXGI_FORMAT_R32G32_UINT;
13051+ case SG_VERTEXFORMAT_UINT3: return DXGI_FORMAT_R32G32B32_UINT;
13052+ case SG_VERTEXFORMAT_UINT4: return DXGI_FORMAT_R32G32B32A32_UINT;
13053+ case SG_VERTEXFORMAT_BYTE4: return DXGI_FORMAT_R8G8B8A8_SINT;
13054+ case SG_VERTEXFORMAT_BYTE4N: return DXGI_FORMAT_R8G8B8A8_SNORM;
13055+ case SG_VERTEXFORMAT_UBYTE4: return DXGI_FORMAT_R8G8B8A8_UINT;
13056+ case SG_VERTEXFORMAT_UBYTE4N: return DXGI_FORMAT_R8G8B8A8_UNORM;
13057+ case SG_VERTEXFORMAT_SHORT2: return DXGI_FORMAT_R16G16_SINT;
13058+ case SG_VERTEXFORMAT_SHORT2N: return DXGI_FORMAT_R16G16_SNORM;
13059+ case SG_VERTEXFORMAT_USHORT2: return DXGI_FORMAT_R16G16_UINT;
13060+ case SG_VERTEXFORMAT_USHORT2N: return DXGI_FORMAT_R16G16_UNORM;
13061+ case SG_VERTEXFORMAT_SHORT4: return DXGI_FORMAT_R16G16B16A16_SINT;
13062+ case SG_VERTEXFORMAT_SHORT4N: return DXGI_FORMAT_R16G16B16A16_SNORM;
13063+ case SG_VERTEXFORMAT_USHORT4: return DXGI_FORMAT_R16G16B16A16_UINT;
13064+ case SG_VERTEXFORMAT_USHORT4N: return DXGI_FORMAT_R16G16B16A16_UNORM;
13065+ case SG_VERTEXFORMAT_UINT10_N2: return DXGI_FORMAT_R10G10B10A2_UNORM;
13066+ case SG_VERTEXFORMAT_HALF2: return DXGI_FORMAT_R16G16_FLOAT;
13067+ case SG_VERTEXFORMAT_HALF4: return DXGI_FORMAT_R16G16B16A16_FLOAT;
13068+ default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0;
13069+ }
13070+}
13071+
13072+_SOKOL_PRIVATE D3D11_INPUT_CLASSIFICATION _sg_d3d11_input_classification(sg_vertex_step step) {
13073+ switch (step) {
13074+ case SG_VERTEXSTEP_PER_VERTEX: return D3D11_INPUT_PER_VERTEX_DATA;
13075+ case SG_VERTEXSTEP_PER_INSTANCE: return D3D11_INPUT_PER_INSTANCE_DATA;
13076+ default: SOKOL_UNREACHABLE; return (D3D11_INPUT_CLASSIFICATION) 0;
13077+ }
13078+}
13079+
13080+_SOKOL_PRIVATE D3D11_CULL_MODE _sg_d3d11_cull_mode(sg_cull_mode m) {
13081+ switch (m) {
13082+ case SG_CULLMODE_NONE: return D3D11_CULL_NONE;
13083+ case SG_CULLMODE_FRONT: return D3D11_CULL_FRONT;
13084+ case SG_CULLMODE_BACK: return D3D11_CULL_BACK;
13085+ default: SOKOL_UNREACHABLE; return (D3D11_CULL_MODE) 0;
13086+ }
13087+}
13088+
13089+_SOKOL_PRIVATE D3D11_COMPARISON_FUNC _sg_d3d11_compare_func(sg_compare_func f) {
13090+ switch (f) {
13091+ case SG_COMPAREFUNC_NEVER: return D3D11_COMPARISON_NEVER;
13092+ case SG_COMPAREFUNC_LESS: return D3D11_COMPARISON_LESS;
13093+ case SG_COMPAREFUNC_EQUAL: return D3D11_COMPARISON_EQUAL;
13094+ case SG_COMPAREFUNC_LESS_EQUAL: return D3D11_COMPARISON_LESS_EQUAL;
13095+ case SG_COMPAREFUNC_GREATER: return D3D11_COMPARISON_GREATER;
13096+ case SG_COMPAREFUNC_NOT_EQUAL: return D3D11_COMPARISON_NOT_EQUAL;
13097+ case SG_COMPAREFUNC_GREATER_EQUAL: return D3D11_COMPARISON_GREATER_EQUAL;
13098+ case SG_COMPAREFUNC_ALWAYS: return D3D11_COMPARISON_ALWAYS;
13099+ default: SOKOL_UNREACHABLE; return (D3D11_COMPARISON_FUNC) 0;
13100+ }
13101+}
13102+
13103+_SOKOL_PRIVATE D3D11_STENCIL_OP _sg_d3d11_stencil_op(sg_stencil_op op) {
13104+ switch (op) {
13105+ case SG_STENCILOP_KEEP: return D3D11_STENCIL_OP_KEEP;
13106+ case SG_STENCILOP_ZERO: return D3D11_STENCIL_OP_ZERO;
13107+ case SG_STENCILOP_REPLACE: return D3D11_STENCIL_OP_REPLACE;
13108+ case SG_STENCILOP_INCR_CLAMP: return D3D11_STENCIL_OP_INCR_SAT;
13109+ case SG_STENCILOP_DECR_CLAMP: return D3D11_STENCIL_OP_DECR_SAT;
13110+ case SG_STENCILOP_INVERT: return D3D11_STENCIL_OP_INVERT;
13111+ case SG_STENCILOP_INCR_WRAP: return D3D11_STENCIL_OP_INCR;
13112+ case SG_STENCILOP_DECR_WRAP: return D3D11_STENCIL_OP_DECR;
13113+ default: SOKOL_UNREACHABLE; return (D3D11_STENCIL_OP) 0;
13114+ }
13115+}
13116+
13117+_SOKOL_PRIVATE D3D11_BLEND _sg_d3d11_blend_factor(sg_blend_factor f) {
13118+ switch (f) {
13119+ case SG_BLENDFACTOR_ZERO: return D3D11_BLEND_ZERO;
13120+ case SG_BLENDFACTOR_ONE: return D3D11_BLEND_ONE;
13121+ case SG_BLENDFACTOR_SRC_COLOR: return D3D11_BLEND_SRC_COLOR;
13122+ case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return D3D11_BLEND_INV_SRC_COLOR;
13123+ case SG_BLENDFACTOR_SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA;
13124+ case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return D3D11_BLEND_INV_SRC_ALPHA;
13125+ case SG_BLENDFACTOR_DST_COLOR: return D3D11_BLEND_DEST_COLOR;
13126+ case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return D3D11_BLEND_INV_DEST_COLOR;
13127+ case SG_BLENDFACTOR_DST_ALPHA: return D3D11_BLEND_DEST_ALPHA;
13128+ case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA;
13129+ case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return D3D11_BLEND_SRC_ALPHA_SAT;
13130+ case SG_BLENDFACTOR_BLEND_COLOR: return D3D11_BLEND_BLEND_FACTOR;
13131+ case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return D3D11_BLEND_INV_BLEND_FACTOR;
13132+ case SG_BLENDFACTOR_BLEND_ALPHA: return D3D11_BLEND_BLEND_FACTOR;
13133+ case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return D3D11_BLEND_INV_BLEND_FACTOR;
13134+ case SG_BLENDFACTOR_SRC1_COLOR: return D3D11_BLEND_SRC1_COLOR;
13135+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: return D3D11_BLEND_INV_SRC1_COLOR;
13136+ case SG_BLENDFACTOR_SRC1_ALPHA: return D3D11_BLEND_SRC1_ALPHA;
13137+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: return D3D11_BLEND_INV_SRC1_ALPHA;
13138+ default: SOKOL_UNREACHABLE; return (D3D11_BLEND) 0;
13139+ }
13140+}
13141+
13142+_SOKOL_PRIVATE D3D11_BLEND_OP _sg_d3d11_blend_op(sg_blend_op op) {
13143+ switch (op) {
13144+ case SG_BLENDOP_ADD: return D3D11_BLEND_OP_ADD;
13145+ case SG_BLENDOP_SUBTRACT: return D3D11_BLEND_OP_SUBTRACT;
13146+ case SG_BLENDOP_REVERSE_SUBTRACT: return D3D11_BLEND_OP_REV_SUBTRACT;
13147+ case SG_BLENDOP_MIN: return D3D11_BLEND_OP_MIN;
13148+ case SG_BLENDOP_MAX: return D3D11_BLEND_OP_MAX;
13149+ default: SOKOL_UNREACHABLE; return (D3D11_BLEND_OP) 0;
13150+ }
13151+}
13152+
13153+_SOKOL_PRIVATE UINT8 _sg_d3d11_color_write_mask(sg_color_mask m) {
13154+ UINT8 res = 0;
13155+ if (m & SG_COLORMASK_R) {
13156+ res |= D3D11_COLOR_WRITE_ENABLE_RED;
13157+ }
13158+ if (m & SG_COLORMASK_G) {
13159+ res |= D3D11_COLOR_WRITE_ENABLE_GREEN;
13160+ }
13161+ if (m & SG_COLORMASK_B) {
13162+ res |= D3D11_COLOR_WRITE_ENABLE_BLUE;
13163+ }
13164+ if (m & SG_COLORMASK_A) {
13165+ res |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
13166+ }
13167+ return res;
13168+}
13169+
13170+_SOKOL_PRIVATE UINT _sg_d3d11_dxgi_fmt_caps(DXGI_FORMAT dxgi_fmt) {
13171+ UINT dxgi_fmt_caps = 0;
13172+ if (dxgi_fmt != DXGI_FORMAT_UNKNOWN) {
13173+ HRESULT hr = _sg_d3d11_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps);
13174+ SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr));
13175+ if (!SUCCEEDED(hr)) {
13176+ dxgi_fmt_caps = 0;
13177+ }
13178+ }
13179+ return dxgi_fmt_caps;
13180+}
13181+
13182+// see: https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits#resource-limits-for-feature-level-11-hardware
13183+_SOKOL_PRIVATE void _sg_d3d11_init_caps(void) {
13184+ _sg.backend = SG_BACKEND_D3D11;
13185+
13186+ _sg.features.origin_top_left = true;
13187+ _sg.features.image_clamp_to_border = true;
13188+ _sg.features.mrt_independent_blend_state = true;
13189+ _sg.features.mrt_independent_write_mask = true;
13190+ _sg.features.compute = true;
13191+ _sg.features.msaa_texture_bindings = true;
13192+ _sg.features.draw_base_vertex = true;
13193+ _sg.features.draw_base_instance = true;
13194+ _sg.features.dual_source_blending = true;
13195+ _sg.features.vertexformat_int10_n2 = false;
13196+
13197+ _sg.limits.max_image_size_2d = 16 * 1024;
13198+ _sg.limits.max_image_size_cube = 16 * 1024;
13199+ _sg.limits.max_image_size_3d = 2 * 1024;
13200+ _sg.limits.max_image_size_array = 16 * 1024;
13201+ _sg.limits.max_image_array_layers = _SG_D3D11_MAX_TEXTUREARRAY_LAYERS;
13202+ _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES;
13203+ _sg.limits.max_color_attachments = _sg_min(8, SG_MAX_COLOR_ATTACHMENTS);
13204+ _sg.limits.max_texture_bindings_per_stage = _sg_min(128, SG_MAX_VIEW_BINDSLOTS);
13205+ _sg.limits.max_storage_buffer_bindings_per_stage = _sg_min(64, SG_MAX_VIEW_BINDSLOTS);
13206+ if (_sg_d3d11_GetFeatureLevel(_sg.d3d11.dev) >= D3D_FEATURE_LEVEL_11_1) {
13207+ _sg.limits.d3d11_max_unordered_access_views = _sg_min(64, SG_MAX_VIEW_BINDSLOTS);
13208+ } else {
13209+ _sg.limits.d3d11_max_unordered_access_views = _sg_min(8, SG_MAX_VIEW_BINDSLOTS);
13210+ }
13211+ _sg.limits.max_storage_image_bindings_per_stage = _sg.limits.d3d11_max_unordered_access_views;
13212+
13213+ // see: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_format_support
13214+ for (int fmt = (SG_PIXELFORMAT_NONE+1); fmt < _SG_PIXELFORMAT_NUM; fmt++) {
13215+ const UINT srv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_srv_pixel_format((sg_pixel_format)fmt));
13216+ const UINT rtv_uav_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_rtv_uav_pixel_format((sg_pixel_format)fmt));
13217+ const UINT dsv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_dsv_pixel_format((sg_pixel_format)fmt));
13218+ _sg_pixelformat_info_t* info = &_sg.formats[fmt];
13219+ const bool render = 0 != (rtv_uav_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET);
13220+ const bool depth = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL);
13221+ info->sample = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D);
13222+ info->filter = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE);
13223+ info->render = render || depth;
13224+ if (depth) {
13225+ info->blend = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE);
13226+ info->msaa = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET);
13227+ } else {
13228+ info->blend = 0 != (rtv_uav_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE);
13229+ info->msaa = 0 != (rtv_uav_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET);
13230+ }
13231+ info->depth = depth;
13232+ info->read = info->write = 0 != (rtv_uav_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW);
13233+ }
13234+}
13235+
13236+_SOKOL_PRIVATE void _sg_d3d11_setup_backend(const sg_desc* desc) {
13237+ // assume _sg.d3d11 already is zero-initialized
13238+ SOKOL_ASSERT(desc);
13239+ SOKOL_ASSERT(desc->environment.d3d11.device);
13240+ SOKOL_ASSERT(desc->environment.d3d11.device_context);
13241+ _sg.d3d11.valid = true;
13242+ _sg.d3d11.dev = (ID3D11Device*) desc->environment.d3d11.device;
13243+ _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->environment.d3d11.device_context;
13244+ _sg_d3d11_init_caps();
13245+ if (_sg_d3d11_GetFeatureLevel(_sg.d3d11.dev) == D3D_FEATURE_LEVEL_11_0) {
13246+ _SG_WARN(D3D11_FEATURE_LEVEL_0_DETECTED);
13247+ }
13248+}
13249+
13250+_SOKOL_PRIVATE void _sg_d3d11_discard_backend(void) {
13251+ SOKOL_ASSERT(_sg.d3d11.valid);
13252+ _sg.d3d11.valid = false;
13253+}
13254+
13255+_SOKOL_PRIVATE void _sg_d3d11_clear_state(void) {
13256+ // clear all the device context state, so that resource refs don't keep stuck in the d3d device context
13257+ _sg_d3d11_ClearState(_sg.d3d11.ctx);
13258+}
13259+
13260+_SOKOL_PRIVATE void _sg_d3d11_reset_state_cache(void) {
13261+ // there's currently no state cache in the D3D11 backend, so this is a no-op
13262+}
13263+
13264+_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) {
13265+ SOKOL_ASSERT(buf && desc);
13266+ SOKOL_ASSERT(!buf->d3d11.buf);
13267+ const bool injected = (0 != desc->d3d11_buffer);
13268+ if (injected) {
13269+ buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer;
13270+ _sg_d3d11_AddRef(buf->d3d11.buf);
13271+ } else {
13272+ _SG_STRUCT(D3D11_BUFFER_DESC, d3d11_buf_desc);
13273+ d3d11_buf_desc.ByteWidth = (UINT)buf->cmn.size;
13274+ d3d11_buf_desc.Usage = _sg_d3d11_buffer_usage(&buf->cmn.usage);
13275+ d3d11_buf_desc.BindFlags = _sg_d3d11_buffer_bind_flags(&buf->cmn.usage);
13276+ d3d11_buf_desc.CPUAccessFlags = _sg_d3d11_buffer_cpu_access_flags(&buf->cmn.usage);
13277+ d3d11_buf_desc.MiscFlags = _sg_d3d11_buffer_misc_flags(&buf->cmn.usage);
13278+ D3D11_SUBRESOURCE_DATA* init_data_ptr = 0;
13279+ _SG_STRUCT(D3D11_SUBRESOURCE_DATA, init_data);
13280+ if (desc->data.ptr) {
13281+ init_data.pSysMem = desc->data.ptr;
13282+ init_data_ptr = &init_data;
13283+ }
13284+ HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_buf_desc, init_data_ptr, &buf->d3d11.buf);
13285+ if (!(SUCCEEDED(hr) && buf->d3d11.buf)) {
13286+ _SG_ERROR(D3D11_CREATE_BUFFER_FAILED);
13287+ return SG_RESOURCESTATE_FAILED;
13288+ }
13289+ _sg_d3d11_setlabel(buf->d3d11.buf, desc->label);
13290+ }
13291+ return SG_RESOURCESTATE_VALID;
13292+}
13293+
13294+_SOKOL_PRIVATE void _sg_d3d11_discard_buffer(_sg_buffer_t* buf) {
13295+ SOKOL_ASSERT(buf);
13296+ if (buf->d3d11.buf) {
13297+ _sg_d3d11_Release(buf->d3d11.buf);
13298+ }
13299+}
13300+
13301+_SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_data* data) {
13302+ const int num_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? 1 : img->cmn.num_slices;
13303+ int subres_index = 0;
13304+ for (int slice_index = 0; slice_index < num_slices; slice_index++) {
13305+ for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) {
13306+ SOKOL_ASSERT(subres_index < _SG_D3D11_MAX_TEXTURE_SUBRESOURCES);
13307+ D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index];
13308+ const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index);
13309+ const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index);
13310+ const sg_range* miplevel_data = &(data->mip_levels[mip_index]);
13311+ const size_t slice_size = miplevel_data->size / (size_t)num_slices;
13312+ const size_t slice_offset = slice_size * (size_t)slice_index;
13313+ const uint8_t* ptr = (const uint8_t*) miplevel_data->ptr;
13314+ subres_data->pSysMem = ptr + slice_offset;
13315+ subres_data->SysMemPitch = (UINT)_sg_row_pitch(img->cmn.pixel_format, mip_width, 1);
13316+ if (img->cmn.type == SG_IMAGETYPE_3D) {
13317+ subres_data->SysMemSlicePitch = (UINT)_sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1);
13318+ } else {
13319+ subres_data->SysMemSlicePitch = 0;
13320+ }
13321+ }
13322+ }
13323+}
13324+
13325+_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const sg_image_desc* desc) {
13326+ SOKOL_ASSERT(img && desc);
13327+ SOKOL_ASSERT((0 == img->d3d11.tex2d) && (0 == img->d3d11.tex3d) && (0 == img->d3d11.res));
13328+ HRESULT hr;
13329+
13330+ const bool injected = (0 != desc->d3d11_texture);
13331+ const bool msaa = (img->cmn.sample_count > 1);
13332+ SOKOL_ASSERT(!(msaa && (img->cmn.type == SG_IMAGETYPE_CUBE)));
13333+ img->d3d11.format = _sg_d3d11_texture_pixel_format(img->cmn.pixel_format);
13334+ if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) {
13335+ _SG_ERROR(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT);
13336+ return SG_RESOURCESTATE_FAILED;
13337+ }
13338+
13339+ // prepare initial content pointers
13340+ D3D11_SUBRESOURCE_DATA* init_data = 0;
13341+ if (!injected && desc->data.mip_levels[0].ptr) {
13342+ _sg_d3d11_fill_subres_data(img, &desc->data);
13343+ init_data = _sg.d3d11.subres_data;
13344+ }
13345+ if (img->cmn.type != SG_IMAGETYPE_3D) {
13346+ // 2D-, cube- or array-texture
13347+ // first check for injected texture and/or resource view
13348+ if (injected) {
13349+ img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture;
13350+ _sg_d3d11_AddRef(img->d3d11.tex2d);
13351+ } else {
13352+ // if not injected, create 2D texture
13353+ _SG_STRUCT(D3D11_TEXTURE2D_DESC, d3d11_tex_desc);
13354+ d3d11_tex_desc.Width = (UINT)img->cmn.width;
13355+ d3d11_tex_desc.Height = (UINT)img->cmn.height;
13356+ d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps;
13357+ d3d11_tex_desc.ArraySize = (UINT)img->cmn.num_slices;
13358+ d3d11_tex_desc.Format = img->d3d11.format;
13359+ d3d11_tex_desc.BindFlags = _sg_d3d11_image_bind_flags(&img->cmn.usage);
13360+ d3d11_tex_desc.Usage = _sg_d3d11_image_usage(&img->cmn.usage);
13361+ d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_image_cpu_access_flags(&img->cmn.usage);
13362+ d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count;
13363+ d3d11_tex_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0);
13364+ d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
13365+ hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d);
13366+ if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) {
13367+ _SG_ERROR(D3D11_CREATE_2D_TEXTURE_FAILED);
13368+ return SG_RESOURCESTATE_FAILED;
13369+ }
13370+ _sg_d3d11_setlabel(img->d3d11.tex2d, desc->label);
13371+ }
13372+ SOKOL_ASSERT(img->d3d11.tex2d);
13373+ img->d3d11.res = (ID3D11Resource*)img->d3d11.tex2d;
13374+ _sg_d3d11_AddRef(img->d3d11.res);
13375+ } else {
13376+ // 3D texture - same procedure, first check if injected, than create non-injected
13377+ if (injected) {
13378+ img->d3d11.tex3d = (ID3D11Texture3D*) desc->d3d11_texture;
13379+ _sg_d3d11_AddRef(img->d3d11.tex3d);
13380+ } else {
13381+ // not injected, create 3d texture
13382+ _SG_STRUCT(D3D11_TEXTURE3D_DESC, d3d11_tex_desc);
13383+ d3d11_tex_desc.Width = (UINT)img->cmn.width;
13384+ d3d11_tex_desc.Height = (UINT)img->cmn.height;
13385+ d3d11_tex_desc.Depth = (UINT)img->cmn.num_slices;
13386+ d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps;
13387+ d3d11_tex_desc.Format = img->d3d11.format;
13388+ d3d11_tex_desc.BindFlags = _sg_d3d11_image_bind_flags(&img->cmn.usage);
13389+ d3d11_tex_desc.Usage = _sg_d3d11_image_usage(&img->cmn.usage);
13390+ d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_image_cpu_access_flags(&img->cmn.usage);
13391+ if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) {
13392+ _SG_ERROR(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT);
13393+ return SG_RESOURCESTATE_FAILED;
13394+ }
13395+ hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d);
13396+ if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) {
13397+ _SG_ERROR(D3D11_CREATE_3D_TEXTURE_FAILED);
13398+ return SG_RESOURCESTATE_FAILED;
13399+ }
13400+ _sg_d3d11_setlabel(img->d3d11.tex3d, desc->label);
13401+ }
13402+ SOKOL_ASSERT(img->d3d11.tex3d);
13403+ img->d3d11.res = (ID3D11Resource*)img->d3d11.tex3d;
13404+ _sg_d3d11_AddRef(img->d3d11.res);
13405+ }
13406+ return SG_RESOURCESTATE_VALID;
13407+}
13408+
13409+_SOKOL_PRIVATE void _sg_d3d11_discard_image(_sg_image_t* img) {
13410+ SOKOL_ASSERT(img);
13411+ if (img->d3d11.tex2d) {
13412+ _sg_d3d11_Release(img->d3d11.tex2d);
13413+ }
13414+ if (img->d3d11.tex3d) {
13415+ _sg_d3d11_Release(img->d3d11.tex3d);
13416+ }
13417+ if (img->d3d11.res) {
13418+ _sg_d3d11_Release(img->d3d11.res);
13419+ }
13420+}
13421+
13422+_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) {
13423+ SOKOL_ASSERT(smp && desc);
13424+ SOKOL_ASSERT(0 == smp->d3d11.smp);
13425+ const bool injected = (0 != desc->d3d11_sampler);
13426+ if (injected) {
13427+ smp->d3d11.smp = (ID3D11SamplerState*)desc->d3d11_sampler;
13428+ _sg_d3d11_AddRef(smp->d3d11.smp);
13429+ } else {
13430+ _SG_STRUCT(D3D11_SAMPLER_DESC, d3d11_smp_desc);
13431+ d3d11_smp_desc.Filter = _sg_d3d11_filter(desc->min_filter, desc->mag_filter, desc->mipmap_filter, desc->compare != SG_COMPAREFUNC_NEVER, desc->max_anisotropy);
13432+ d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(desc->wrap_u);
13433+ d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(desc->wrap_v);
13434+ d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(desc->wrap_w);
13435+ d3d11_smp_desc.MipLODBias = 0.0f; // FIXME?
13436+ switch (desc->border_color) {
13437+ case SG_BORDERCOLOR_TRANSPARENT_BLACK:
13438+ // all 0.0f
13439+ break;
13440+ case SG_BORDERCOLOR_OPAQUE_WHITE:
13441+ for (int i = 0; i < 4; i++) {
13442+ d3d11_smp_desc.BorderColor[i] = 1.0f;
13443+ }
13444+ break;
13445+ default:
13446+ // opaque black
13447+ d3d11_smp_desc.BorderColor[3] = 1.0f;
13448+ break;
13449+ }
13450+ d3d11_smp_desc.MaxAnisotropy = desc->max_anisotropy;
13451+ d3d11_smp_desc.ComparisonFunc = _sg_d3d11_compare_func(desc->compare);
13452+ d3d11_smp_desc.MinLOD = desc->min_lod;
13453+ d3d11_smp_desc.MaxLOD = desc->max_lod;
13454+ HRESULT hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &smp->d3d11.smp);
13455+ if (!(SUCCEEDED(hr) && smp->d3d11.smp)) {
13456+ _SG_ERROR(D3D11_CREATE_SAMPLER_STATE_FAILED);
13457+ return SG_RESOURCESTATE_FAILED;
13458+ }
13459+ _sg_d3d11_setlabel(smp->d3d11.smp, desc->label);
13460+ }
13461+ return SG_RESOURCESTATE_VALID;
13462+}
13463+
13464+_SOKOL_PRIVATE void _sg_d3d11_discard_sampler(_sg_sampler_t* smp) {
13465+ SOKOL_ASSERT(smp);
13466+ if (smp->d3d11.smp) {
13467+ _sg_d3d11_Release(smp->d3d11.smp);
13468+ }
13469+}
13470+
13471+_SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) {
13472+ if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) {
13473+ _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll");
13474+ if (0 == _sg.d3d11.d3dcompiler_dll) {
13475+ // don't attempt to load missing DLL in the future
13476+ _SG_ERROR(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED);
13477+ _sg.d3d11.d3dcompiler_dll_load_failed = true;
13478+ return false;
13479+ }
13480+ // look up function pointers
13481+ _sg.d3d11.D3DCompile_func = (pD3DCompile)(void*) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile");
13482+ SOKOL_ASSERT(_sg.d3d11.D3DCompile_func);
13483+ }
13484+ return 0 != _sg.d3d11.d3dcompiler_dll;
13485+}
13486+
13487+_SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_function* shd_func) {
13488+ if (!_sg_d3d11_load_d3dcompiler_dll()) {
13489+ return NULL;
13490+ }
13491+ SOKOL_ASSERT(shd_func->d3d11_target);
13492+ UINT flags1 = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR;
13493+ if (_sg.desc.d3d11.shader_debugging) {
13494+ flags1 |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
13495+ } else {
13496+ flags1 |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
13497+ }
13498+ ID3DBlob* output = NULL;
13499+ ID3DBlob* errors_or_warnings = NULL;
13500+ HRESULT hr = _sg.d3d11.D3DCompile_func(
13501+ shd_func->source, // pSrcData
13502+ strlen(shd_func->source), // SrcDataSize
13503+ shd_func->d3d11_filepath, // pSourceName
13504+ NULL, // pDefines
13505+ D3D_COMPILE_STANDARD_FILE_INCLUDE, // pInclude
13506+ shd_func->entry ? shd_func->entry : "main", // pEntryPoint
13507+ shd_func->d3d11_target, // pTarget
13508+ flags1, // Flags1
13509+ 0, // Flags2
13510+ &output, // ppCode
13511+ &errors_or_warnings); // ppErrorMsgs
13512+ if (FAILED(hr)) {
13513+ _SG_ERROR(D3D11_SHADER_COMPILATION_FAILED);
13514+ }
13515+ if (errors_or_warnings) {
13516+ _SG_WARN(D3D11_SHADER_COMPILATION_OUTPUT);
13517+ _SG_LOGMSG(D3D11_SHADER_COMPILATION_OUTPUT, (LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings));
13518+ _sg_d3d11_Release(errors_or_warnings); errors_or_warnings = NULL;
13519+ }
13520+ if (FAILED(hr)) {
13521+ // just in case, usually output is NULL here
13522+ if (output) {
13523+ _sg_d3d11_Release(output);
13524+ output = NULL;
13525+ }
13526+ }
13527+ return output;
13528+}
13529+
13530+// NOTE: this is an out-of-range check for HLSL bindslots that's also active in release mode
13531+_SOKOL_PRIVATE bool _sg_d3d11_ensure_hlsl_bindslot_ranges(const sg_shader_desc* desc) {
13532+ SOKOL_ASSERT(desc);
13533+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
13534+ const sg_shader_uniform_block* ub = &desc->uniform_blocks[i];
13535+ if (ub->stage != SG_SHADERSTAGE_NONE) {
13536+ if (ub->hlsl_register_b_n >= _SG_D3D11_MAX_STAGE_UB_BINDINGS) {
13537+ _SG_ERROR(D3D11_UNIFORMBLOCK_HLSL_REGISTER_B_OUT_OF_RANGE);
13538+ return false;
13539+ }
13540+ }
13541+ }
13542+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
13543+ const sg_shader_view* view = &desc->views[i];
13544+ if (view->texture.stage != SG_SHADERSTAGE_NONE) {
13545+ if (view->texture.hlsl_register_t_n >= _SG_D3D11_MAX_STAGE_SRV_BINDINGS) {
13546+ _SG_ERROR(D3D11_IMAGE_HLSL_REGISTER_T_OUT_OF_RANGE);
13547+ return false;
13548+ }
13549+ }
13550+ if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) {
13551+ if (view->storage_buffer.hlsl_register_t_n >= _SG_D3D11_MAX_STAGE_SRV_BINDINGS) {
13552+ _SG_ERROR(D3D11_STORAGEBUFFER_HLSL_REGISTER_T_OUT_OF_RANGE);
13553+ return false;
13554+ }
13555+ if (view->storage_buffer.hlsl_register_u_n >= _SG_D3D11_MAX_STAGE_UAV_BINDINGS) {
13556+ _SG_ERROR(D3D11_STORAGEBUFFER_HLSL_REGISTER_U_OUT_OF_RANGE);
13557+ return false;
13558+ }
13559+ }
13560+ if (view->storage_image.stage != SG_SHADERSTAGE_NONE) {
13561+ if (view->storage_image.hlsl_register_u_n >= _SG_D3D11_MAX_STAGE_UAV_BINDINGS) {
13562+ _SG_ERROR(D3D11_STORAGEIMAGE_HLSL_REGISTER_U_OUT_OF_RANGE);
13563+ return false;
13564+ }
13565+ }
13566+ }
13567+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
13568+ const sg_shader_sampler* smp = &desc->samplers[i];
13569+ if (smp->stage != SG_SHADERSTAGE_NONE) {
13570+ if (smp->hlsl_register_s_n >= _SG_D3D11_MAX_STAGE_SMP_BINDINGS) {
13571+ _SG_ERROR(D3D11_SAMPLER_HLSL_REGISTER_S_OUT_OF_RANGE);
13572+ return false;
13573+ }
13574+ }
13575+ }
13576+ return true;
13577+}
13578+
13579+_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) {
13580+ SOKOL_ASSERT(shd && desc);
13581+ SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.cs && !shd->d3d11.vs_blob);
13582+ HRESULT hr;
13583+
13584+ // perform a range-check on HLSL bindslots that's also active in release
13585+ // mode to avoid potential out-of-bounds array accesses
13586+ if (!_sg_d3d11_ensure_hlsl_bindslot_ranges(desc)) {
13587+ return SG_RESOURCESTATE_FAILED;
13588+ }
13589+
13590+ // copy vertex attribute semantic names and indices
13591+ for (size_t i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) {
13592+ _sg_strcpy(&shd->d3d11.attrs[i].sem_name, desc->attrs[i].hlsl_sem_name);
13593+ shd->d3d11.attrs[i].sem_index = desc->attrs[i].hlsl_sem_index;
13594+ }
13595+
13596+ // copy HLSL bind slots
13597+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
13598+ SOKOL_ASSERT(0 == shd->d3d11.ub_register_b_n[i]);
13599+ shd->d3d11.ub_register_b_n[i] = desc->uniform_blocks[i].hlsl_register_b_n;
13600+ }
13601+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
13602+ const sg_shader_view* view = &desc->views[i];
13603+ SOKOL_ASSERT((0 == shd->d3d11.view_register_t_n[i]) && (0 == shd->d3d11.view_register_u_n[i]));
13604+ if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) {
13605+ shd->d3d11.view_register_t_n[i] = view->storage_buffer.hlsl_register_t_n;
13606+ shd->d3d11.view_register_u_n[i] = view->storage_buffer.hlsl_register_u_n;
13607+ } else if (view->texture.stage != SG_SHADERSTAGE_NONE) {
13608+ shd->d3d11.view_register_t_n[i] = view->texture.hlsl_register_t_n;
13609+ } else if (view->storage_image.stage != SG_SHADERSTAGE_NONE) {
13610+ shd->d3d11.view_register_u_n[i] = view->storage_image.hlsl_register_u_n;
13611+ }
13612+ }
13613+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
13614+ SOKOL_ASSERT(0 == shd->d3d11.smp_register_s_n[i]);
13615+ shd->d3d11.smp_register_s_n[i] = desc->samplers[i].hlsl_register_s_n;
13616+ }
13617+
13618+ // create a D3D constant buffer for each uniform block
13619+ for (size_t ub_index = 0; ub_index < SG_MAX_UNIFORMBLOCK_BINDSLOTS; ub_index++) {
13620+ const sg_shader_stage stage = desc->uniform_blocks[ub_index].stage;
13621+ if (stage == SG_SHADERSTAGE_NONE) {
13622+ continue;
13623+ }
13624+ const _sg_shader_uniform_block_t* ub = &shd->cmn.uniform_blocks[ub_index];
13625+ ID3D11Buffer* cbuf = 0;
13626+ _SG_STRUCT(D3D11_BUFFER_DESC, cb_desc);
13627+ cb_desc.ByteWidth = (UINT)_sg_roundup((int)ub->size, 16);
13628+ cb_desc.Usage = D3D11_USAGE_DEFAULT;
13629+ cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
13630+ hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &cbuf);
13631+ if (!(SUCCEEDED(hr) && cbuf)) {
13632+ _SG_ERROR(D3D11_CREATE_CONSTANT_BUFFER_FAILED);
13633+ return SG_RESOURCESTATE_FAILED;
13634+ }
13635+ _sg_d3d11_setlabel(cbuf, desc->label);
13636+ shd->d3d11.all_cbufs[ub_index] = cbuf;
13637+
13638+ const uint8_t d3d11_slot = shd->d3d11.ub_register_b_n[ub_index];
13639+ SOKOL_ASSERT(d3d11_slot < _SG_D3D11_MAX_STAGE_UB_BINDINGS);
13640+ if (stage == SG_SHADERSTAGE_VERTEX) {
13641+ SOKOL_ASSERT(0 == shd->d3d11.vs_cbufs[d3d11_slot]);
13642+ shd->d3d11.vs_cbufs[d3d11_slot] = cbuf;
13643+ } else if (stage == SG_SHADERSTAGE_FRAGMENT) {
13644+ SOKOL_ASSERT(0 == shd->d3d11.fs_cbufs[d3d11_slot]);
13645+ shd->d3d11.fs_cbufs[d3d11_slot] = cbuf;
13646+ } else if (stage == SG_SHADERSTAGE_COMPUTE) {
13647+ SOKOL_ASSERT(0 == shd->d3d11.cs_cbufs[d3d11_slot]);
13648+ shd->d3d11.cs_cbufs[d3d11_slot] = cbuf;
13649+ } else {
13650+ SOKOL_UNREACHABLE;
13651+ }
13652+ }
13653+
13654+ // create shader functions
13655+ const bool has_vs = desc->vertex_func.bytecode.ptr || desc->vertex_func.source;
13656+ const bool has_fs = desc->fragment_func.bytecode.ptr || desc->fragment_func.source;
13657+ const bool has_cs = desc->compute_func.bytecode.ptr || desc->compute_func.source;
13658+ bool vs_valid = false; bool fs_valid = false; bool cs_valid = false;
13659+ if (has_vs) {
13660+ const void* vs_ptr = 0; SIZE_T vs_length = 0;
13661+ ID3DBlob* vs_blob = 0;
13662+ if (desc->vertex_func.bytecode.ptr) {
13663+ SOKOL_ASSERT(desc->vertex_func.bytecode.size > 0);
13664+ vs_ptr = desc->vertex_func.bytecode.ptr;
13665+ vs_length = desc->vertex_func.bytecode.size;
13666+ } else {
13667+ SOKOL_ASSERT(desc->vertex_func.source);
13668+ vs_blob = _sg_d3d11_compile_shader(&desc->vertex_func);
13669+ if (vs_blob) {
13670+ vs_ptr = _sg_d3d11_GetBufferPointer(vs_blob);
13671+ vs_length = _sg_d3d11_GetBufferSize(vs_blob);
13672+ }
13673+ }
13674+ if (vs_ptr && (vs_length > 0)) {
13675+ hr = _sg_d3d11_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11.vs);
13676+ vs_valid = SUCCEEDED(hr) && shd->d3d11.vs;
13677+ }
13678+ // set label, and need to store a copy of the vertex shader blob for the pipeline creation
13679+ if (vs_valid) {
13680+ _sg_d3d11_setlabel(shd->d3d11.vs, desc->label);
13681+ shd->d3d11.vs_blob_length = vs_length;
13682+ shd->d3d11.vs_blob = _sg_malloc((size_t)vs_length);
13683+ SOKOL_ASSERT(shd->d3d11.vs_blob);
13684+ memcpy(shd->d3d11.vs_blob, vs_ptr, vs_length);
13685+ }
13686+ if (vs_blob) {
13687+ _sg_d3d11_Release(vs_blob);
13688+ }
13689+ }
13690+ if (has_fs) {
13691+ const void* fs_ptr = 0; SIZE_T fs_length = 0;
13692+ ID3DBlob* fs_blob = 0;
13693+ if (desc->fragment_func.bytecode.ptr) {
13694+ SOKOL_ASSERT(desc->fragment_func.bytecode.size > 0);
13695+ fs_ptr = desc->fragment_func.bytecode.ptr;
13696+ fs_length = desc->fragment_func.bytecode.size;
13697+ } else {
13698+ SOKOL_ASSERT(desc->fragment_func.source);
13699+ fs_blob = _sg_d3d11_compile_shader(&desc->fragment_func);
13700+ if (fs_blob) {
13701+ fs_ptr = _sg_d3d11_GetBufferPointer(fs_blob);
13702+ fs_length = _sg_d3d11_GetBufferSize(fs_blob);
13703+ }
13704+ }
13705+ if (fs_ptr && (fs_length > 0)) {
13706+ hr = _sg_d3d11_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11.fs);
13707+ fs_valid = SUCCEEDED(hr) && shd->d3d11.fs;
13708+ }
13709+ if (fs_valid) {
13710+ _sg_d3d11_setlabel(shd->d3d11.fs, desc->label);
13711+ }
13712+ if (fs_blob) {
13713+ _sg_d3d11_Release(fs_blob);
13714+ }
13715+ }
13716+ if (has_cs) {
13717+ const void* cs_ptr = 0; SIZE_T cs_length = 0;
13718+ ID3DBlob* cs_blob = 0;
13719+ if (desc->compute_func.bytecode.ptr) {
13720+ SOKOL_ASSERT(desc->compute_func.bytecode.size > 0);
13721+ cs_ptr = desc->compute_func.bytecode.ptr;
13722+ cs_length = desc->compute_func.bytecode.size;
13723+ } else {
13724+ SOKOL_ASSERT(desc->compute_func.source);
13725+ cs_blob = _sg_d3d11_compile_shader(&desc->compute_func);
13726+ if (cs_blob) {
13727+ cs_ptr = _sg_d3d11_GetBufferPointer(cs_blob);
13728+ cs_length = _sg_d3d11_GetBufferSize(cs_blob);
13729+ }
13730+ }
13731+ if (cs_ptr && (cs_length > 0)) {
13732+ hr = _sg_d3d11_CreateComputeShader(_sg.d3d11.dev, cs_ptr, cs_length, NULL, &shd->d3d11.cs);
13733+ cs_valid = SUCCEEDED(hr) && shd->d3d11.cs;
13734+ }
13735+ if (cs_blob) {
13736+ _sg_d3d11_Release(cs_blob);
13737+ }
13738+ }
13739+ if ((vs_valid && fs_valid) || cs_valid) {
13740+ return SG_RESOURCESTATE_VALID;
13741+ } else {
13742+ return SG_RESOURCESTATE_FAILED;
13743+ }
13744+}
13745+
13746+_SOKOL_PRIVATE void _sg_d3d11_discard_shader(_sg_shader_t* shd) {
13747+ SOKOL_ASSERT(shd);
13748+ if (shd->d3d11.vs) {
13749+ _sg_d3d11_Release(shd->d3d11.vs);
13750+ }
13751+ if (shd->d3d11.fs) {
13752+ _sg_d3d11_Release(shd->d3d11.fs);
13753+ }
13754+ if (shd->d3d11.cs) {
13755+ _sg_d3d11_Release(shd->d3d11.cs);
13756+ }
13757+ if (shd->d3d11.vs_blob) {
13758+ _sg_free(shd->d3d11.vs_blob);
13759+ }
13760+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
13761+ if (shd->d3d11.all_cbufs[i]) {
13762+ _sg_d3d11_Release(shd->d3d11.all_cbufs[i]);
13763+ }
13764+ }
13765+}
13766+
13767+_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) {
13768+ SOKOL_ASSERT(pip && desc);
13769+ _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
13770+
13771+ // if this is a compute pipeline, we're done here
13772+ if (pip->cmn.is_compute) {
13773+ return SG_RESOURCESTATE_VALID;
13774+ }
13775+
13776+ // a render pipeline...
13777+ SOKOL_ASSERT(shd->d3d11.vs_blob && shd->d3d11.vs_blob_length > 0);
13778+ SOKOL_ASSERT(!pip->d3d11.il && !pip->d3d11.rs && !pip->d3d11.dss && !pip->d3d11.bs);
13779+
13780+ pip->d3d11.index_format = _sg_d3d11_index_format(pip->cmn.index_type);
13781+ pip->d3d11.topology = _sg_d3d11_primitive_topology(desc->primitive_type);
13782+ pip->d3d11.stencil_ref = desc->stencil.ref;
13783+
13784+ // create input layout object
13785+ HRESULT hr;
13786+ _SG_STRUCT(D3D11_INPUT_ELEMENT_DESC, d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]);
13787+ size_t attr_index = 0;
13788+ for (; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) {
13789+ const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index];
13790+ if (a_state->format == SG_VERTEXFORMAT_INVALID) {
13791+ break;
13792+ }
13793+ SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS);
13794+ SOKOL_ASSERT(pip->cmn.vertex_buffer_layout_active[a_state->buffer_index]);
13795+ const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index];
13796+ const sg_vertex_step step_func = l_state->step_func;
13797+ const int step_rate = l_state->step_rate;
13798+ D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index];
13799+ d3d11_comp->SemanticName = _sg_strptr(&shd->d3d11.attrs[attr_index].sem_name);
13800+ d3d11_comp->SemanticIndex = (UINT)shd->d3d11.attrs[attr_index].sem_index;
13801+ d3d11_comp->Format = _sg_d3d11_vertex_format(a_state->format);
13802+ d3d11_comp->InputSlot = (UINT)a_state->buffer_index;
13803+ d3d11_comp->AlignedByteOffset = (UINT)a_state->offset;
13804+ d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func);
13805+ if (SG_VERTEXSTEP_PER_INSTANCE == step_func) {
13806+ d3d11_comp->InstanceDataStepRate = (UINT)step_rate;
13807+ }
13808+ }
13809+ for (size_t layout_index = 0; layout_index < SG_MAX_VERTEXBUFFER_BINDSLOTS; layout_index++) {
13810+ if (pip->cmn.vertex_buffer_layout_active[layout_index]) {
13811+ const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index];
13812+ SOKOL_ASSERT(l_state->stride > 0);
13813+ pip->d3d11.vb_strides[layout_index] = (UINT)l_state->stride;
13814+ } else {
13815+ pip->d3d11.vb_strides[layout_index] = 0;
13816+ }
13817+ }
13818+ if (attr_index > 0) {
13819+ hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev,
13820+ d3d11_comps, // pInputElementDesc
13821+ (UINT)attr_index, // NumElements
13822+ shd->d3d11.vs_blob, // pShaderByteCodeWithInputSignature
13823+ shd->d3d11.vs_blob_length, // BytecodeLength
13824+ &pip->d3d11.il);
13825+ if (!(SUCCEEDED(hr) && pip->d3d11.il)) {
13826+ _SG_ERROR(D3D11_CREATE_INPUT_LAYOUT_FAILED);
13827+ return SG_RESOURCESTATE_FAILED;
13828+ }
13829+ _sg_d3d11_setlabel(pip->d3d11.il, desc->label);
13830+ }
13831+
13832+ // create rasterizer state
13833+ _SG_STRUCT(D3D11_RASTERIZER_DESC, rs_desc);
13834+ rs_desc.FillMode = D3D11_FILL_SOLID;
13835+ rs_desc.CullMode = _sg_d3d11_cull_mode(desc->cull_mode);
13836+ rs_desc.FrontCounterClockwise = desc->face_winding == SG_FACEWINDING_CCW;
13837+ rs_desc.DepthBias = (INT) pip->cmn.depth.bias;
13838+ rs_desc.DepthBiasClamp = pip->cmn.depth.bias_clamp;
13839+ rs_desc.SlopeScaledDepthBias = pip->cmn.depth.bias_slope_scale;
13840+ rs_desc.DepthClipEnable = TRUE;
13841+ rs_desc.ScissorEnable = TRUE;
13842+ rs_desc.MultisampleEnable = desc->sample_count > 1;
13843+ rs_desc.AntialiasedLineEnable = FALSE;
13844+ hr = _sg_d3d11_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11.rs);
13845+ if (!(SUCCEEDED(hr) && pip->d3d11.rs)) {
13846+ _SG_ERROR(D3D11_CREATE_RASTERIZER_STATE_FAILED);
13847+ return SG_RESOURCESTATE_FAILED;
13848+ }
13849+ _sg_d3d11_setlabel(pip->d3d11.rs, desc->label);
13850+
13851+ // create depth-stencil state
13852+ _SG_STRUCT(D3D11_DEPTH_STENCIL_DESC, dss_desc);
13853+ dss_desc.DepthEnable = TRUE;
13854+ dss_desc.DepthWriteMask = desc->depth.write_enabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
13855+ dss_desc.DepthFunc = _sg_d3d11_compare_func(desc->depth.compare);
13856+ dss_desc.StencilEnable = desc->stencil.enabled;
13857+ dss_desc.StencilReadMask = desc->stencil.read_mask;
13858+ dss_desc.StencilWriteMask = desc->stencil.write_mask;
13859+ const sg_stencil_face_state* sf = &desc->stencil.front;
13860+ dss_desc.FrontFace.StencilFailOp = _sg_d3d11_stencil_op(sf->fail_op);
13861+ dss_desc.FrontFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sf->depth_fail_op);
13862+ dss_desc.FrontFace.StencilPassOp = _sg_d3d11_stencil_op(sf->pass_op);
13863+ dss_desc.FrontFace.StencilFunc = _sg_d3d11_compare_func(sf->compare);
13864+ const sg_stencil_face_state* sb = &desc->stencil.back;
13865+ dss_desc.BackFace.StencilFailOp = _sg_d3d11_stencil_op(sb->fail_op);
13866+ dss_desc.BackFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sb->depth_fail_op);
13867+ dss_desc.BackFace.StencilPassOp = _sg_d3d11_stencil_op(sb->pass_op);
13868+ dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare);
13869+ hr = _sg_d3d11_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11.dss);
13870+ if (!(SUCCEEDED(hr) && pip->d3d11.dss)) {
13871+ _SG_ERROR(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED);
13872+ return SG_RESOURCESTATE_FAILED;
13873+ }
13874+ _sg_d3d11_setlabel(pip->d3d11.dss, desc->label);
13875+
13876+ // create blend state
13877+ _SG_STRUCT(D3D11_BLEND_DESC, bs_desc);
13878+ bs_desc.AlphaToCoverageEnable = desc->alpha_to_coverage_enabled;
13879+ bs_desc.IndependentBlendEnable = TRUE;
13880+ {
13881+ size_t i = 0;
13882+ for (i = 0; i < (size_t)desc->color_count; i++) {
13883+ const sg_blend_state* src = &desc->colors[i].blend;
13884+ D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i];
13885+ dst->BlendEnable = src->enabled;
13886+ dst->SrcBlend = _sg_d3d11_blend_factor(src->src_factor_rgb);
13887+ dst->DestBlend = _sg_d3d11_blend_factor(src->dst_factor_rgb);
13888+ dst->BlendOp = _sg_d3d11_blend_op(src->op_rgb);
13889+ dst->SrcBlendAlpha = _sg_d3d11_blend_factor(src->src_factor_alpha);
13890+ dst->DestBlendAlpha = _sg_d3d11_blend_factor(src->dst_factor_alpha);
13891+ dst->BlendOpAlpha = _sg_d3d11_blend_op(src->op_alpha);
13892+ dst->RenderTargetWriteMask = _sg_d3d11_color_write_mask(desc->colors[i].write_mask);
13893+ }
13894+ for (; i < 8; i++) {
13895+ D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i];
13896+ dst->BlendEnable = FALSE;
13897+ dst->SrcBlend = dst->SrcBlendAlpha = D3D11_BLEND_ONE;
13898+ dst->DestBlend = dst->DestBlendAlpha = D3D11_BLEND_ZERO;
13899+ dst->BlendOp = dst->BlendOpAlpha = D3D11_BLEND_OP_ADD;
13900+ dst->RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
13901+ }
13902+ }
13903+ hr = _sg_d3d11_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs);
13904+ if (!(SUCCEEDED(hr) && pip->d3d11.bs)) {
13905+ _SG_ERROR(D3D11_CREATE_BLEND_STATE_FAILED);
13906+ return SG_RESOURCESTATE_FAILED;
13907+ }
13908+ _sg_d3d11_setlabel(pip->d3d11.bs, desc->label);
13909+ return SG_RESOURCESTATE_VALID;
13910+}
13911+
13912+_SOKOL_PRIVATE void _sg_d3d11_discard_pipeline(_sg_pipeline_t* pip) {
13913+ SOKOL_ASSERT(pip);
13914+ if (pip->d3d11.il) {
13915+ _sg_d3d11_Release(pip->d3d11.il);
13916+ }
13917+ if (pip->d3d11.rs) {
13918+ _sg_d3d11_Release(pip->d3d11.rs);
13919+ }
13920+ if (pip->d3d11.dss) {
13921+ _sg_d3d11_Release(pip->d3d11.dss);
13922+ }
13923+ if (pip->d3d11.bs) {
13924+ _sg_d3d11_Release(pip->d3d11.bs);
13925+ }
13926+}
13927+
13928+_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_view(_sg_view_t* view, const sg_view_desc* desc) {
13929+ SOKOL_ASSERT(view && desc);
13930+ _SOKOL_UNUSED(desc);
13931+ HRESULT hr;
13932+ if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) {
13933+ const _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref);
13934+ SOKOL_ASSERT(buf->d3d11.buf);
13935+ const UINT size = (UINT) buf->cmn.size;
13936+ SOKOL_ASSERT(_sg_multiple_u64(size, 4));
13937+ const UINT offset = (UINT) view->cmn.buf.offset;
13938+ SOKOL_ASSERT(_sg_multiple_u64(offset, 4));
13939+ SOKOL_ASSERT(offset < size);
13940+ const UINT first_element = offset / 4;
13941+ const UINT num_elements = (size - offset) / 4;
13942+ _SG_STRUCT(D3D11_SHADER_RESOURCE_VIEW_DESC, d3d11_srv_desc);
13943+ d3d11_srv_desc.Format = DXGI_FORMAT_R32_TYPELESS;
13944+ d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
13945+ d3d11_srv_desc.BufferEx.FirstElement = first_element;
13946+ d3d11_srv_desc.BufferEx.NumElements = num_elements;
13947+ d3d11_srv_desc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW;
13948+ SOKOL_ASSERT(!view->d3d11.srv);
13949+ hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)buf->d3d11.buf, &d3d11_srv_desc, &view->d3d11.srv);
13950+ if (!(SUCCEEDED(hr) && view->d3d11.srv)) {
13951+ _SG_ERROR(D3D11_CREATE_BUFFER_SRV_FAILED);
13952+ return SG_RESOURCESTATE_FAILED;
13953+ }
13954+ _sg_d3d11_setlabel(view->d3d11.srv, desc->label);
13955+ if (buf->cmn.usage.immutable) {
13956+ _SG_STRUCT(D3D11_UNORDERED_ACCESS_VIEW_DESC, d3d11_uav_desc);
13957+ d3d11_uav_desc.Format = DXGI_FORMAT_R32_TYPELESS;
13958+ d3d11_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
13959+ d3d11_uav_desc.Buffer.FirstElement = first_element;
13960+ d3d11_uav_desc.Buffer.NumElements = num_elements;
13961+ d3d11_uav_desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
13962+ SOKOL_ASSERT(!view->d3d11.uav);
13963+ hr = _sg_d3d11_CreateUnorderedAccessView(_sg.d3d11.dev, (ID3D11Resource*)buf->d3d11.buf, &d3d11_uav_desc, &view->d3d11.uav);
13964+ if (!(SUCCEEDED(hr) && view->d3d11.uav)) {
13965+ _SG_ERROR(D3D11_CREATE_BUFFER_UAV_FAILED);
13966+ return SG_RESOURCESTATE_FAILED;
13967+ }
13968+ _sg_d3d11_setlabel(view->d3d11.uav, desc->label);
13969+ }
13970+ } else {
13971+ // it's an image view
13972+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
13973+ SOKOL_ASSERT(img->d3d11.res);
13974+ const bool msaa = img->cmn.sample_count > 1;
13975+ SOKOL_ASSERT(view->cmn.img.mip_level_count >= 1);
13976+ SOKOL_ASSERT(view->cmn.img.slice_count >= 1);
13977+ const UINT mip_level = (UINT)view->cmn.img.mip_level;
13978+ const UINT mip_count = (UINT)view->cmn.img.mip_level_count;
13979+ const UINT slice = (UINT)view->cmn.img.slice;
13980+ const UINT slice_count = (UINT)view->cmn.img.slice_count;
13981+
13982+ if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) {
13983+ SOKOL_ASSERT(!msaa);
13984+ _SG_STRUCT(D3D11_UNORDERED_ACCESS_VIEW_DESC, d3d11_uav_desc);
13985+ d3d11_uav_desc.Format = _sg_d3d11_rtv_uav_pixel_format(img->cmn.pixel_format);
13986+ switch (img->cmn.type) {
13987+ case SG_IMAGETYPE_2D:
13988+ d3d11_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
13989+ d3d11_uav_desc.Texture2D.MipSlice = mip_level;
13990+ break;
13991+ case SG_IMAGETYPE_CUBE:
13992+ case SG_IMAGETYPE_ARRAY:
13993+ d3d11_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
13994+ d3d11_uav_desc.Texture2DArray.MipSlice = mip_level;
13995+ d3d11_uav_desc.Texture2DArray.FirstArraySlice = slice;
13996+ d3d11_uav_desc.Texture2DArray.ArraySize = 1;
13997+ break;
13998+ case SG_IMAGETYPE_3D:
13999+ d3d11_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
14000+ d3d11_uav_desc.Texture3D.MipSlice = mip_level;
14001+ d3d11_uav_desc.Texture3D.FirstWSlice = slice;
14002+ d3d11_uav_desc.Texture3D.WSize = 1;
14003+ break;
14004+ default: SOKOL_UNREACHABLE; break;
14005+ }
14006+ hr = _sg_d3d11_CreateUnorderedAccessView(_sg.d3d11.dev, img->d3d11.res, &d3d11_uav_desc, &view->d3d11.uav);
14007+ if (!(SUCCEEDED(hr) && view->d3d11.uav)) {
14008+ _SG_ERROR(D3D11_CREATE_UAV_FAILED);
14009+ return SG_RESOURCESTATE_FAILED;
14010+ }
14011+ _sg_d3d11_setlabel(view->d3d11.uav, desc->label);
14012+
14013+ } else if (view->cmn.type == SG_VIEWTYPE_TEXTURE) {
14014+
14015+ _SG_STRUCT(D3D11_SHADER_RESOURCE_VIEW_DESC, d3d11_srv_desc);
14016+ d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format);
14017+ switch (img->cmn.type) {
14018+ case SG_IMAGETYPE_2D:
14019+ if (msaa) {
14020+ d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
14021+ } else {
14022+ d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
14023+ d3d11_srv_desc.Texture2D.MostDetailedMip = mip_level;
14024+ d3d11_srv_desc.Texture2D.MipLevels = mip_count;
14025+ }
14026+ break;
14027+ case SG_IMAGETYPE_CUBE:
14028+ SOKOL_ASSERT(!msaa);
14029+ d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
14030+ d3d11_srv_desc.TextureCube.MostDetailedMip = mip_level;
14031+ d3d11_srv_desc.TextureCube.MipLevels = mip_count;
14032+ break;
14033+ case SG_IMAGETYPE_ARRAY:
14034+ if (msaa) {
14035+ // NOTE: _sg_validate_image_desc() currently disallows MSAA array textures
14036+ d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY;
14037+ d3d11_srv_desc.Texture2DMSArray.FirstArraySlice = slice;
14038+ d3d11_srv_desc.Texture2DMSArray.ArraySize = slice_count;
14039+ } else {
14040+ d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
14041+ d3d11_srv_desc.Texture2DArray.MostDetailedMip = mip_level;
14042+ d3d11_srv_desc.Texture2DArray.MipLevels = mip_count;
14043+ d3d11_srv_desc.Texture2DArray.FirstArraySlice = slice;
14044+ d3d11_srv_desc.Texture2DArray.ArraySize = slice_count;
14045+ }
14046+ break;
14047+ case SG_IMAGETYPE_3D:
14048+ SOKOL_ASSERT(!msaa);
14049+ d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
14050+ d3d11_srv_desc.Texture3D.MostDetailedMip = mip_level;
14051+ d3d11_srv_desc.Texture3D.MipLevels = mip_count;
14052+ break;
14053+ default:
14054+ SOKOL_UNREACHABLE; break;
14055+ }
14056+ hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, img->d3d11.res, &d3d11_srv_desc, &view->d3d11.srv);
14057+ if (!(SUCCEEDED(hr) && view->d3d11.srv)) {
14058+ _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED);
14059+ return SG_RESOURCESTATE_FAILED;
14060+ }
14061+ _sg_d3d11_setlabel(view->d3d11.srv, desc->label);
14062+
14063+ } else if (view->cmn.type == SG_VIEWTYPE_COLORATTACHMENT) {
14064+
14065+ _SG_STRUCT(D3D11_RENDER_TARGET_VIEW_DESC, d3d11_rtv_desc);
14066+ d3d11_rtv_desc.Format = _sg_d3d11_rtv_uav_pixel_format(img->cmn.pixel_format);
14067+ switch (img->cmn.type) {
14068+ case SG_IMAGETYPE_2D:
14069+ if (msaa) {
14070+ d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
14071+ } else {
14072+ d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
14073+ d3d11_rtv_desc.Texture2D.MipSlice = mip_level;
14074+ }
14075+ break;
14076+ case SG_IMAGETYPE_CUBE:
14077+ case SG_IMAGETYPE_ARRAY:
14078+ if (msaa) {
14079+ d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
14080+ d3d11_rtv_desc.Texture2DMSArray.FirstArraySlice = slice;
14081+ d3d11_rtv_desc.Texture2DMSArray.ArraySize = 1;
14082+ } else {
14083+ d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
14084+ d3d11_rtv_desc.Texture2DArray.MipSlice = mip_level;
14085+ d3d11_rtv_desc.Texture2DArray.FirstArraySlice = slice;
14086+ d3d11_rtv_desc.Texture2DArray.ArraySize = 1;
14087+ }
14088+ break;
14089+ case SG_IMAGETYPE_3D:
14090+ SOKOL_ASSERT(!msaa);
14091+ d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
14092+ d3d11_rtv_desc.Texture3D.MipSlice = mip_level;
14093+ d3d11_rtv_desc.Texture3D.FirstWSlice = slice;
14094+ d3d11_rtv_desc.Texture3D.WSize = 1;
14095+ break;
14096+ default: SOKOL_UNREACHABLE; break;
14097+ }
14098+ hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, img->d3d11.res, &d3d11_rtv_desc, &view->d3d11.rtv);
14099+ if (!(SUCCEEDED(hr) && view->d3d11.rtv)) {
14100+ _SG_ERROR(D3D11_CREATE_RTV_FAILED);
14101+ return SG_RESOURCESTATE_FAILED;
14102+ }
14103+ _sg_d3d11_setlabel(view->d3d11.rtv, desc->label);
14104+
14105+ } else if (view->cmn.type == SG_VIEWTYPE_DEPTHSTENCILATTACHMENT) {
14106+
14107+ SOKOL_ASSERT(img->cmn.type != SG_IMAGETYPE_3D);
14108+ _SG_STRUCT(D3D11_DEPTH_STENCIL_VIEW_DESC, d3d11_dsv_desc);
14109+ d3d11_dsv_desc.Format = _sg_d3d11_dsv_pixel_format(img->cmn.pixel_format);
14110+ switch (img->cmn.type) {
14111+ case SG_IMAGETYPE_2D:
14112+ if (msaa) {
14113+ d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
14114+ } else {
14115+ d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
14116+ d3d11_dsv_desc.Texture2D.MipSlice = mip_level;
14117+ }
14118+ break;
14119+ case SG_IMAGETYPE_CUBE:
14120+ case SG_IMAGETYPE_ARRAY:
14121+ if (msaa) {
14122+ d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY;
14123+ d3d11_dsv_desc.Texture2DMSArray.FirstArraySlice = slice;
14124+ d3d11_dsv_desc.Texture2DMSArray.ArraySize = 1;
14125+ } else {
14126+ d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
14127+ d3d11_dsv_desc.Texture2DArray.MipSlice = mip_level;
14128+ d3d11_dsv_desc.Texture2DArray.FirstArraySlice = slice;
14129+ d3d11_dsv_desc.Texture2DArray.ArraySize = 1;
14130+ }
14131+ break;
14132+ default: SOKOL_UNREACHABLE; break;
14133+ }
14134+ hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, img->d3d11.res, &d3d11_dsv_desc, &view->d3d11.dsv);
14135+ if (!(SUCCEEDED(hr) && view->d3d11.dsv)) {
14136+ _SG_ERROR(D3D11_CREATE_DSV_FAILED);
14137+ return SG_RESOURCESTATE_FAILED;
14138+ }
14139+ _sg_d3d11_setlabel(view->d3d11.dsv, desc->label);
14140+ }
14141+ }
14142+ return SG_RESOURCESTATE_VALID;
14143+}
14144+
14145+_SOKOL_PRIVATE void _sg_d3d11_discard_view(_sg_view_t* view) {
14146+ SOKOL_ASSERT(view);
14147+ if (view->d3d11.srv) {
14148+ _sg_d3d11_Release(view->d3d11.srv);
14149+ }
14150+ if (view->d3d11.uav) {
14151+ _sg_d3d11_Release(view->d3d11.uav);
14152+ }
14153+ if (view->d3d11.rtv) {
14154+ _sg_d3d11_Release(view->d3d11.rtv);
14155+ }
14156+ if (view->d3d11.dsv) {
14157+ _sg_d3d11_Release(view->d3d11.dsv);
14158+ }
14159+}
14160+
14161+_SOKOL_PRIVATE void _sg_d3d11_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) {
14162+ SOKOL_ASSERT(_sg.d3d11.ctx && pass && atts);
14163+ if (_sg.cur_pass.is_compute) {
14164+ // nothing to do in compute passes
14165+ return;
14166+ }
14167+ int num_rtvs = 0;
14168+ ID3D11RenderTargetView* rtvs[SG_MAX_COLOR_ATTACHMENTS] = { 0 };
14169+ ID3D11DepthStencilView* dsv = 0;
14170+ _sg.d3d11.cur_swapchain.render_view = 0;
14171+ _sg.d3d11.cur_swapchain.resolve_view = 0;
14172+ if (!atts->empty) {
14173+ SOKOL_ASSERT(atts->num_color_views <= SG_MAX_COLOR_ATTACHMENTS);
14174+ num_rtvs = atts->num_color_views;
14175+ for (int i = 0; i < num_rtvs; i++) {
14176+ SOKOL_ASSERT(atts->color_views[i]);
14177+ SOKOL_ASSERT(atts->color_views[i]->d3d11.rtv);
14178+ rtvs[i] = atts->color_views[i]->d3d11.rtv;
14179+ }
14180+ if (atts->ds_view) {
14181+ SOKOL_ASSERT(atts->ds_view->d3d11.dsv);
14182+ dsv = atts->ds_view->d3d11.dsv;
14183+ }
14184+ } else {
14185+ // NOTE: swapchain depth-stencil-view is optional
14186+ const sg_swapchain* swapchain = &pass->swapchain;
14187+ SOKOL_ASSERT(swapchain->d3d11.render_view);
14188+ num_rtvs = 1;
14189+ rtvs[0] = (ID3D11RenderTargetView*) swapchain->d3d11.render_view;
14190+ dsv = (ID3D11DepthStencilView*) swapchain->d3d11.depth_stencil_view;
14191+ _sg.d3d11.cur_swapchain.render_view = (ID3D11RenderTargetView*) swapchain->d3d11.render_view;
14192+ _sg.d3d11.cur_swapchain.resolve_view = (ID3D11RenderTargetView*) swapchain->d3d11.resolve_view;
14193+ }
14194+ // apply the render-target- and depth-stencil-views
14195+ _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, rtvs, dsv);
14196+ _sg_stats_inc(d3d11.pass.num_om_set_render_targets);
14197+
14198+ // set viewport and scissor rect to cover whole screen
14199+ _SG_STRUCT(D3D11_VIEWPORT, vp);
14200+ vp.Width = (FLOAT) _sg.cur_pass.dim.width;
14201+ vp.Height = (FLOAT) _sg.cur_pass.dim.height;
14202+ vp.MaxDepth = 1.0f;
14203+ _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp);
14204+ D3D11_RECT rect;
14205+ rect.left = 0;
14206+ rect.top = 0;
14207+ rect.right = _sg.cur_pass.dim.width;
14208+ rect.bottom = _sg.cur_pass.dim.height;
14209+ _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect);
14210+
14211+ // perform clear action
14212+ const sg_pass_action* action = &pass->action;
14213+ for (size_t i = 0; i < (size_t)num_rtvs; i++) {
14214+ if (action->colors[i].load_action == SG_LOADACTION_CLEAR) {
14215+ _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, rtvs[i], (float*)&action->colors[i].clear_value);
14216+ _sg_stats_inc(d3d11.pass.num_clear_render_target_view);
14217+ }
14218+ }
14219+ UINT ds_flags = 0;
14220+ if (action->depth.load_action == SG_LOADACTION_CLEAR) {
14221+ ds_flags |= D3D11_CLEAR_DEPTH;
14222+ }
14223+ if (action->stencil.load_action == SG_LOADACTION_CLEAR) {
14224+ ds_flags |= D3D11_CLEAR_STENCIL;
14225+ }
14226+ if ((0 != ds_flags) && dsv) {
14227+ _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, dsv, ds_flags, action->depth.clear_value, action->stencil.clear_value);
14228+ _sg_stats_inc(d3d11.pass.num_clear_depth_stencil_view);
14229+ }
14230+}
14231+
14232+// D3D11CalcSubresource only exists for C++
14233+_SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) {
14234+ return mip_slice + array_slice * mip_levels;
14235+}
14236+
14237+_SOKOL_PRIVATE void _sg_d3d11_end_pass(const _sg_attachments_ptrs_t* atts) {
14238+ SOKOL_ASSERT(_sg.d3d11.ctx && atts);
14239+
14240+ if (!_sg.cur_pass.is_compute) {
14241+ // need to resolve MSAA render attachments into texture?
14242+ if (!atts->empty) {
14243+ // ...for offscreen pass...
14244+ for (int i = 0; i < atts->num_color_views; i++) {
14245+ const _sg_view_t* resolve_view = atts->resolve_views[i];
14246+ if (resolve_view) {
14247+ const _sg_image_t* resolve_img = _sg_image_ref_ptr(&resolve_view->cmn.img.ref);
14248+ const _sg_view_t* color_view = atts->color_views[i];
14249+ SOKOL_ASSERT(color_view);
14250+ const _sg_image_t* color_img = _sg_image_ref_ptr(&color_view->cmn.img.ref);
14251+ SOKOL_ASSERT(color_img->cmn.sample_count > 1);
14252+ SOKOL_ASSERT(resolve_img->cmn.sample_count == 1);
14253+ const UINT src_subres = _sg_d3d11_calcsubresource(
14254+ (UINT)color_view->cmn.img.mip_level,
14255+ (UINT)color_view->cmn.img.slice,
14256+ (UINT)color_img->cmn.num_mipmaps);
14257+ const UINT dst_subres = _sg_d3d11_calcsubresource(
14258+ (UINT)resolve_view->cmn.img.mip_level,
14259+ (UINT)resolve_view->cmn.img.slice,
14260+ (UINT)resolve_img->cmn.num_mipmaps);
14261+ _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx,
14262+ resolve_img->d3d11.res,
14263+ dst_subres,
14264+ color_img->d3d11.res,
14265+ src_subres,
14266+ _sg_d3d11_rtv_uav_pixel_format(color_img->cmn.pixel_format));
14267+ _sg_stats_inc(d3d11.pass.num_resolve_subresource);
14268+ }
14269+ }
14270+ } else {
14271+ // ...for swapchain pass...
14272+ if (_sg.d3d11.cur_swapchain.resolve_view) {
14273+ SOKOL_ASSERT(_sg.d3d11.cur_swapchain.render_view);
14274+ SOKOL_ASSERT(_sg.cur_pass.swapchain.sample_count > 1);
14275+ SOKOL_ASSERT(_sg.cur_pass.swapchain.color_fmt > SG_PIXELFORMAT_NONE);
14276+ ID3D11Resource* d3d11_render_res = 0;
14277+ ID3D11Resource* d3d11_resolve_res = 0;
14278+ _sg_d3d11_GetResource((ID3D11View*)_sg.d3d11.cur_swapchain.render_view, &d3d11_render_res);
14279+ _sg_d3d11_GetResource((ID3D11View*)_sg.d3d11.cur_swapchain.resolve_view, &d3d11_resolve_res);
14280+ SOKOL_ASSERT(d3d11_render_res);
14281+ SOKOL_ASSERT(d3d11_resolve_res);
14282+ const sg_pixel_format color_fmt = _sg.cur_pass.swapchain.color_fmt;
14283+ _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, d3d11_resolve_res, 0, d3d11_render_res, 0, _sg_d3d11_rtv_uav_pixel_format(color_fmt));
14284+ _sg_d3d11_Release(d3d11_render_res);
14285+ _sg_d3d11_Release(d3d11_resolve_res);
14286+ _sg_stats_inc(d3d11.pass.num_resolve_subresource);
14287+ }
14288+ }
14289+ }
14290+ _sg.d3d11.cur_swapchain.render_view = 0;
14291+ _sg.d3d11.cur_swapchain.resolve_view = 0;
14292+ _sg_d3d11_clear_state();
14293+}
14294+
14295+_SOKOL_PRIVATE void _sg_d3d11_apply_viewport(int x, int y, int w, int h, bool origin_top_left) {
14296+ SOKOL_ASSERT(_sg.d3d11.ctx);
14297+ D3D11_VIEWPORT vp;
14298+ vp.TopLeftX = (FLOAT) x;
14299+ vp.TopLeftY = (FLOAT) (origin_top_left ? y : (_sg.cur_pass.dim.height - (y + h)));
14300+ vp.Width = (FLOAT) w;
14301+ vp.Height = (FLOAT) h;
14302+ vp.MinDepth = 0.0f;
14303+ vp.MaxDepth = 1.0f;
14304+ _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp);
14305+}
14306+
14307+_SOKOL_PRIVATE void _sg_d3d11_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) {
14308+ SOKOL_ASSERT(_sg.d3d11.ctx);
14309+ D3D11_RECT rect;
14310+ rect.left = x;
14311+ rect.top = (origin_top_left ? y : (_sg.cur_pass.dim.height - (y + h)));
14312+ rect.right = x + w;
14313+ rect.bottom = origin_top_left ? (y + h) : (_sg.cur_pass.dim.height - y);
14314+ _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect);
14315+}
14316+
14317+_SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) {
14318+ SOKOL_ASSERT(pip);
14319+ SOKOL_ASSERT(_sg.d3d11.ctx);
14320+
14321+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
14322+ if (pip->cmn.is_compute) {
14323+ // a compute pipeline
14324+ SOKOL_ASSERT(shd->d3d11.cs);
14325+ _sg_d3d11_CSSetShader(_sg.d3d11.ctx, shd->d3d11.cs, NULL, 0);
14326+ _sg_d3d11_CSSetConstantBuffers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_UB_BINDINGS, shd->d3d11.cs_cbufs);
14327+ _sg_stats_inc(d3d11.pipeline.num_cs_set_shader);
14328+ _sg_stats_inc(d3d11.pipeline.num_cs_set_constant_buffers);
14329+ } else {
14330+ // a render pipeline
14331+ SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss);
14332+ SOKOL_ASSERT(shd->d3d11.vs);
14333+ SOKOL_ASSERT(shd->d3d11.fs);
14334+
14335+ _sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs);
14336+ _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref);
14337+ _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, (float*)&pip->cmn.blend_color, 0xFFFFFFFF);
14338+ _sg_d3d11_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11.topology);
14339+ _sg_d3d11_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11.il);
14340+ _sg_d3d11_VSSetShader(_sg.d3d11.ctx, shd->d3d11.vs, NULL, 0);
14341+ _sg_d3d11_VSSetConstantBuffers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_UB_BINDINGS, shd->d3d11.vs_cbufs);
14342+ _sg_d3d11_PSSetShader(_sg.d3d11.ctx, shd->d3d11.fs, NULL, 0);
14343+ _sg_d3d11_PSSetConstantBuffers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_UB_BINDINGS, shd->d3d11.fs_cbufs);
14344+ _sg_stats_inc(d3d11.pipeline.num_rs_set_state);
14345+ _sg_stats_inc(d3d11.pipeline.num_om_set_depth_stencil_state);
14346+ _sg_stats_inc(d3d11.pipeline.num_om_set_blend_state);
14347+ _sg_stats_inc(d3d11.pipeline.num_ia_set_primitive_topology);
14348+ _sg_stats_inc(d3d11.pipeline.num_ia_set_input_layout);
14349+ _sg_stats_inc(d3d11.pipeline.num_vs_set_shader);
14350+ _sg_stats_inc(d3d11.pipeline.num_vs_set_constant_buffers);
14351+ _sg_stats_inc(d3d11.pipeline.num_ps_set_shader);
14352+ _sg_stats_inc(d3d11.pipeline.num_ps_set_constant_buffers);
14353+ }
14354+}
14355+
14356+_SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_ptrs_t* bnd) {
14357+ SOKOL_ASSERT(bnd);
14358+ SOKOL_ASSERT(bnd->pip);
14359+ SOKOL_ASSERT(_sg.d3d11.ctx);
14360+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader);
14361+ const bool is_compute = bnd->pip->cmn.is_compute;
14362+
14363+ if (is_compute) {
14364+ _sg_clear(&_sg.d3d11.bnd.cs_srvs, sizeof(_sg.d3d11.bnd.cs_srvs));
14365+ _sg_clear(&_sg.d3d11.bnd.cs_uavs, sizeof(_sg.d3d11.bnd.cs_uavs));
14366+ _sg_clear(&_sg.d3d11.bnd.cs_smps, sizeof(_sg.d3d11.bnd.cs_smps));
14367+ } else {
14368+ _sg_clear(&_sg.d3d11.bnd.vbs, sizeof(_sg.d3d11.bnd.vbs));
14369+ _sg_clear(&_sg.d3d11.bnd.vb_offsets, sizeof(_sg.d3d11.bnd.vb_offsets));
14370+ _sg_clear(&_sg.d3d11.bnd.vs_srvs, sizeof(_sg.d3d11.bnd.vs_srvs));
14371+ _sg_clear(&_sg.d3d11.bnd.fs_srvs, sizeof(_sg.d3d11.bnd.fs_srvs));
14372+ _sg_clear(&_sg.d3d11.bnd.vs_smps, sizeof(_sg.d3d11.bnd.vs_smps));
14373+ _sg_clear(&_sg.d3d11.bnd.fs_smps, sizeof(_sg.d3d11.bnd.fs_smps));
14374+ }
14375+
14376+ // gather all the D3D11 resources into arrays
14377+ ID3D11Buffer* d3d11_ib = bnd->ib ? bnd->ib->d3d11.buf : 0;
14378+
14379+ if (is_compute) {
14380+ // on D3D11 we need to break a chicken-egg-situation where a resource
14381+ // may still be set as shader resource view, but is going to be set
14382+ // as unordered-access-view, so first clear all shader resource view bindings
14383+ _sg_d3d11_CSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SRV_BINDINGS, _sg.d3d11.bnd.cs_srvs);
14384+ } else {
14385+ for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) {
14386+ const _sg_buffer_t* vb = bnd->vbs[i];
14387+ if (vb == 0) {
14388+ continue;
14389+ }
14390+ SOKOL_ASSERT(vb->d3d11.buf);
14391+ _sg.d3d11.bnd.vbs[i] = vb->d3d11.buf;
14392+ _sg.d3d11.bnd.vb_offsets[i] = (UINT)bnd->vb_offsets[i];
14393+ }
14394+ }
14395+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
14396+ const _sg_view_t* view = bnd->views[i];
14397+ if (0 == view) {
14398+ continue;
14399+ }
14400+ const _sg_shader_view_t* shd_view = &shd->cmn.views[i];
14401+ const sg_shader_stage stage = shd_view->stage;
14402+ SOKOL_ASSERT((stage == SG_SHADERSTAGE_VERTEX)
14403+ || (stage == SG_SHADERSTAGE_FRAGMENT)
14404+ || (stage == SG_SHADERSTAGE_COMPUTE));
14405+ SOKOL_ASSERT((shd_view->view_type == SG_VIEWTYPE_TEXTURE)
14406+ || (shd_view->view_type == SG_VIEWTYPE_STORAGEBUFFER)
14407+ || (shd_view->view_type == SG_VIEWTYPE_STORAGEIMAGE));
14408+ if (shd_view->view_type == SG_VIEWTYPE_TEXTURE) {
14409+ const uint8_t d3d11_slot = shd->d3d11.view_register_t_n[i];
14410+ SOKOL_ASSERT(d3d11_slot < _SG_D3D11_MAX_STAGE_SRV_BINDINGS);
14411+ ID3D11ShaderResourceView* d3d11_srv = view->d3d11.srv;
14412+ SOKOL_ASSERT(d3d11_srv);
14413+ switch (stage) {
14414+ case SG_SHADERSTAGE_VERTEX: _sg.d3d11.bnd.vs_srvs[d3d11_slot] = d3d11_srv; break;
14415+ case SG_SHADERSTAGE_FRAGMENT: _sg.d3d11.bnd.fs_srvs[d3d11_slot] = d3d11_srv; break;
14416+ case SG_SHADERSTAGE_COMPUTE: _sg.d3d11.bnd.cs_srvs[d3d11_slot] = d3d11_srv; break;
14417+ default: SOKOL_UNREACHABLE;
14418+ }
14419+ } else if (shd_view->view_type == SG_VIEWTYPE_STORAGEBUFFER) {
14420+ if (shd->cmn.views[i].sbuf_readonly) {
14421+ const uint8_t d3d11_slot = shd->d3d11.view_register_t_n[i];
14422+ SOKOL_ASSERT(d3d11_slot < _SG_D3D11_MAX_STAGE_SRV_BINDINGS);
14423+ ID3D11ShaderResourceView* d3d11_srv = view->d3d11.srv;
14424+ SOKOL_ASSERT(d3d11_srv);
14425+ switch (stage) {
14426+ case SG_SHADERSTAGE_VERTEX: _sg.d3d11.bnd.vs_srvs[d3d11_slot] = d3d11_srv; break;
14427+ case SG_SHADERSTAGE_FRAGMENT: _sg.d3d11.bnd.fs_srvs[d3d11_slot] = d3d11_srv; break;
14428+ case SG_SHADERSTAGE_COMPUTE: _sg.d3d11.bnd.cs_srvs[d3d11_slot] = d3d11_srv; break;
14429+ default: SOKOL_UNREACHABLE;
14430+ }
14431+ } else {
14432+ SOKOL_ASSERT(stage == SG_SHADERSTAGE_COMPUTE);
14433+ const uint8_t d3d11_slot = shd->d3d11.view_register_u_n[i];
14434+ SOKOL_ASSERT(d3d11_slot < _sg.limits.d3d11_max_unordered_access_views);
14435+ ID3D11UnorderedAccessView* d3d11_uav = view->d3d11.uav;
14436+ SOKOL_ASSERT(d3d11_uav);
14437+ _sg.d3d11.bnd.cs_uavs[d3d11_slot] = d3d11_uav;
14438+ }
14439+ } else if (shd_view->view_type == SG_VIEWTYPE_STORAGEIMAGE) {
14440+ SOKOL_ASSERT(stage == SG_SHADERSTAGE_COMPUTE);
14441+ const uint8_t d3d11_slot = shd->d3d11.view_register_u_n[i];
14442+ SOKOL_ASSERT(d3d11_slot < _sg.limits.d3d11_max_unordered_access_views);
14443+ ID3D11UnorderedAccessView* d3d11_uav = view->d3d11.uav;
14444+ SOKOL_ASSERT(d3d11_uav);
14445+ _sg.d3d11.bnd.cs_uavs[d3d11_slot] = d3d11_uav;
14446+ } else SOKOL_UNREACHABLE;
14447+ }
14448+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
14449+ const _sg_sampler_t* smp = bnd->smps[i];
14450+ if (smp == 0) {
14451+ continue;
14452+ }
14453+ const sg_shader_stage stage = shd->cmn.samplers[i].stage;
14454+ SOKOL_ASSERT(stage != SG_SHADERSTAGE_NONE);
14455+ const uint8_t d3d11_slot = shd->d3d11.smp_register_s_n[i];
14456+ SOKOL_ASSERT(d3d11_slot < _SG_D3D11_MAX_STAGE_SMP_BINDINGS);
14457+ SOKOL_ASSERT(smp->d3d11.smp);
14458+ ID3D11SamplerState* d3d11_smp = smp->d3d11.smp;
14459+ switch (stage) {
14460+ case SG_SHADERSTAGE_VERTEX: _sg.d3d11.bnd.vs_smps[d3d11_slot] = d3d11_smp; break;
14461+ case SG_SHADERSTAGE_FRAGMENT: _sg.d3d11.bnd.fs_smps[d3d11_slot] = d3d11_smp; break;
14462+ case SG_SHADERSTAGE_COMPUTE: _sg.d3d11.bnd.cs_smps[d3d11_slot] = d3d11_smp; break;
14463+ default: SOKOL_UNREACHABLE;
14464+ }
14465+ }
14466+ if (is_compute) {
14467+ SOKOL_ASSERT(_sg.limits.d3d11_max_unordered_access_views <= _SG_D3D11_MAX_STAGE_UAV_BINDINGS);
14468+ _sg_d3d11_CSSetUnorderedAccessViews(_sg.d3d11.ctx, 0, _sg.limits.d3d11_max_unordered_access_views, _sg.d3d11.bnd.cs_uavs, NULL);
14469+ _sg_d3d11_CSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SRV_BINDINGS, _sg.d3d11.bnd.cs_srvs);
14470+ _sg_d3d11_CSSetSamplers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SMP_BINDINGS, _sg.d3d11.bnd.cs_smps);
14471+ _sg_stats_inc(d3d11.bindings.num_cs_set_shader_resources);
14472+ _sg_stats_inc(d3d11.bindings.num_cs_set_samplers);
14473+ _sg_stats_inc(d3d11.bindings.num_cs_set_unordered_access_views);
14474+ } else {
14475+ _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_VERTEXBUFFER_BINDSLOTS, _sg.d3d11.bnd.vbs, bnd->pip->d3d11.vb_strides, _sg.d3d11.bnd.vb_offsets);
14476+ _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, bnd->pip->d3d11.index_format, (UINT)bnd->ib_offset);
14477+ _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SRV_BINDINGS, _sg.d3d11.bnd.vs_srvs);
14478+ _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SRV_BINDINGS, _sg.d3d11.bnd.fs_srvs);
14479+ _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SMP_BINDINGS, _sg.d3d11.bnd.vs_smps);
14480+ _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_STAGE_SMP_BINDINGS, _sg.d3d11.bnd.fs_smps);
14481+ _sg_stats_inc(d3d11.bindings.num_ia_set_vertex_buffers);
14482+ _sg_stats_inc(d3d11.bindings.num_ia_set_index_buffer);
14483+ _sg_stats_inc(d3d11.bindings.num_vs_set_shader_resources);
14484+ _sg_stats_inc(d3d11.bindings.num_ps_set_shader_resources);
14485+ _sg_stats_inc(d3d11.bindings.num_vs_set_samplers);
14486+ _sg_stats_inc(d3d11.bindings.num_ps_set_samplers);
14487+ }
14488+ return true;
14489+}
14490+
14491+_SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(int ub_slot, const sg_range* data) {
14492+ SOKOL_ASSERT(_sg.d3d11.ctx);
14493+ SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS));
14494+ const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip);
14495+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
14496+ SOKOL_ASSERT(data->size == shd->cmn.uniform_blocks[ub_slot].size);
14497+
14498+ ID3D11Buffer* cbuf = shd->d3d11.all_cbufs[ub_slot];
14499+ SOKOL_ASSERT(cbuf);
14500+ _sg_d3d11_UpdateSubresource(_sg.d3d11.ctx, (ID3D11Resource*)cbuf, 0, NULL, data->ptr, 0, 0);
14501+ _sg_stats_inc(d3d11.uniforms.num_update_subresource);
14502+}
14503+
14504+_SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) {
14505+ const bool use_instanced_draw = (num_instances > 1) || (_sg.use_instanced_draw);
14506+ if (_sg.use_indexed_draw) {
14507+ if (use_instanced_draw) {
14508+ _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx,
14509+ (UINT)num_elements,
14510+ (UINT)num_instances,
14511+ (UINT)base_element,
14512+ base_vertex,
14513+ (UINT)base_instance);
14514+ _sg_stats_inc(d3d11.draw.num_draw_indexed_instanced);
14515+ } else {
14516+ _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, base_vertex);
14517+ _sg_stats_inc(d3d11.draw.num_draw_indexed);
14518+ }
14519+ } else {
14520+ if (use_instanced_draw) {
14521+ _sg_d3d11_DrawInstanced(_sg.d3d11.ctx,
14522+ (UINT)num_elements,
14523+ (UINT)num_instances,
14524+ (UINT)base_element,
14525+ (UINT)base_instance);
14526+ _sg_stats_inc(d3d11.draw.num_draw_instanced);
14527+ } else {
14528+ _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element);
14529+ _sg_stats_inc(d3d11.draw.num_draw);
14530+ }
14531+ }
14532+}
14533+
14534+_SOKOL_PRIVATE void _sg_d3d11_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) {
14535+ _sg_d3d11_Dispatch(_sg.d3d11.ctx, (UINT)num_groups_x, (UINT)num_groups_y, (UINT)num_groups_z);
14536+}
14537+
14538+_SOKOL_PRIVATE void _sg_d3d11_commit(void) {
14539+ // empty
14540+}
14541+
14542+_SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* data) {
14543+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
14544+ SOKOL_ASSERT(_sg.d3d11.ctx);
14545+ SOKOL_ASSERT(buf->d3d11.buf);
14546+ D3D11_MAPPED_SUBRESOURCE d3d11_msr;
14547+ HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr);
14548+ _sg_stats_inc(d3d11.num_map);
14549+ if (SUCCEEDED(hr)) {
14550+ memcpy(d3d11_msr.pData, data->ptr, data->size);
14551+ _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0);
14552+ _sg_stats_inc(d3d11.num_unmap);
14553+ } else {
14554+ _SG_ERROR(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED);
14555+ }
14556+}
14557+
14558+_SOKOL_PRIVATE void _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) {
14559+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
14560+ SOKOL_ASSERT(_sg.d3d11.ctx);
14561+ SOKOL_ASSERT(buf->d3d11.buf);
14562+ D3D11_MAP map_type = new_frame ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE;
14563+ D3D11_MAPPED_SUBRESOURCE d3d11_msr;
14564+ HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, map_type, 0, &d3d11_msr);
14565+ _sg_stats_inc(d3d11.num_map);
14566+ if (SUCCEEDED(hr)) {
14567+ uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->cmn.append_pos;
14568+ memcpy(dst_ptr, data->ptr, data->size);
14569+ _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0);
14570+ _sg_stats_inc(d3d11.num_unmap);
14571+ } else {
14572+ _SG_ERROR(D3D11_MAP_FOR_APPEND_BUFFER_FAILED);
14573+ }
14574+}
14575+
14576+// see: https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-subresources
14577+// also see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11calcsubresource
14578+_SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) {
14579+ SOKOL_ASSERT(img && data);
14580+ SOKOL_ASSERT(_sg.d3d11.ctx);
14581+ SOKOL_ASSERT(img->d3d11.res);
14582+ const int num_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? 1 : img->cmn.num_slices;
14583+ const int num_depth_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? img->cmn.num_slices : 1;
14584+ UINT subres_index = 0;
14585+ HRESULT hr;
14586+ D3D11_MAPPED_SUBRESOURCE d3d11_msr;
14587+ for (int slice_index = 0; slice_index < num_slices; slice_index++) {
14588+ for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) {
14589+ SOKOL_ASSERT(subres_index < _SG_D3D11_MAX_TEXTURE_SUBRESOURCES);
14590+ const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index);
14591+ const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index);
14592+ const int src_row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1);
14593+ const int src_depth_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1);
14594+ const sg_range* miplevel_data = &(data->mip_levels[mip_index]);
14595+ const size_t slice_size = miplevel_data->size / (size_t)num_slices;
14596+ SOKOL_ASSERT(slice_size == (size_t)(src_depth_pitch * num_depth_slices));
14597+ const size_t slice_offset = slice_size * (size_t)slice_index;
14598+ const uint8_t* slice_ptr = ((const uint8_t*)miplevel_data->ptr) + slice_offset;
14599+ hr = _sg_d3d11_Map(_sg.d3d11.ctx, img->d3d11.res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr);
14600+ _sg_stats_inc(d3d11.num_map);
14601+ if (SUCCEEDED(hr)) {
14602+ const uint8_t* src_ptr = slice_ptr;
14603+ uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData;
14604+ for (int depth_index = 0; depth_index < num_depth_slices; depth_index++) {
14605+ if (src_row_pitch == (int)d3d11_msr.RowPitch) {
14606+ const size_t copy_size = slice_size / (size_t)num_depth_slices;
14607+ SOKOL_ASSERT((copy_size * (size_t)num_depth_slices) == slice_size);
14608+ memcpy(dst_ptr, src_ptr, copy_size);
14609+ } else {
14610+ SOKOL_ASSERT(src_row_pitch < (int)d3d11_msr.RowPitch);
14611+ const uint8_t* src_row_ptr = src_ptr;
14612+ uint8_t* dst_row_ptr = dst_ptr;
14613+ for (int row_index = 0; row_index < mip_height; row_index++) {
14614+ memcpy(dst_row_ptr, src_row_ptr, (size_t)src_row_pitch);
14615+ src_row_ptr += src_row_pitch;
14616+ dst_row_ptr += d3d11_msr.RowPitch;
14617+ }
14618+ }
14619+ src_ptr += src_depth_pitch;
14620+ dst_ptr += d3d11_msr.DepthPitch;
14621+ }
14622+ _sg_d3d11_Unmap(_sg.d3d11.ctx, img->d3d11.res, subres_index);
14623+ _sg_stats_inc(d3d11.num_unmap);
14624+ } else {
14625+ _SG_ERROR(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED);
14626+ }
14627+ }
14628+ }
14629+}
14630+
14631+// ███ ███ ███████ ████████ █████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████
14632+// ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██
14633+// ██ ████ ██ █████ ██ ███████ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██
14634+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
14635+// ██ ██ ███████ ██ ██ ██ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████
14636+//
14637+// >>metal backend
14638+#elif defined(SOKOL_METAL)
14639+#pragma clang diagnostic push
14640+// silence Intel Mac deprecations for now
14641+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
14642+
14643+#if __has_feature(objc_arc)
14644+#define _SG_OBJC_RETAIN(obj) { }
14645+#define _SG_OBJC_RELEASE(obj) { obj = nil; }
14646+#else
14647+#define _SG_OBJC_RETAIN(obj) { [obj retain]; }
14648+#define _SG_OBJC_RELEASE(obj) { [obj release]; obj = nil; }
14649+#endif
14650+
14651+//-- enum translation functions ------------------------------------------------
14652+_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_load_action a) {
14653+ switch (a) {
14654+ case SG_LOADACTION_CLEAR: return MTLLoadActionClear;
14655+ case SG_LOADACTION_LOAD: return MTLLoadActionLoad;
14656+ case SG_LOADACTION_DONTCARE: return MTLLoadActionDontCare;
14657+ default: SOKOL_UNREACHABLE; return (MTLLoadAction)0;
14658+ }
14659+}
14660+
14661+_SOKOL_PRIVATE MTLStoreAction _sg_mtl_store_action(sg_store_action a, bool resolve) {
14662+ switch (a) {
14663+ case SG_STOREACTION_STORE:
14664+ if (resolve) {
14665+ return MTLStoreActionStoreAndMultisampleResolve;
14666+ } else {
14667+ return MTLStoreActionStore;
14668+ }
14669+ break;
14670+ case SG_STOREACTION_DONTCARE:
14671+ if (resolve) {
14672+ return MTLStoreActionMultisampleResolve;
14673+ } else {
14674+ return MTLStoreActionDontCare;
14675+ }
14676+ break;
14677+ default: SOKOL_UNREACHABLE; return (MTLStoreAction)0;
14678+ }
14679+}
14680+
14681+_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_resource_options_storage_mode_managed_or_shared(void) {
14682+ #if defined(_SG_TARGET_MACOS)
14683+ if (_sg.mtl.use_shared_storage_mode) {
14684+ return MTLResourceStorageModeShared;
14685+ } else {
14686+ return MTLResourceStorageModeManaged;
14687+ }
14688+ #else
14689+ // MTLResourceStorageModeManaged is not even defined on iOS SDK
14690+ return MTLResourceStorageModeShared;
14691+ #endif
14692+}
14693+
14694+_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_buffer_resource_options(const sg_buffer_usage* usage) {
14695+ if (usage->immutable) {
14696+ return _sg_mtl_resource_options_storage_mode_managed_or_shared();
14697+ } else {
14698+ return MTLResourceCPUCacheModeWriteCombined | _sg_mtl_resource_options_storage_mode_managed_or_shared();
14699+ }
14700+}
14701+
14702+_SOKOL_PRIVATE MTLVertexStepFunction _sg_mtl_step_function(sg_vertex_step step) {
14703+ switch (step) {
14704+ case SG_VERTEXSTEP_PER_VERTEX: return MTLVertexStepFunctionPerVertex;
14705+ case SG_VERTEXSTEP_PER_INSTANCE: return MTLVertexStepFunctionPerInstance;
14706+ default: SOKOL_UNREACHABLE; return (MTLVertexStepFunction)0;
14707+ }
14708+}
14709+
14710+_SOKOL_PRIVATE MTLVertexFormat _sg_mtl_vertex_format(sg_vertex_format fmt) {
14711+ switch (fmt) {
14712+ case SG_VERTEXFORMAT_FLOAT: return MTLVertexFormatFloat;
14713+ case SG_VERTEXFORMAT_FLOAT2: return MTLVertexFormatFloat2;
14714+ case SG_VERTEXFORMAT_FLOAT3: return MTLVertexFormatFloat3;
14715+ case SG_VERTEXFORMAT_FLOAT4: return MTLVertexFormatFloat4;
14716+ case SG_VERTEXFORMAT_INT: return MTLVertexFormatInt;
14717+ case SG_VERTEXFORMAT_INT2: return MTLVertexFormatInt2;
14718+ case SG_VERTEXFORMAT_INT3: return MTLVertexFormatInt3;
14719+ case SG_VERTEXFORMAT_INT4: return MTLVertexFormatInt4;
14720+ case SG_VERTEXFORMAT_UINT: return MTLVertexFormatUInt;
14721+ case SG_VERTEXFORMAT_UINT2: return MTLVertexFormatUInt2;
14722+ case SG_VERTEXFORMAT_UINT3: return MTLVertexFormatUInt3;
14723+ case SG_VERTEXFORMAT_UINT4: return MTLVertexFormatUInt4;
14724+ case SG_VERTEXFORMAT_BYTE4: return MTLVertexFormatChar4;
14725+ case SG_VERTEXFORMAT_BYTE4N: return MTLVertexFormatChar4Normalized;
14726+ case SG_VERTEXFORMAT_UBYTE4: return MTLVertexFormatUChar4;
14727+ case SG_VERTEXFORMAT_UBYTE4N: return MTLVertexFormatUChar4Normalized;
14728+ case SG_VERTEXFORMAT_SHORT2: return MTLVertexFormatShort2;
14729+ case SG_VERTEXFORMAT_SHORT2N: return MTLVertexFormatShort2Normalized;
14730+ case SG_VERTEXFORMAT_USHORT2: return MTLVertexFormatUShort2;
14731+ case SG_VERTEXFORMAT_USHORT2N: return MTLVertexFormatUShort2Normalized;
14732+ case SG_VERTEXFORMAT_SHORT4: return MTLVertexFormatShort4;
14733+ case SG_VERTEXFORMAT_SHORT4N: return MTLVertexFormatShort4Normalized;
14734+ case SG_VERTEXFORMAT_USHORT4: return MTLVertexFormatUShort4;
14735+ case SG_VERTEXFORMAT_USHORT4N: return MTLVertexFormatUShort4Normalized;
14736+ case SG_VERTEXFORMAT_INT10_N2: return MTLVertexFormatInt1010102Normalized;
14737+ case SG_VERTEXFORMAT_UINT10_N2: return MTLVertexFormatUInt1010102Normalized;
14738+ case SG_VERTEXFORMAT_HALF2: return MTLVertexFormatHalf2;
14739+ case SG_VERTEXFORMAT_HALF4: return MTLVertexFormatHalf4;
14740+ default: SOKOL_UNREACHABLE; return (MTLVertexFormat)0;
14741+ }
14742+}
14743+
14744+_SOKOL_PRIVATE MTLPrimitiveType _sg_mtl_primitive_type(sg_primitive_type t) {
14745+ switch (t) {
14746+ case SG_PRIMITIVETYPE_POINTS: return MTLPrimitiveTypePoint;
14747+ case SG_PRIMITIVETYPE_LINES: return MTLPrimitiveTypeLine;
14748+ case SG_PRIMITIVETYPE_LINE_STRIP: return MTLPrimitiveTypeLineStrip;
14749+ case SG_PRIMITIVETYPE_TRIANGLES: return MTLPrimitiveTypeTriangle;
14750+ case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip;
14751+ default: SOKOL_UNREACHABLE; return (MTLPrimitiveType)0;
14752+ }
14753+}
14754+
14755+_SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) {
14756+ switch (fmt) {
14757+ case SG_PIXELFORMAT_R8: return MTLPixelFormatR8Unorm;
14758+ case SG_PIXELFORMAT_R8SN: return MTLPixelFormatR8Snorm;
14759+ case SG_PIXELFORMAT_R8UI: return MTLPixelFormatR8Uint;
14760+ case SG_PIXELFORMAT_R8SI: return MTLPixelFormatR8Sint;
14761+ case SG_PIXELFORMAT_R16: return MTLPixelFormatR16Unorm;
14762+ case SG_PIXELFORMAT_R16SN: return MTLPixelFormatR16Snorm;
14763+ case SG_PIXELFORMAT_R16UI: return MTLPixelFormatR16Uint;
14764+ case SG_PIXELFORMAT_R16SI: return MTLPixelFormatR16Sint;
14765+ case SG_PIXELFORMAT_R16F: return MTLPixelFormatR16Float;
14766+ case SG_PIXELFORMAT_RG8: return MTLPixelFormatRG8Unorm;
14767+ case SG_PIXELFORMAT_RG8SN: return MTLPixelFormatRG8Snorm;
14768+ case SG_PIXELFORMAT_RG8UI: return MTLPixelFormatRG8Uint;
14769+ case SG_PIXELFORMAT_RG8SI: return MTLPixelFormatRG8Sint;
14770+ case SG_PIXELFORMAT_R32UI: return MTLPixelFormatR32Uint;
14771+ case SG_PIXELFORMAT_R32SI: return MTLPixelFormatR32Sint;
14772+ case SG_PIXELFORMAT_R32F: return MTLPixelFormatR32Float;
14773+ case SG_PIXELFORMAT_RG16: return MTLPixelFormatRG16Unorm;
14774+ case SG_PIXELFORMAT_RG16SN: return MTLPixelFormatRG16Snorm;
14775+ case SG_PIXELFORMAT_RG16UI: return MTLPixelFormatRG16Uint;
14776+ case SG_PIXELFORMAT_RG16SI: return MTLPixelFormatRG16Sint;
14777+ case SG_PIXELFORMAT_RG16F: return MTLPixelFormatRG16Float;
14778+ case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatRGBA8Unorm;
14779+ case SG_PIXELFORMAT_SRGB8A8: return MTLPixelFormatRGBA8Unorm_sRGB;
14780+ case SG_PIXELFORMAT_RGBA8SN: return MTLPixelFormatRGBA8Snorm;
14781+ case SG_PIXELFORMAT_RGBA8UI: return MTLPixelFormatRGBA8Uint;
14782+ case SG_PIXELFORMAT_RGBA8SI: return MTLPixelFormatRGBA8Sint;
14783+ case SG_PIXELFORMAT_BGRA8: return MTLPixelFormatBGRA8Unorm;
14784+ case SG_PIXELFORMAT_SBGR8A8: return MTLPixelFormatBGRA8Unorm_sRGB;
14785+ case SG_PIXELFORMAT_RGB10A2: return MTLPixelFormatRGB10A2Unorm;
14786+ case SG_PIXELFORMAT_RG11B10F: return MTLPixelFormatRG11B10Float;
14787+ case SG_PIXELFORMAT_RGB9E5: return MTLPixelFormatRGB9E5Float;
14788+ case SG_PIXELFORMAT_RG32UI: return MTLPixelFormatRG32Uint;
14789+ case SG_PIXELFORMAT_RG32SI: return MTLPixelFormatRG32Sint;
14790+ case SG_PIXELFORMAT_RG32F: return MTLPixelFormatRG32Float;
14791+ case SG_PIXELFORMAT_RGBA16: return MTLPixelFormatRGBA16Unorm;
14792+ case SG_PIXELFORMAT_RGBA16SN: return MTLPixelFormatRGBA16Snorm;
14793+ case SG_PIXELFORMAT_RGBA16UI: return MTLPixelFormatRGBA16Uint;
14794+ case SG_PIXELFORMAT_RGBA16SI: return MTLPixelFormatRGBA16Sint;
14795+ case SG_PIXELFORMAT_RGBA16F: return MTLPixelFormatRGBA16Float;
14796+ case SG_PIXELFORMAT_RGBA32UI: return MTLPixelFormatRGBA32Uint;
14797+ case SG_PIXELFORMAT_RGBA32SI: return MTLPixelFormatRGBA32Sint;
14798+ case SG_PIXELFORMAT_RGBA32F: return MTLPixelFormatRGBA32Float;
14799+ case SG_PIXELFORMAT_DEPTH: return MTLPixelFormatDepth32Float;
14800+ case SG_PIXELFORMAT_DEPTH_STENCIL: return MTLPixelFormatDepth32Float_Stencil8;
14801+ #if defined(_SG_TARGET_MACOS)
14802+ case SG_PIXELFORMAT_BC1_RGBA: return MTLPixelFormatBC1_RGBA;
14803+ case SG_PIXELFORMAT_BC2_RGBA: return MTLPixelFormatBC2_RGBA;
14804+ case SG_PIXELFORMAT_BC3_RGBA: return MTLPixelFormatBC3_RGBA;
14805+ case SG_PIXELFORMAT_BC3_SRGBA: return MTLPixelFormatBC3_RGBA_sRGB;
14806+ case SG_PIXELFORMAT_BC4_R: return MTLPixelFormatBC4_RUnorm;
14807+ case SG_PIXELFORMAT_BC4_RSN: return MTLPixelFormatBC4_RSnorm;
14808+ case SG_PIXELFORMAT_BC5_RG: return MTLPixelFormatBC5_RGUnorm;
14809+ case SG_PIXELFORMAT_BC5_RGSN: return MTLPixelFormatBC5_RGSnorm;
14810+ case SG_PIXELFORMAT_BC6H_RGBF: return MTLPixelFormatBC6H_RGBFloat;
14811+ case SG_PIXELFORMAT_BC6H_RGBUF: return MTLPixelFormatBC6H_RGBUfloat;
14812+ case SG_PIXELFORMAT_BC7_RGBA: return MTLPixelFormatBC7_RGBAUnorm;
14813+ case SG_PIXELFORMAT_BC7_SRGBA: return MTLPixelFormatBC7_RGBAUnorm_sRGB;
14814+ #else
14815+ case SG_PIXELFORMAT_ETC2_RGB8: return MTLPixelFormatETC2_RGB8;
14816+ case SG_PIXELFORMAT_ETC2_SRGB8: return MTLPixelFormatETC2_RGB8_sRGB;
14817+ case SG_PIXELFORMAT_ETC2_RGB8A1: return MTLPixelFormatETC2_RGB8A1;
14818+ case SG_PIXELFORMAT_ETC2_RGBA8: return MTLPixelFormatEAC_RGBA8;
14819+ case SG_PIXELFORMAT_ETC2_SRGB8A8: return MTLPixelFormatEAC_RGBA8_sRGB;
14820+ case SG_PIXELFORMAT_EAC_R11: return MTLPixelFormatEAC_R11Unorm;
14821+ case SG_PIXELFORMAT_EAC_R11SN: return MTLPixelFormatEAC_R11Snorm;
14822+ case SG_PIXELFORMAT_EAC_RG11: return MTLPixelFormatEAC_RG11Unorm;
14823+ case SG_PIXELFORMAT_EAC_RG11SN: return MTLPixelFormatEAC_RG11Snorm;
14824+ case SG_PIXELFORMAT_ASTC_4x4_RGBA: return MTLPixelFormatASTC_4x4_LDR;
14825+ case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return MTLPixelFormatASTC_4x4_sRGB;
14826+ #endif
14827+ default: return MTLPixelFormatInvalid;
14828+ }
14829+}
14830+
14831+_SOKOL_PRIVATE MTLColorWriteMask _sg_mtl_color_write_mask(sg_color_mask m) {
14832+ MTLColorWriteMask mtl_mask = MTLColorWriteMaskNone;
14833+ if (m & SG_COLORMASK_R) {
14834+ mtl_mask |= MTLColorWriteMaskRed;
14835+ }
14836+ if (m & SG_COLORMASK_G) {
14837+ mtl_mask |= MTLColorWriteMaskGreen;
14838+ }
14839+ if (m & SG_COLORMASK_B) {
14840+ mtl_mask |= MTLColorWriteMaskBlue;
14841+ }
14842+ if (m & SG_COLORMASK_A) {
14843+ mtl_mask |= MTLColorWriteMaskAlpha;
14844+ }
14845+ return mtl_mask;
14846+}
14847+
14848+_SOKOL_PRIVATE MTLBlendOperation _sg_mtl_blend_op(sg_blend_op op) {
14849+ switch (op) {
14850+ case SG_BLENDOP_ADD: return MTLBlendOperationAdd;
14851+ case SG_BLENDOP_SUBTRACT: return MTLBlendOperationSubtract;
14852+ case SG_BLENDOP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract;
14853+ case SG_BLENDOP_MIN: return MTLBlendOperationMin;
14854+ case SG_BLENDOP_MAX: return MTLBlendOperationMax;
14855+ default: SOKOL_UNREACHABLE; return (MTLBlendOperation)0;
14856+ }
14857+}
14858+
14859+_SOKOL_PRIVATE MTLBlendFactor _sg_mtl_blend_factor(sg_blend_factor f) {
14860+ switch (f) {
14861+ case SG_BLENDFACTOR_ZERO: return MTLBlendFactorZero;
14862+ case SG_BLENDFACTOR_ONE: return MTLBlendFactorOne;
14863+ case SG_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor;
14864+ case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor;
14865+ case SG_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha;
14866+ case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha;
14867+ case SG_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor;
14868+ case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor;
14869+ case SG_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha;
14870+ case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha;
14871+ case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return MTLBlendFactorSourceAlphaSaturated;
14872+ case SG_BLENDFACTOR_BLEND_COLOR: return MTLBlendFactorBlendColor;
14873+ case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return MTLBlendFactorOneMinusBlendColor;
14874+ case SG_BLENDFACTOR_BLEND_ALPHA: return MTLBlendFactorBlendAlpha;
14875+ case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return MTLBlendFactorOneMinusBlendAlpha;
14876+ case SG_BLENDFACTOR_SRC1_COLOR: return MTLBlendFactorSource1Color;
14877+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: return MTLBlendFactorOneMinusSource1Color;
14878+ case SG_BLENDFACTOR_SRC1_ALPHA: return MTLBlendFactorSource1Alpha;
14879+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: return MTLBlendFactorOneMinusSource1Alpha;
14880+ default: SOKOL_UNREACHABLE; return (MTLBlendFactor)0;
14881+ }
14882+}
14883+
14884+_SOKOL_PRIVATE MTLCompareFunction _sg_mtl_compare_func(sg_compare_func f) {
14885+ switch (f) {
14886+ case SG_COMPAREFUNC_NEVER: return MTLCompareFunctionNever;
14887+ case SG_COMPAREFUNC_LESS: return MTLCompareFunctionLess;
14888+ case SG_COMPAREFUNC_EQUAL: return MTLCompareFunctionEqual;
14889+ case SG_COMPAREFUNC_LESS_EQUAL: return MTLCompareFunctionLessEqual;
14890+ case SG_COMPAREFUNC_GREATER: return MTLCompareFunctionGreater;
14891+ case SG_COMPAREFUNC_NOT_EQUAL: return MTLCompareFunctionNotEqual;
14892+ case SG_COMPAREFUNC_GREATER_EQUAL: return MTLCompareFunctionGreaterEqual;
14893+ case SG_COMPAREFUNC_ALWAYS: return MTLCompareFunctionAlways;
14894+ default: SOKOL_UNREACHABLE; return (MTLCompareFunction)0;
14895+ }
14896+}
14897+
14898+_SOKOL_PRIVATE MTLStencilOperation _sg_mtl_stencil_op(sg_stencil_op op) {
14899+ switch (op) {
14900+ case SG_STENCILOP_KEEP: return MTLStencilOperationKeep;
14901+ case SG_STENCILOP_ZERO: return MTLStencilOperationZero;
14902+ case SG_STENCILOP_REPLACE: return MTLStencilOperationReplace;
14903+ case SG_STENCILOP_INCR_CLAMP: return MTLStencilOperationIncrementClamp;
14904+ case SG_STENCILOP_DECR_CLAMP: return MTLStencilOperationDecrementClamp;
14905+ case SG_STENCILOP_INVERT: return MTLStencilOperationInvert;
14906+ case SG_STENCILOP_INCR_WRAP: return MTLStencilOperationIncrementWrap;
14907+ case SG_STENCILOP_DECR_WRAP: return MTLStencilOperationDecrementWrap;
14908+ default: SOKOL_UNREACHABLE; return (MTLStencilOperation)0;
14909+ }
14910+}
14911+
14912+_SOKOL_PRIVATE MTLCullMode _sg_mtl_cull_mode(sg_cull_mode m) {
14913+ switch (m) {
14914+ case SG_CULLMODE_NONE: return MTLCullModeNone;
14915+ case SG_CULLMODE_FRONT: return MTLCullModeFront;
14916+ case SG_CULLMODE_BACK: return MTLCullModeBack;
14917+ default: SOKOL_UNREACHABLE; return (MTLCullMode)0;
14918+ }
14919+}
14920+
14921+_SOKOL_PRIVATE MTLWinding _sg_mtl_winding(sg_face_winding w) {
14922+ switch (w) {
14923+ case SG_FACEWINDING_CW: return MTLWindingClockwise;
14924+ case SG_FACEWINDING_CCW: return MTLWindingCounterClockwise;
14925+ default: SOKOL_UNREACHABLE; return (MTLWinding)0;
14926+ }
14927+}
14928+
14929+_SOKOL_PRIVATE MTLIndexType _sg_mtl_index_type(sg_index_type t) {
14930+ switch (t) {
14931+ case SG_INDEXTYPE_UINT16: return MTLIndexTypeUInt16;
14932+ case SG_INDEXTYPE_UINT32: return MTLIndexTypeUInt32;
14933+ default: SOKOL_UNREACHABLE; return (MTLIndexType)0;
14934+ }
14935+}
14936+
14937+_SOKOL_PRIVATE int _sg_mtl_index_size(sg_index_type t) {
14938+ switch (t) {
14939+ case SG_INDEXTYPE_NONE: return 0;
14940+ case SG_INDEXTYPE_UINT16: return 2;
14941+ case SG_INDEXTYPE_UINT32: return 4;
14942+ default: SOKOL_UNREACHABLE; return 0;
14943+ }
14944+}
14945+
14946+_SOKOL_PRIVATE MTLTextureType _sg_mtl_texture_type(sg_image_type t, bool msaa) {
14947+ switch (t) {
14948+ case SG_IMAGETYPE_2D: return msaa ? MTLTextureType2DMultisample : MTLTextureType2D;
14949+ case SG_IMAGETYPE_CUBE: return MTLTextureTypeCube;
14950+ case SG_IMAGETYPE_3D: return MTLTextureType3D;
14951+ // NOTE: MTLTextureType2DMultisampleArray requires macOS 10.14+, iOS 14.0+
14952+ case SG_IMAGETYPE_ARRAY: return MTLTextureType2DArray;
14953+ default: SOKOL_UNREACHABLE; return (MTLTextureType)0;
14954+ }
14955+}
14956+
14957+_SOKOL_PRIVATE MTLSamplerAddressMode _sg_mtl_address_mode(sg_wrap w) {
14958+ if (_sg.features.image_clamp_to_border) {
14959+ if (@available(macOS 12.0, iOS 14.0, *)) {
14960+ // border color feature available
14961+ switch (w) {
14962+ case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat;
14963+ case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge;
14964+ case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToBorderColor;
14965+ case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat;
14966+ default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0;
14967+ }
14968+ }
14969+ }
14970+ // fallthrough: clamp to border no supported
14971+ switch (w) {
14972+ case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat;
14973+ case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge;
14974+ case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToEdge;
14975+ case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat;
14976+ default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0;
14977+ }
14978+}
14979+
14980+_SOKOL_PRIVATE API_AVAILABLE(ios(14.0), macos(12.0)) MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) {
14981+ switch (c) {
14982+ case SG_BORDERCOLOR_TRANSPARENT_BLACK: return MTLSamplerBorderColorTransparentBlack;
14983+ case SG_BORDERCOLOR_OPAQUE_BLACK: return MTLSamplerBorderColorOpaqueBlack;
14984+ case SG_BORDERCOLOR_OPAQUE_WHITE: return MTLSamplerBorderColorOpaqueWhite;
14985+ default: SOKOL_UNREACHABLE; return (MTLSamplerBorderColor)0;
14986+ }
14987+}
14988+
14989+_SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) {
14990+ switch (f) {
14991+ case SG_FILTER_NEAREST:
14992+ return MTLSamplerMinMagFilterNearest;
14993+ case SG_FILTER_LINEAR:
14994+ return MTLSamplerMinMagFilterLinear;
14995+ default:
14996+ SOKOL_UNREACHABLE; return (MTLSamplerMinMagFilter)0;
14997+ }
14998+}
14999+
15000+_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mipmap_filter(sg_filter f) {
15001+ switch (f) {
15002+ case SG_FILTER_NEAREST:
15003+ return MTLSamplerMipFilterNearest;
15004+ case SG_FILTER_LINEAR:
15005+ return MTLSamplerMipFilterLinear;
15006+ default:
15007+ SOKOL_UNREACHABLE; return (MTLSamplerMipFilter)0;
15008+ }
15009+}
15010+
15011+_SOKOL_PRIVATE size_t _sg_mtl_vertexbuffer_bindslot(size_t sokol_bindslot) {
15012+ return sokol_bindslot + _SG_MTL_MAX_STAGE_UB_SBUF_BINDINGS;
15013+}
15014+
15015+//-- a pool for all Metal resource objects, with deferred release queue ---------
15016+_SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) {
15017+ _sg.mtl.idpool.num_slots = 2 *
15018+ (
15019+ 2 * desc->buffer_pool_size +
15020+ 2 * desc->image_pool_size +
15021+ 1 * desc->sampler_pool_size +
15022+ 6 * desc->shader_pool_size +
15023+ 3 * desc->pipeline_pool_size +
15024+ 1 * desc->view_pool_size +
15025+ 128
15026+ );
15027+ _sg.mtl.idpool.pool = [NSMutableArray arrayWithCapacity:(NSUInteger)_sg.mtl.idpool.num_slots];
15028+ _SG_OBJC_RETAIN(_sg.mtl.idpool.pool);
15029+ NSNull* null = [NSNull null];
15030+ for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) {
15031+ [_sg.mtl.idpool.pool addObject:null];
15032+ }
15033+ SOKOL_ASSERT([_sg.mtl.idpool.pool count] == (NSUInteger)_sg.mtl.idpool.num_slots);
15034+ // a queue of currently free slot indices
15035+ _sg.mtl.idpool.free_queue_top = 0;
15036+ _sg.mtl.idpool.free_queue = (int*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(int));
15037+ // pool slot 0 is reserved!
15038+ for (int i = _sg.mtl.idpool.num_slots-1; i >= 1; i--) {
15039+ _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = i;
15040+ }
15041+ // a circular queue which holds release items (frame index when a resource is to be released, and the resource's pool index
15042+ _sg.mtl.idpool.release_queue_front = 0;
15043+ _sg.mtl.idpool.release_queue_back = 0;
15044+ _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t));
15045+ for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) {
15046+ _sg.mtl.idpool.release_queue[i].frame_index = 0;
15047+ _sg.mtl.idpool.release_queue[i].slot_index = _SG_MTL_INVALID_SLOT_INDEX;
15048+ }
15049+}
15050+
15051+_SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) {
15052+ _sg_free(_sg.mtl.idpool.release_queue); _sg.mtl.idpool.release_queue = 0;
15053+ _sg_free(_sg.mtl.idpool.free_queue); _sg.mtl.idpool.free_queue = 0;
15054+ _SG_OBJC_RELEASE(_sg.mtl.idpool.pool);
15055+}
15056+
15057+// get a new free resource pool slot
15058+_SOKOL_PRIVATE int _sg_mtl_alloc_pool_slot(void) {
15059+ SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top > 0);
15060+ const int slot_index = _sg.mtl.idpool.free_queue[--_sg.mtl.idpool.free_queue_top];
15061+ SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots));
15062+ return slot_index;
15063+}
15064+
15065+// put a free resource pool slot back into the free-queue
15066+_SOKOL_PRIVATE void _sg_mtl_free_pool_slot(int slot_index) {
15067+ SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top < _sg.mtl.idpool.num_slots);
15068+ SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots));
15069+ _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = slot_index;
15070+}
15071+
15072+// add an MTLResource to the pool, return pool index or 0 if input was 'nil'
15073+_SOKOL_PRIVATE int _sg_mtl_add_resource(id res) {
15074+ if (nil == res) {
15075+ return _SG_MTL_INVALID_SLOT_INDEX;
15076+ }
15077+ _sg_stats_inc(metal.idpool.num_added);
15078+ const int slot_index = _sg_mtl_alloc_pool_slot();
15079+ // NOTE: the NSMutableArray will take ownership of its items
15080+ SOKOL_ASSERT([NSNull null] == _sg.mtl.idpool.pool[(NSUInteger)slot_index]);
15081+ _sg.mtl.idpool.pool[(NSUInteger)slot_index] = res;
15082+ return slot_index;
15083+}
15084+
15085+/* mark an MTLResource for release, this will put the resource into the
15086+ deferred-release queue, and the resource will then be released N frames later,
15087+ the special pool index 0 will be ignored (this means that a nil
15088+ value was provided to _sg_mtl_add_resource()
15089+*/
15090+_SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_index) {
15091+ if (slot_index == _SG_MTL_INVALID_SLOT_INDEX) {
15092+ return;
15093+ }
15094+ _sg_stats_inc(metal.idpool.num_released);
15095+ SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots));
15096+ SOKOL_ASSERT([NSNull null] != _sg.mtl.idpool.pool[(NSUInteger)slot_index]);
15097+ int release_index = _sg.mtl.idpool.release_queue_front++;
15098+ if (_sg.mtl.idpool.release_queue_front >= _sg.mtl.idpool.num_slots) {
15099+ // wrap-around
15100+ _sg.mtl.idpool.release_queue_front = 0;
15101+ }
15102+ // release queue full?
15103+ SOKOL_ASSERT(_sg.mtl.idpool.release_queue_front != _sg.mtl.idpool.release_queue_back);
15104+ SOKOL_ASSERT(0 == _sg.mtl.idpool.release_queue[release_index].frame_index);
15105+ const uint32_t safe_to_release_frame_index = frame_index + SG_NUM_INFLIGHT_FRAMES + 1;
15106+ _sg.mtl.idpool.release_queue[release_index].frame_index = safe_to_release_frame_index;
15107+ _sg.mtl.idpool.release_queue[release_index].slot_index = slot_index;
15108+}
15109+
15110+// run garbage-collection pass on all resources in the release-queue
15111+_SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) {
15112+ while (_sg.mtl.idpool.release_queue_back != _sg.mtl.idpool.release_queue_front) {
15113+ if (frame_index < _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index) {
15114+ // don't need to check further, release-items past this are too young
15115+ break;
15116+ }
15117+ _sg_stats_inc(metal.idpool.num_garbage_collected);
15118+ // safe to release this resource
15119+ const int slot_index = _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index;
15120+ SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots));
15121+ // note: the NSMutableArray takes ownership of its items, assigning an NSNull object will
15122+ // release the object, no matter if using ARC or not
15123+ SOKOL_ASSERT(_sg.mtl.idpool.pool[(NSUInteger)slot_index] != [NSNull null]);
15124+ _sg.mtl.idpool.pool[(NSUInteger)slot_index] = [NSNull null];
15125+ // put the now free pool index back on the free queue
15126+ _sg_mtl_free_pool_slot(slot_index);
15127+ // reset the release queue slot and advance the back index
15128+ _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index = 0;
15129+ _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index = _SG_MTL_INVALID_SLOT_INDEX;
15130+ _sg.mtl.idpool.release_queue_back++;
15131+ if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) {
15132+ // wrap-around
15133+ _sg.mtl.idpool.release_queue_back = 0;
15134+ }
15135+ }
15136+}
15137+
15138+_SOKOL_PRIVATE id _sg_mtl_id(int slot_index) {
15139+ return _sg.mtl.idpool.pool[(NSUInteger)slot_index];
15140+}
15141+
15142+_SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) {
15143+ _sg_clear(&_sg.mtl.cache, sizeof(_sg.mtl.cache));
15144+}
15145+
15146+// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
15147+_SOKOL_PRIVATE void _sg_mtl_init_caps(void) {
15148+ #if defined(_SG_TARGET_MACOS)
15149+ _sg.backend = SG_BACKEND_METAL_MACOS;
15150+ #elif defined(_SG_TARGET_IOS)
15151+ #if defined(_SG_TARGET_IOS_SIMULATOR)
15152+ _sg.backend = SG_BACKEND_METAL_SIMULATOR;
15153+ #else
15154+ _sg.backend = SG_BACKEND_METAL_IOS;
15155+ #endif
15156+ #endif
15157+ _sg.features.origin_top_left = true;
15158+ _sg.features.mrt_independent_blend_state = true;
15159+ _sg.features.mrt_independent_write_mask = true;
15160+ _sg.features.compute = true;
15161+ _sg.features.msaa_texture_bindings = true;
15162+ _sg.features.draw_base_vertex = true;
15163+ _sg.features.draw_base_instance = true;
15164+ _sg.features.dual_source_blending = true;
15165+ _sg.features.vertexformat_int10_n2 = true;
15166+
15167+ _sg.features.image_clamp_to_border = false;
15168+ #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000)
15169+ if (@available(macOS 12.0, iOS 14.0, *)) {
15170+ _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple7]
15171+ || [_sg.mtl.device supportsFamily:MTLGPUFamilyMac2];
15172+ #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 130000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 160000)
15173+ if (!_sg.features.image_clamp_to_border) {
15174+ if (@available(macOS 13.0, iOS 16.0, *)) {
15175+ _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyMetal3];
15176+ }
15177+ }
15178+ #endif
15179+ }
15180+ #endif
15181+
15182+ #if defined(_SG_TARGET_MACOS)
15183+ _sg.limits.max_image_size_2d = 16 * 1024;
15184+ _sg.limits.max_image_size_cube = 16 * 1024;
15185+ _sg.limits.max_image_size_3d = 2 * 1024;
15186+ _sg.limits.max_image_size_array = 16 * 1024;
15187+ _sg.limits.max_image_array_layers = 2 * 1024;
15188+ _sg.limits.max_texture_bindings_per_stage = _sg_min(128, SG_MAX_VIEW_BINDSLOTS);
15189+ #else
15190+ // FIXME: newer iOS devices support 16k textures
15191+ _sg.limits.max_image_size_2d = 8 * 1024;
15192+ _sg.limits.max_image_size_cube = 8 * 1024;
15193+ _sg.limits.max_image_size_3d = 2 * 1024;
15194+ _sg.limits.max_image_size_array = 8 * 1024;
15195+ _sg.limits.max_image_array_layers = 2 * 1024;
15196+ _sg.limits.max_texture_bindings_per_stage = _sg_min(96, SG_MAX_VIEW_BINDSLOTS); // since iPhone8
15197+ #endif
15198+ _sg.limits.max_storage_image_bindings_per_stage = _sg.limits.max_texture_bindings_per_stage; // shared with texture bindings
15199+ _sg.limits.max_storage_buffer_bindings_per_stage = _sg_min(_SG_MTL_MAX_STAGE_BUFFER_BINDINGS - (SG_MAX_VERTEXBUFFER_BINDSLOTS + SG_MAX_UNIFORMBLOCK_BINDSLOTS), SG_MAX_VIEW_BINDSLOTS);
15200+ _sg.limits.max_color_attachments = _sg_min(8, SG_MAX_COLOR_ATTACHMENTS);
15201+ _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES;
15202+
15203+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]);
15204+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8SN]);
15205+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]);
15206+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]);
15207+ #if defined(_SG_TARGET_MACOS)
15208+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]);
15209+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]);
15210+ #else
15211+ _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16]);
15212+ _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16SN]);
15213+ #endif
15214+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]);
15215+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]);
15216+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]);
15217+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]);
15218+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8SN]);
15219+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]);
15220+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]);
15221+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]);
15222+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]);
15223+ #if defined(_SG_TARGET_MACOS)
15224+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]);
15225+ #else
15226+ _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]);
15227+ #endif
15228+ #if defined(_SG_TARGET_MACOS)
15229+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]);
15230+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]);
15231+ #else
15232+ _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16]);
15233+ _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16SN]);
15234+ #endif
15235+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]);
15236+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]);
15237+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]);
15238+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]);
15239+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]);
15240+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]);
15241+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]);
15242+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]);
15243+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]);
15244+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SBGR8A8]);
15245+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]);
15246+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG11B10F]);
15247+ #if defined(_SG_TARGET_MACOS)
15248+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]);
15249+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]);
15250+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]);
15251+ #else
15252+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB9E5]);
15253+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]);
15254+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]);
15255+ #endif
15256+ #if defined(_SG_TARGET_MACOS)
15257+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]);
15258+ #else
15259+ _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]);
15260+ #endif
15261+ #if defined(_SG_TARGET_MACOS)
15262+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]);
15263+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]);
15264+ #else
15265+ _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16]);
15266+ _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]);
15267+ #endif
15268+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]);
15269+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]);
15270+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]);
15271+ #if defined(_SG_TARGET_MACOS)
15272+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]);
15273+ _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]);
15274+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
15275+ #else
15276+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]);
15277+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]);
15278+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
15279+ #endif
15280+ _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]);
15281+ _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]);
15282+ #if defined(_SG_TARGET_MACOS)
15283+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]);
15284+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]);
15285+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]);
15286+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]);
15287+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]);
15288+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]);
15289+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]);
15290+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]);
15291+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]);
15292+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]);
15293+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]);
15294+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]);
15295+ #else
15296+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]);
15297+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]);
15298+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]);
15299+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]);
15300+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]);
15301+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]);
15302+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]);
15303+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]);
15304+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]);
15305+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]);
15306+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]);
15307+ #endif
15308+
15309+ // compute shader access
15310+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R8]);
15311+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R8SN]);
15312+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R8UI]);
15313+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R8SI]);
15314+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16]);
15315+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16SN]);
15316+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16UI]);
15317+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16SI]);
15318+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16F]);
15319+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG8]);
15320+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG8SN]);
15321+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG8UI]);
15322+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG8SI]);
15323+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32UI]);
15324+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32SI]);
15325+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32F]);
15326+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG16]);
15327+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]);
15328+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG16UI]);
15329+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG16SI]);
15330+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG16F]);
15331+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]);
15332+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]);
15333+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]);
15334+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]);
15335+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]);
15336+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]);
15337+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG11B10F]);
15338+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32UI]);
15339+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32SI]);
15340+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32F]);
15341+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]);
15342+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]);
15343+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]);
15344+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]);
15345+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]);
15346+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]);
15347+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]);
15348+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
15349+}
15350+
15351+//-- main Metal backend state and functions ------------------------------------
15352+_SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) {
15353+ // assume already zero-initialized
15354+ SOKOL_ASSERT(desc);
15355+ SOKOL_ASSERT(desc->environment.metal.device);
15356+ SOKOL_ASSERT(desc->uniform_buffer_size > 0);
15357+ _sg_mtl_init_pool(desc);
15358+ _sg_mtl_clear_state_cache();
15359+ _sg.mtl.valid = true;
15360+ _sg.mtl.ub_size = desc->uniform_buffer_size;
15361+ _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES);
15362+ _sg.mtl.device = (__bridge id<MTLDevice>) desc->environment.metal.device;
15363+ _sg.mtl.cmd_queue = [_sg.mtl.device newCommandQueue];
15364+
15365+ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
15366+ _sg.mtl.uniform_buffers[i] = [_sg.mtl.device
15367+ newBufferWithLength:(NSUInteger)_sg.mtl.ub_size
15368+ options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared
15369+ ];
15370+ #if defined(SOKOL_DEBUG)
15371+ _sg.mtl.uniform_buffers[i].label = [NSString stringWithFormat:@"sg-uniform-buffer.%d", i];
15372+ #endif
15373+ }
15374+
15375+ if (desc->metal.force_managed_storage_mode) {
15376+ _sg.mtl.use_shared_storage_mode = false;
15377+ } else if (@available(macOS 10.15, iOS 13.0, *)) {
15378+ // on Intel Macs, always use managed resources even though the
15379+ // device says it supports unified memory (because of texture restrictions)
15380+ const bool is_apple_gpu = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple1];
15381+ if (!is_apple_gpu) {
15382+ _sg.mtl.use_shared_storage_mode = false;
15383+ } else {
15384+ _sg.mtl.use_shared_storage_mode = true;
15385+ }
15386+ } else {
15387+ #if defined(_SG_TARGET_MACOS)
15388+ _sg.mtl.use_shared_storage_mode = false;
15389+ #else
15390+ _sg.mtl.use_shared_storage_mode = true;
15391+ #endif
15392+ }
15393+ _sg_mtl_init_caps();
15394+}
15395+
15396+_SOKOL_PRIVATE void _sg_mtl_discard_backend(void) {
15397+ SOKOL_ASSERT(_sg.mtl.valid);
15398+ // wait for the last frame to finish
15399+ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
15400+ dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER);
15401+ }
15402+ // semaphore must be "relinquished" before destruction
15403+ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
15404+ dispatch_semaphore_signal(_sg.mtl.sem);
15405+ }
15406+ _sg_mtl_garbage_collect(_sg.frame_index + SG_NUM_INFLIGHT_FRAMES + 2);
15407+ _sg_mtl_destroy_pool();
15408+ _sg.mtl.valid = false;
15409+
15410+ _SG_OBJC_RELEASE(_sg.mtl.sem);
15411+ _SG_OBJC_RELEASE(_sg.mtl.device);
15412+ _SG_OBJC_RELEASE(_sg.mtl.cmd_queue);
15413+ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
15414+ _SG_OBJC_RELEASE(_sg.mtl.uniform_buffers[i]);
15415+ }
15416+ // NOTE: MTLCommandBuffer, MTLRenderCommandEncoder and MTLComputeCommandEncoder are auto-released
15417+ _sg.mtl.cmd_buffer = nil;
15418+ _sg.mtl.render_cmd_encoder = nil;
15419+ _sg.mtl.compute_cmd_encoder = nil;
15420+}
15421+
15422+_SOKOL_PRIVATE void _sg_mtl_reset_state_cache(void) {
15423+ _sg_mtl_clear_state_cache();
15424+}
15425+
15426+_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) {
15427+ SOKOL_ASSERT(buf && desc);
15428+ SOKOL_ASSERT(buf->cmn.size > 0);
15429+ const bool injected = (0 != desc->mtl_buffers[0]);
15430+ MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(&buf->cmn.usage);
15431+ for (int slot = 0; slot < buf->cmn.num_slots; slot++) {
15432+ id<MTLBuffer> mtl_buf;
15433+ if (injected) {
15434+ SOKOL_ASSERT(desc->mtl_buffers[slot]);
15435+ mtl_buf = (__bridge id<MTLBuffer>) desc->mtl_buffers[slot];
15436+ } else {
15437+ if (desc->data.ptr) {
15438+ SOKOL_ASSERT(desc->data.size > 0);
15439+ mtl_buf = [_sg.mtl.device newBufferWithBytes:desc->data.ptr length:(NSUInteger)buf->cmn.size options:mtl_options];
15440+ } else {
15441+ mtl_buf = [_sg.mtl.device newBufferWithLength:(NSUInteger)buf->cmn.size options:mtl_options];
15442+ }
15443+ if (nil == mtl_buf) {
15444+ _SG_ERROR(METAL_CREATE_BUFFER_FAILED);
15445+ return SG_RESOURCESTATE_FAILED;
15446+ }
15447+ }
15448+ #if defined(SOKOL_DEBUG)
15449+ if (desc->label) {
15450+ mtl_buf.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot];
15451+ }
15452+ #endif
15453+ buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf);
15454+ _SG_OBJC_RELEASE(mtl_buf);
15455+ }
15456+ return SG_RESOURCESTATE_VALID;
15457+}
15458+
15459+_SOKOL_PRIVATE void _sg_mtl_discard_buffer(_sg_buffer_t* buf) {
15460+ SOKOL_ASSERT(buf);
15461+ for (int slot = 0; slot < buf->cmn.num_slots; slot++) {
15462+ // it's valid to call release resource with '0'
15463+ _sg_mtl_release_resource(_sg.frame_index, buf->mtl.buf[slot]);
15464+ }
15465+}
15466+
15467+_SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unretained id<MTLTexture> mtl_tex, const sg_image_data* data) {
15468+ const int num_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? 1 : img->cmn.num_slices;
15469+ for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) {
15470+ SOKOL_ASSERT(data->mip_levels[mip_index].ptr);
15471+ SOKOL_ASSERT(data->mip_levels[mip_index].size > 0);
15472+ const uint8_t* data_ptr = (const uint8_t*)data->mip_levels[mip_index].ptr;
15473+ const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index);
15474+ const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index);
15475+ int bytes_per_row = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1);
15476+ int bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1);
15477+ /* bytesPerImage special case: https://developer.apple.com/documentation/metal/mtltexture/1515679-replaceregion
15478+
15479+ "Supply a nonzero value only when you copy data to a MTLTextureType3D type texture"
15480+ */
15481+ MTLRegion region;
15482+ int bytes_per_image;
15483+ if (img->cmn.type == SG_IMAGETYPE_3D) {
15484+ const int mip_depth = _sg_miplevel_dim(img->cmn.num_slices, mip_index);
15485+ region = MTLRegionMake3D(0, 0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height, (NSUInteger)mip_depth);
15486+ bytes_per_image = bytes_per_slice;
15487+ // FIXME: apparently the minimal bytes_per_image size for 3D texture is 4 KByte... somehow need to handle this
15488+ } else {
15489+ region = MTLRegionMake2D(0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height);
15490+ bytes_per_image = 0;
15491+ }
15492+
15493+ for (int slice_index = 0; slice_index < num_slices; slice_index++) {
15494+ const int slice_offset = slice_index * bytes_per_slice;
15495+ SOKOL_ASSERT((slice_offset + bytes_per_slice) <= (int)data->mip_levels[mip_index].size);
15496+ [mtl_tex replaceRegion:region
15497+ mipmapLevel:(NSUInteger)mip_index
15498+ slice:(NSUInteger)slice_index
15499+ withBytes:data_ptr + slice_offset
15500+ bytesPerRow:(NSUInteger)bytes_per_row
15501+ bytesPerImage:(NSUInteger)bytes_per_image];
15502+ }
15503+ }
15504+}
15505+
15506+_SOKOL_PRIVATE bool _sg_mtl_init_texdesc(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) {
15507+ mtl_desc.textureType = _sg_mtl_texture_type(img->cmn.type, img->cmn.sample_count > 1);
15508+ mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->cmn.pixel_format);
15509+ if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) {
15510+ _SG_ERROR(METAL_TEXTURE_FORMAT_NOT_SUPPORTED);
15511+ return false;
15512+ }
15513+ mtl_desc.width = (NSUInteger)img->cmn.width;
15514+ mtl_desc.height = (NSUInteger)img->cmn.height;
15515+ if (SG_IMAGETYPE_3D == img->cmn.type) {
15516+ mtl_desc.depth = (NSUInteger)img->cmn.num_slices;
15517+ } else {
15518+ mtl_desc.depth = 1;
15519+ }
15520+ mtl_desc.mipmapLevelCount = (NSUInteger)img->cmn.num_mipmaps;
15521+ if (SG_IMAGETYPE_ARRAY == img->cmn.type) {
15522+ mtl_desc.arrayLength = (NSUInteger)img->cmn.num_slices;
15523+ } else {
15524+ mtl_desc.arrayLength = 1;
15525+ }
15526+ mtl_desc.sampleCount = (NSUInteger)img->cmn.sample_count;
15527+
15528+ const sg_image_usage* usg = &img->cmn.usage;
15529+ const bool any_attachment = usg->color_attachment || usg->resolve_attachment || usg->depth_stencil_attachment;
15530+ MTLTextureUsage mtl_tex_usage = MTLTextureUsageShaderRead;
15531+ if (any_attachment) {
15532+ mtl_tex_usage |= MTLTextureUsageRenderTarget;
15533+ }
15534+ if (img->cmn.usage.storage_image) {
15535+ mtl_tex_usage |= MTLTextureUsageShaderWrite;
15536+ }
15537+ mtl_desc.usage = mtl_tex_usage;
15538+
15539+ MTLResourceOptions mtl_res_options = 0;
15540+ if (any_attachment || img->cmn.usage.storage_image) {
15541+ mtl_res_options |= MTLResourceStorageModePrivate;
15542+ } else {
15543+ mtl_res_options |= _sg_mtl_resource_options_storage_mode_managed_or_shared();
15544+ if (!img->cmn.usage.immutable) {
15545+ mtl_res_options |= MTLResourceCPUCacheModeWriteCombined;
15546+ }
15547+ }
15548+ mtl_desc.resourceOptions = mtl_res_options;
15549+ return true;
15550+}
15551+
15552+_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg_image_desc* desc) {
15553+ SOKOL_ASSERT(img && desc);
15554+ const bool injected = (0 != desc->mtl_textures[0]);
15555+
15556+ // first initialize all Metal resource pool slots to 'empty'
15557+ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
15558+ img->mtl.tex[i] = _sg_mtl_add_resource(nil);
15559+ }
15560+
15561+ // initialize a Metal texture descriptor
15562+ MTLTextureDescriptor* mtl_desc = [[MTLTextureDescriptor alloc] init];
15563+ if (!_sg_mtl_init_texdesc(mtl_desc, img)) {
15564+ _SG_OBJC_RELEASE(mtl_desc);
15565+ return SG_RESOURCESTATE_FAILED;
15566+ }
15567+ for (int slot = 0; slot < img->cmn.num_slots; slot++) {
15568+ id<MTLTexture> mtl_tex;
15569+ if (injected) {
15570+ SOKOL_ASSERT(desc->mtl_textures[slot]);
15571+ mtl_tex = (__bridge id<MTLTexture>) desc->mtl_textures[slot];
15572+ } else {
15573+ mtl_tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc];
15574+ if (nil == mtl_tex) {
15575+ _SG_OBJC_RELEASE(mtl_desc);
15576+ _SG_ERROR(METAL_CREATE_TEXTURE_FAILED);
15577+ return SG_RESOURCESTATE_FAILED;
15578+ }
15579+ if (desc->data.mip_levels[0].ptr) {
15580+ _sg_mtl_copy_image_data(img, mtl_tex, &desc->data);
15581+ }
15582+ }
15583+ #if defined(SOKOL_DEBUG)
15584+ if (desc->label) {
15585+ mtl_tex.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot];
15586+ }
15587+ #endif
15588+ img->mtl.tex[slot] = _sg_mtl_add_resource(mtl_tex);
15589+ _SG_OBJC_RELEASE(mtl_tex);
15590+ }
15591+ _SG_OBJC_RELEASE(mtl_desc);
15592+ return SG_RESOURCESTATE_VALID;
15593+}
15594+
15595+_SOKOL_PRIVATE void _sg_mtl_discard_image(_sg_image_t* img) {
15596+ SOKOL_ASSERT(img);
15597+ // it's valid to call release resource with a 'null resource'
15598+ for (int slot = 0; slot < img->cmn.num_slots; slot++) {
15599+ _sg_mtl_release_resource(_sg.frame_index, img->mtl.tex[slot]);
15600+ }
15601+}
15602+
15603+_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) {
15604+ SOKOL_ASSERT(smp && desc);
15605+ id<MTLSamplerState> mtl_smp;
15606+ const bool injected = (0 != desc->mtl_sampler);
15607+ if (injected) {
15608+ SOKOL_ASSERT(desc->mtl_sampler);
15609+ mtl_smp = (__bridge id<MTLSamplerState>) desc->mtl_sampler;
15610+ } else {
15611+ MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init];
15612+ mtl_desc.sAddressMode = _sg_mtl_address_mode(desc->wrap_u);
15613+ mtl_desc.tAddressMode = _sg_mtl_address_mode(desc->wrap_v);
15614+ mtl_desc.rAddressMode = _sg_mtl_address_mode(desc->wrap_w);
15615+ if (_sg.features.image_clamp_to_border) {
15616+ if (@available(macOS 12.0, iOS 14.0, *)) {
15617+ mtl_desc.borderColor = _sg_mtl_border_color(desc->border_color);
15618+ }
15619+ }
15620+ mtl_desc.minFilter = _sg_mtl_minmag_filter(desc->min_filter);
15621+ mtl_desc.magFilter = _sg_mtl_minmag_filter(desc->mag_filter);
15622+ mtl_desc.mipFilter = _sg_mtl_mipmap_filter(desc->mipmap_filter);
15623+ mtl_desc.lodMinClamp = desc->min_lod;
15624+ mtl_desc.lodMaxClamp = desc->max_lod;
15625+ // FIXME: lodAverage?
15626+ mtl_desc.maxAnisotropy = desc->max_anisotropy;
15627+ mtl_desc.normalizedCoordinates = YES;
15628+ mtl_desc.compareFunction = _sg_mtl_compare_func(desc->compare);
15629+ #if defined(SOKOL_DEBUG)
15630+ if (desc->label) {
15631+ mtl_desc.label = [NSString stringWithUTF8String:desc->label];
15632+ }
15633+ #endif
15634+ mtl_smp = [_sg.mtl.device newSamplerStateWithDescriptor:mtl_desc];
15635+ _SG_OBJC_RELEASE(mtl_desc);
15636+ if (nil == mtl_smp) {
15637+ _SG_ERROR(METAL_CREATE_SAMPLER_FAILED);
15638+ return SG_RESOURCESTATE_FAILED;
15639+ }
15640+ }
15641+ smp->mtl.sampler_state = _sg_mtl_add_resource(mtl_smp);
15642+ _SG_OBJC_RELEASE(mtl_smp);
15643+ return SG_RESOURCESTATE_VALID;
15644+}
15645+
15646+_SOKOL_PRIVATE void _sg_mtl_discard_sampler(_sg_sampler_t* smp) {
15647+ SOKOL_ASSERT(smp);
15648+ // it's valid to call release resource with a 'null resource'
15649+ _sg_mtl_release_resource(_sg.frame_index, smp->mtl.sampler_state);
15650+}
15651+
15652+_SOKOL_PRIVATE id<MTLLibrary> _sg_mtl_compile_library(const char* src) {
15653+ NSError* err = NULL;
15654+ id<MTLLibrary> lib = [_sg.mtl.device
15655+ newLibraryWithSource:[NSString stringWithUTF8String:src]
15656+ options:nil
15657+ error:&err
15658+ ];
15659+ if (err) {
15660+ _SG_ERROR(METAL_SHADER_COMPILATION_FAILED);
15661+ _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]);
15662+ }
15663+ return lib;
15664+}
15665+
15666+_SOKOL_PRIVATE id<MTLLibrary> _sg_mtl_library_from_bytecode(const void* ptr, size_t num_bytes) {
15667+ NSError* err = NULL;
15668+ dispatch_data_t lib_data = dispatch_data_create(ptr, num_bytes, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
15669+ id<MTLLibrary> lib = [_sg.mtl.device newLibraryWithData:lib_data error:&err];
15670+ if (err) {
15671+ _SG_ERROR(METAL_SHADER_CREATION_FAILED);
15672+ _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]);
15673+ }
15674+ _SG_OBJC_RELEASE(lib_data);
15675+ return lib;
15676+}
15677+
15678+_SOKOL_PRIVATE bool _sg_mtl_create_shader_func(const sg_shader_function* func, const char* label, const char* label_ext, _sg_mtl_shader_func_t* res) {
15679+ SOKOL_ASSERT(res->mtl_lib == _SG_MTL_INVALID_SLOT_INDEX);
15680+ SOKOL_ASSERT(res->mtl_func == _SG_MTL_INVALID_SLOT_INDEX);
15681+ id<MTLLibrary> mtl_lib = nil;
15682+ if (func->bytecode.ptr) {
15683+ SOKOL_ASSERT(func->bytecode.size > 0);
15684+ mtl_lib = _sg_mtl_library_from_bytecode(func->bytecode.ptr, func->bytecode.size);
15685+ } else if (func->source) {
15686+ mtl_lib = _sg_mtl_compile_library(func->source);
15687+ }
15688+ if (mtl_lib == nil) {
15689+ return false;
15690+ }
15691+ #if defined(SOKOL_DEBUG)
15692+ if (label) {
15693+ SOKOL_ASSERT(label_ext);
15694+ mtl_lib.label = [NSString stringWithFormat:@"%s.%s", label, label_ext];
15695+ }
15696+ #else
15697+ _SOKOL_UNUSED(label);
15698+ _SOKOL_UNUSED(label_ext);
15699+ #endif
15700+ SOKOL_ASSERT(func->entry);
15701+ id<MTLFunction> mtl_func = [mtl_lib newFunctionWithName:[NSString stringWithUTF8String:func->entry]];
15702+ if (mtl_func == nil) {
15703+ _SG_ERROR(METAL_SHADER_ENTRY_NOT_FOUND);
15704+ _SG_OBJC_RELEASE(mtl_lib);
15705+ return false;
15706+ }
15707+ res->mtl_lib = _sg_mtl_add_resource(mtl_lib);
15708+ res->mtl_func = _sg_mtl_add_resource(mtl_func);
15709+ _SG_OBJC_RELEASE(mtl_lib);
15710+ _SG_OBJC_RELEASE(mtl_func);
15711+ return true;
15712+}
15713+
15714+_SOKOL_PRIVATE void _sg_mtl_discard_shader_func(const _sg_mtl_shader_func_t* func) {
15715+ // it is valid to call _sg_mtl_release_resource with a 'null resource'
15716+ _sg_mtl_release_resource(_sg.frame_index, func->mtl_func);
15717+ _sg_mtl_release_resource(_sg.frame_index, func->mtl_lib);
15718+}
15719+
15720+// NOTE: this is an out-of-range check for MSL bindslots that's also active in release mode
15721+_SOKOL_PRIVATE bool _sg_mtl_ensure_msl_bindslot_ranges(const sg_shader_desc* desc) {
15722+ SOKOL_ASSERT(desc);
15723+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
15724+ const sg_shader_uniform_block* ub = &desc->uniform_blocks[i];
15725+ if (ub->stage != SG_SHADERSTAGE_NONE) {
15726+ if (ub->msl_buffer_n >= _SG_MTL_MAX_STAGE_UB_BINDINGS) {
15727+ _SG_ERROR(METAL_UNIFORMBLOCK_MSL_BUFFER_SLOT_OUT_OF_RANGE);
15728+ return false;
15729+ }
15730+ }
15731+ }
15732+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
15733+ const sg_shader_view* view = &desc->views[i];
15734+ if (view->texture.stage != SG_SHADERSTAGE_NONE) {
15735+ if (view->texture.msl_texture_n >= _SG_MTL_MAX_STAGE_TEXTURE_BINDINGS) {
15736+ _SG_ERROR(METAL_IMAGE_MSL_TEXTURE_SLOT_OUT_OF_RANGE);
15737+ return false;
15738+ }
15739+ }
15740+ if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) {
15741+ if ((view->storage_buffer.msl_buffer_n < _SG_MTL_MAX_STAGE_UB_BINDINGS) ||
15742+ (view->storage_buffer.msl_buffer_n >= _SG_MTL_MAX_STAGE_UB_SBUF_BINDINGS))
15743+ {
15744+ _SG_ERROR(METAL_STORAGEBUFFER_MSL_BUFFER_SLOT_OUT_OF_RANGE);
15745+ return false;
15746+ }
15747+ }
15748+ if (view->storage_image.stage != SG_SHADERSTAGE_NONE) {
15749+ if (view->storage_image.msl_texture_n >= _SG_MTL_MAX_STAGE_TEXTURE_BINDINGS) {
15750+ _SG_ERROR(METAL_STORAGEIMAGE_MSL_TEXTURE_SLOT_OUT_OF_RANGE);
15751+ return false;
15752+ }
15753+ }
15754+ }
15755+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
15756+ const sg_shader_sampler* smp = &desc->samplers[i];
15757+ if (smp->stage != SG_SHADERSTAGE_NONE) {
15758+ if (smp->msl_sampler_n >= _SG_MTL_MAX_STAGE_SAMPLER_BINDINGS) {
15759+ _SG_ERROR(METAL_SAMPLER_MSL_SAMPLER_SLOT_OUT_OF_RANGE);
15760+ return false;
15761+ }
15762+ }
15763+ }
15764+ return true;
15765+}
15766+
15767+_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) {
15768+ SOKOL_ASSERT(shd && desc);
15769+
15770+ // do a MSL bindslot range check also in release mode, and if that fails,
15771+ // also fail shader creation
15772+ if (!_sg_mtl_ensure_msl_bindslot_ranges(desc)) {
15773+ return SG_RESOURCESTATE_FAILED;
15774+ }
15775+
15776+ shd->mtl.threads_per_threadgroup = MTLSizeMake(
15777+ (NSUInteger)desc->mtl_threads_per_threadgroup.x,
15778+ (NSUInteger)desc->mtl_threads_per_threadgroup.y,
15779+ (NSUInteger)desc->mtl_threads_per_threadgroup.z);
15780+
15781+ // copy resource bindslot mappings
15782+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
15783+ shd->mtl.ub_buffer_n[i] = desc->uniform_blocks[i].msl_buffer_n;
15784+ }
15785+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
15786+ const sg_shader_view* view = &desc->views[i];
15787+ SOKOL_ASSERT(0 == shd->mtl.view_buffer_texture_n[i]);
15788+ if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) {
15789+ shd->mtl.view_buffer_texture_n[i] = view->storage_buffer.msl_buffer_n;
15790+ } else if (view->texture.stage != SG_SHADERSTAGE_NONE) {
15791+ shd->mtl.view_buffer_texture_n[i] = view->texture.msl_texture_n;
15792+ } else if (view->storage_image.stage != SG_SHADERSTAGE_NONE) {
15793+ shd->mtl.view_buffer_texture_n[i] = view->storage_image.msl_texture_n;
15794+ }
15795+ }
15796+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
15797+ shd->mtl.smp_sampler_n[i] = desc->samplers[i].msl_sampler_n;
15798+ }
15799+
15800+ // create metal library and function objects
15801+ bool shd_valid = true;
15802+ if (desc->vertex_func.source || desc->vertex_func.bytecode.ptr) {
15803+ shd_valid &= _sg_mtl_create_shader_func(&desc->vertex_func, desc->label, "vs", &shd->mtl.vertex_func);
15804+ }
15805+ if (desc->fragment_func.source || desc->fragment_func.bytecode.ptr) {
15806+ shd_valid &= _sg_mtl_create_shader_func(&desc->fragment_func, desc->label, "fs", &shd->mtl.fragment_func);
15807+ }
15808+ if (desc->compute_func.source || desc->compute_func.bytecode.ptr) {
15809+ shd_valid &= _sg_mtl_create_shader_func(&desc->compute_func, desc->label, "cs", &shd->mtl.compute_func);
15810+ }
15811+ if (!shd_valid) {
15812+ _sg_mtl_discard_shader_func(&shd->mtl.vertex_func);
15813+ _sg_mtl_discard_shader_func(&shd->mtl.fragment_func);
15814+ _sg_mtl_discard_shader_func(&shd->mtl.compute_func);
15815+ }
15816+ return shd_valid ? SG_RESOURCESTATE_VALID : SG_RESOURCESTATE_FAILED;
15817+}
15818+
15819+_SOKOL_PRIVATE void _sg_mtl_discard_shader(_sg_shader_t* shd) {
15820+ SOKOL_ASSERT(shd);
15821+ _sg_mtl_discard_shader_func(&shd->mtl.vertex_func);
15822+ _sg_mtl_discard_shader_func(&shd->mtl.fragment_func);
15823+ _sg_mtl_discard_shader_func(&shd->mtl.compute_func);
15824+}
15825+
15826+_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) {
15827+ SOKOL_ASSERT(pip && desc);
15828+ _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
15829+ if (pip->cmn.is_compute) {
15830+ NSError* err = NULL;
15831+ MTLComputePipelineDescriptor* cp_desc = [[MTLComputePipelineDescriptor alloc] init];
15832+ cp_desc.computeFunction = _sg_mtl_id(shd->mtl.compute_func.mtl_func);
15833+ cp_desc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true;
15834+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
15835+ const _sg_shader_view_t* view = &shd->cmn.views[i];
15836+ if (view->view_type != SG_VIEWTYPE_STORAGEBUFFER) {
15837+ continue;
15838+ }
15839+ if (!view->sbuf_readonly) {
15840+ continue;
15841+ }
15842+ SOKOL_ASSERT(view->stage == SG_SHADERSTAGE_COMPUTE);
15843+ const NSUInteger mtl_slot = shd->mtl.view_buffer_texture_n[i];
15844+ SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_BUFFER_BINDINGS);
15845+ cp_desc.buffers[mtl_slot].mutability = MTLMutabilityImmutable;
15846+ }
15847+ #if defined(SOKOL_DEBUG)
15848+ if (desc->label) {
15849+ cp_desc.label = [NSString stringWithFormat:@"%s", desc->label];
15850+ }
15851+ #endif
15852+ id<MTLComputePipelineState> mtl_cps = [_sg.mtl.device
15853+ newComputePipelineStateWithDescriptor:cp_desc
15854+ options:MTLPipelineOptionNone
15855+ reflection:nil
15856+ error:&err];
15857+ _SG_OBJC_RELEASE(cp_desc);
15858+ if (nil == mtl_cps) {
15859+ SOKOL_ASSERT(err);
15860+ _SG_ERROR(METAL_CREATE_CPS_FAILED);
15861+ _SG_LOGMSG(METAL_CREATE_CPS_OUTPUT, [err.localizedDescription UTF8String]);
15862+ return SG_RESOURCESTATE_FAILED;
15863+ }
15864+ pip->mtl.cps = _sg_mtl_add_resource(mtl_cps);
15865+ _SG_OBJC_RELEASE(mtl_cps);
15866+ pip->mtl.threads_per_threadgroup = shd->mtl.threads_per_threadgroup;
15867+ } else {
15868+ sg_primitive_type prim_type = desc->primitive_type;
15869+ pip->mtl.prim_type = _sg_mtl_primitive_type(prim_type);
15870+ pip->mtl.index_size = _sg_mtl_index_size(pip->cmn.index_type);
15871+ if (SG_INDEXTYPE_NONE != pip->cmn.index_type) {
15872+ pip->mtl.index_type = _sg_mtl_index_type(pip->cmn.index_type);
15873+ }
15874+ pip->mtl.cull_mode = _sg_mtl_cull_mode(desc->cull_mode);
15875+ pip->mtl.winding = _sg_mtl_winding(desc->face_winding);
15876+ pip->mtl.stencil_ref = desc->stencil.ref;
15877+
15878+ // create vertex-descriptor
15879+ MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor];
15880+ for (NSUInteger attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) {
15881+ const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index];
15882+ if (a_state->format == SG_VERTEXFORMAT_INVALID) {
15883+ break;
15884+ }
15885+ SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS);
15886+ SOKOL_ASSERT(pip->cmn.vertex_buffer_layout_active[a_state->buffer_index]);
15887+ vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_state->format);
15888+ vtx_desc.attributes[attr_index].offset = (NSUInteger)a_state->offset;
15889+ vtx_desc.attributes[attr_index].bufferIndex = _sg_mtl_vertexbuffer_bindslot((size_t)a_state->buffer_index);
15890+ }
15891+ for (NSUInteger layout_index = 0; layout_index < SG_MAX_VERTEXBUFFER_BINDSLOTS; layout_index++) {
15892+ if (pip->cmn.vertex_buffer_layout_active[layout_index]) {
15893+ const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index];
15894+ const NSUInteger mtl_vb_slot = _sg_mtl_vertexbuffer_bindslot(layout_index);
15895+ SOKOL_ASSERT(l_state->stride > 0);
15896+ vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_state->stride;
15897+ vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_state->step_func);
15898+ vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_state->step_rate;
15899+ }
15900+ }
15901+
15902+ // render-pipeline descriptor
15903+ MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init];
15904+ rp_desc.vertexDescriptor = vtx_desc;
15905+ SOKOL_ASSERT(shd->mtl.vertex_func.mtl_func != _SG_MTL_INVALID_SLOT_INDEX);
15906+ rp_desc.vertexFunction = _sg_mtl_id(shd->mtl.vertex_func.mtl_func);
15907+ SOKOL_ASSERT(shd->mtl.fragment_func.mtl_func != _SG_MTL_INVALID_SLOT_INDEX);
15908+ rp_desc.fragmentFunction = _sg_mtl_id(shd->mtl.fragment_func.mtl_func);
15909+ rp_desc.rasterSampleCount = (NSUInteger)desc->sample_count;
15910+ rp_desc.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled;
15911+ rp_desc.alphaToOneEnabled = NO;
15912+ rp_desc.rasterizationEnabled = YES;
15913+ rp_desc.depthAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format);
15914+ if (desc->depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL) {
15915+ rp_desc.stencilAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format);
15916+ }
15917+ for (NSUInteger i = 0; i < (NSUInteger)desc->color_count; i++) {
15918+ SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS);
15919+ const sg_color_target_state* cs = &desc->colors[i];
15920+ rp_desc.colorAttachments[i].pixelFormat = _sg_mtl_pixel_format(cs->pixel_format);
15921+ rp_desc.colorAttachments[i].writeMask = _sg_mtl_color_write_mask(cs->write_mask);
15922+ rp_desc.colorAttachments[i].blendingEnabled = cs->blend.enabled;
15923+ rp_desc.colorAttachments[i].alphaBlendOperation = _sg_mtl_blend_op(cs->blend.op_alpha);
15924+ rp_desc.colorAttachments[i].rgbBlendOperation = _sg_mtl_blend_op(cs->blend.op_rgb);
15925+ rp_desc.colorAttachments[i].destinationAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_alpha);
15926+ rp_desc.colorAttachments[i].destinationRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_rgb);
15927+ rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_alpha);
15928+ rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_rgb);
15929+ }
15930+ // Set buffer mutability for all buffers (vertex buffers and storage buffers).
15931+ // For vertex buffer it is guaranteed that neither the GPU nor CPU will update their content
15932+ // as long as it is in flight (since dynamic buffers are double-buffered, and vertex-buffers
15933+ // are not updated by the GPU).
15934+ // For storage buffer the same double-buffering applies, and if they are applied
15935+ // to the vertex- or fragment-stage must be declared as readonly in the shader.
15936+ for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) {
15937+ if (pip->cmn.vertex_buffer_layout_active[i]) {
15938+ const NSUInteger mtl_slot = _sg_mtl_vertexbuffer_bindslot(i);
15939+ rp_desc.vertexBuffers[mtl_slot].mutability = MTLMutabilityImmutable;
15940+ }
15941+ }
15942+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
15943+ const _sg_shader_view_t* view = &shd->cmn.views[i];
15944+ if (view->view_type != SG_VIEWTYPE_STORAGEBUFFER) {
15945+ continue;
15946+ }
15947+ const sg_shader_stage stage = view->stage;
15948+ SOKOL_ASSERT(view->stage != SG_SHADERSTAGE_COMPUTE);
15949+ SOKOL_ASSERT(view->sbuf_readonly);
15950+ const NSUInteger mtl_slot = shd->mtl.view_buffer_texture_n[i];
15951+ SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_BUFFER_BINDINGS);
15952+ if (stage == SG_SHADERSTAGE_VERTEX) {
15953+ rp_desc.vertexBuffers[mtl_slot].mutability = MTLMutabilityImmutable;
15954+ } else if (stage == SG_SHADERSTAGE_FRAGMENT) {
15955+ rp_desc.fragmentBuffers[mtl_slot].mutability = MTLMutabilityImmutable;
15956+ }
15957+ }
15958+ #if defined(SOKOL_DEBUG)
15959+ if (desc->label) {
15960+ rp_desc.label = [NSString stringWithFormat:@"%s", desc->label];
15961+ }
15962+ #endif
15963+ NSError* err = NULL;
15964+ id<MTLRenderPipelineState> mtl_rps = [_sg.mtl.device newRenderPipelineStateWithDescriptor:rp_desc error:&err];
15965+ _SG_OBJC_RELEASE(rp_desc);
15966+ if (nil == mtl_rps) {
15967+ SOKOL_ASSERT(err);
15968+ _SG_ERROR(METAL_CREATE_RPS_FAILED);
15969+ _SG_LOGMSG(METAL_CREATE_RPS_OUTPUT, [err.localizedDescription UTF8String]);
15970+ return SG_RESOURCESTATE_FAILED;
15971+ }
15972+ pip->mtl.rps = _sg_mtl_add_resource(mtl_rps);
15973+ _SG_OBJC_RELEASE(mtl_rps);
15974+
15975+ // depth-stencil-state
15976+ MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init];
15977+ ds_desc.depthCompareFunction = _sg_mtl_compare_func(desc->depth.compare);
15978+ ds_desc.depthWriteEnabled = desc->depth.write_enabled;
15979+ if (desc->stencil.enabled) {
15980+ const sg_stencil_face_state* sb = &desc->stencil.back;
15981+ ds_desc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
15982+ ds_desc.backFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sb->fail_op);
15983+ ds_desc.backFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sb->depth_fail_op);
15984+ ds_desc.backFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sb->pass_op);
15985+ ds_desc.backFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sb->compare);
15986+ ds_desc.backFaceStencil.readMask = desc->stencil.read_mask;
15987+ ds_desc.backFaceStencil.writeMask = desc->stencil.write_mask;
15988+ const sg_stencil_face_state* sf = &desc->stencil.front;
15989+ ds_desc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
15990+ ds_desc.frontFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sf->fail_op);
15991+ ds_desc.frontFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sf->depth_fail_op);
15992+ ds_desc.frontFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sf->pass_op);
15993+ ds_desc.frontFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sf->compare);
15994+ ds_desc.frontFaceStencil.readMask = desc->stencil.read_mask;
15995+ ds_desc.frontFaceStencil.writeMask = desc->stencil.write_mask;
15996+ }
15997+ #if defined(SOKOL_DEBUG)
15998+ if (desc->label) {
15999+ ds_desc.label = [NSString stringWithFormat:@"%s.dss", desc->label];
16000+ }
16001+ #endif
16002+ id<MTLDepthStencilState> mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc];
16003+ _SG_OBJC_RELEASE(ds_desc);
16004+ if (nil == mtl_dss) {
16005+ _SG_ERROR(METAL_CREATE_DSS_FAILED);
16006+ return SG_RESOURCESTATE_FAILED;
16007+ }
16008+ pip->mtl.dss = _sg_mtl_add_resource(mtl_dss);
16009+ _SG_OBJC_RELEASE(mtl_dss);
16010+ }
16011+ return SG_RESOURCESTATE_VALID;
16012+}
16013+
16014+_SOKOL_PRIVATE void _sg_mtl_discard_pipeline(_sg_pipeline_t* pip) {
16015+ SOKOL_ASSERT(pip);
16016+ // it's valid to call release resource with a 'null resource'
16017+ _sg_mtl_release_resource(_sg.frame_index, pip->mtl.cps);
16018+ _sg_mtl_release_resource(_sg.frame_index, pip->mtl.rps);
16019+ _sg_mtl_release_resource(_sg.frame_index, pip->mtl.dss);
16020+}
16021+
16022+_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_view(_sg_view_t* view, const sg_view_desc* desc) {
16023+ SOKOL_ASSERT(view && desc);
16024+ _SOKOL_UNUSED(desc);
16025+ if ((SG_VIEWTYPE_TEXTURE == view->cmn.type) || (SG_VIEWTYPE_STORAGEIMAGE == view->cmn.type)) {
16026+ const _sg_image_view_common_t* cmn = &view->cmn.img;
16027+ const _sg_image_t* img = _sg_image_ref_ptr(&cmn->ref);
16028+ SOKOL_ASSERT(cmn->mip_level_count >= 1);
16029+ SOKOL_ASSERT(cmn->slice_count >= 1);
16030+ for (int slot = 0; slot < img->cmn.num_slots; slot++) {
16031+ SOKOL_ASSERT(img->mtl.tex[slot] != _SG_MTL_INVALID_SLOT_INDEX);
16032+ id<MTLTexture> mtl_tex_view = [_sg_mtl_id(img->mtl.tex[slot])
16033+ newTextureViewWithPixelFormat: _sg_mtl_pixel_format(img->cmn.pixel_format)
16034+ textureType: _sg_mtl_texture_type(img->cmn.type, img->cmn.sample_count > 1)
16035+ levels: NSMakeRange((NSUInteger)cmn->mip_level, (NSUInteger)cmn->mip_level_count)
16036+ slices: NSMakeRange((NSUInteger)cmn->slice, (NSUInteger)cmn->slice_count)];
16037+ #if defined(SOKOL_DEBUG)
16038+ if (desc->label) {
16039+ mtl_tex_view.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot];
16040+ }
16041+ #endif
16042+ view->mtl.tex_view[slot] = _sg_mtl_add_resource(mtl_tex_view);
16043+ _SG_OBJC_RELEASE(mtl_tex_view);
16044+ }
16045+ }
16046+ return SG_RESOURCESTATE_VALID;
16047+}
16048+
16049+_SOKOL_PRIVATE void _sg_mtl_discard_view(_sg_view_t* view) {
16050+ SOKOL_ASSERT(view);
16051+ for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
16052+ // it's valid to call _sg_mtl_release_resource with a null handle
16053+ _sg_mtl_release_resource(_sg.frame_index, view->mtl.tex_view[i]);
16054+ }
16055+}
16056+
16057+_SOKOL_PRIVATE void _sg_mtl_bind_uniform_buffers(void) {
16058+ // In the Metal backend, uniform buffer bindings happen once in sg_begin_pass() and
16059+ // remain valid for the entire pass. Only binding offsets will be updated
16060+ // in sg_apply_uniforms()
16061+ if (_sg.cur_pass.is_compute) {
16062+ SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder);
16063+ for (size_t slot = 0; slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS; slot++) {
16064+ [_sg.mtl.compute_cmd_encoder
16065+ setBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index]
16066+ offset:0
16067+ atIndex:slot];
16068+ }
16069+ } else {
16070+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16071+ for (size_t slot = 0; slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS; slot++) {
16072+ [_sg.mtl.render_cmd_encoder
16073+ setVertexBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index]
16074+ offset:0
16075+ atIndex:slot];
16076+ [_sg.mtl.render_cmd_encoder
16077+ setFragmentBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index]
16078+ offset:0
16079+ atIndex:slot];
16080+ }
16081+ }
16082+}
16083+
16084+_SOKOL_PRIVATE void _sg_mtl_begin_compute_pass(const sg_pass* pass) {
16085+ SOKOL_ASSERT(pass); (void)pass;
16086+ SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer);
16087+ SOKOL_ASSERT(nil == _sg.mtl.compute_cmd_encoder);
16088+ SOKOL_ASSERT(nil == _sg.mtl.render_cmd_encoder);
16089+
16090+ _sg.mtl.compute_cmd_encoder = [_sg.mtl.cmd_buffer computeCommandEncoder];
16091+ if (nil == _sg.mtl.compute_cmd_encoder) {
16092+ _sg.cur_pass.valid = false;
16093+ return;
16094+ }
16095+
16096+ #if defined(SOKOL_DEBUG)
16097+ if (pass->label) {
16098+ _sg.mtl.compute_cmd_encoder.label = [NSString stringWithUTF8String:pass->label];
16099+ }
16100+ #endif
16101+}
16102+
16103+_SOKOL_PRIVATE void _sg_mtl_begin_render_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) {
16104+ SOKOL_ASSERT(pass && atts);
16105+ SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer);
16106+ SOKOL_ASSERT(nil == _sg.mtl.render_cmd_encoder);
16107+ SOKOL_ASSERT(nil == _sg.mtl.compute_cmd_encoder);
16108+
16109+ const sg_swapchain* swapchain = &pass->swapchain;
16110+ const sg_pass_action* action = &pass->action;
16111+
16112+ MTLRenderPassDescriptor* pass_desc = [MTLRenderPassDescriptor renderPassDescriptor];
16113+ SOKOL_ASSERT(pass_desc);
16114+ if (!atts->empty) {
16115+ // setup pass descriptor for offscreen rendering
16116+ for (NSUInteger i = 0; i < (NSUInteger)atts->num_color_views; i++) {
16117+ const _sg_view_t* color_view = atts->color_views[i];
16118+ SOKOL_ASSERT(color_view);
16119+ const _sg_view_t* resolve_view = atts->resolve_views[i];
16120+ const _sg_image_t* color_img = _sg_image_ref_ptr(&color_view->cmn.img.ref);
16121+ SOKOL_ASSERT(color_img->slot.state == SG_RESOURCESTATE_VALID);
16122+ SOKOL_ASSERT(color_img->cmn.active_slot == 0);
16123+ SOKOL_ASSERT(color_img->mtl.tex[0] != _SG_MTL_INVALID_SLOT_INDEX);
16124+ pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].load_action);
16125+ pass_desc.colorAttachments[i].storeAction = _sg_mtl_store_action(action->colors[i].store_action, resolve_view != 0);
16126+ sg_color c = action->colors[i].clear_value;
16127+ pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a);
16128+ pass_desc.colorAttachments[i].texture = _sg_mtl_id(color_img->mtl.tex[0]);
16129+ pass_desc.colorAttachments[i].level = (NSUInteger)color_view->cmn.img.mip_level;
16130+ switch (color_img->cmn.type) {
16131+ case SG_IMAGETYPE_CUBE:
16132+ case SG_IMAGETYPE_ARRAY:
16133+ pass_desc.colorAttachments[i].slice = (NSUInteger)color_view->cmn.img.slice;
16134+ break;
16135+ case SG_IMAGETYPE_3D:
16136+ pass_desc.colorAttachments[i].depthPlane = (NSUInteger)color_view->cmn.img.slice;
16137+ break;
16138+ default: break;
16139+ }
16140+ if (resolve_view) {
16141+ const _sg_image_t* resolve_img = _sg_image_ref_ptr(&resolve_view->cmn.img.ref);
16142+ SOKOL_ASSERT(resolve_img->slot.state == SG_RESOURCESTATE_VALID);
16143+ SOKOL_ASSERT(resolve_img->cmn.active_slot == 0);
16144+ SOKOL_ASSERT(resolve_img->mtl.tex[0] != _SG_MTL_INVALID_SLOT_INDEX);
16145+ pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_id(resolve_img->mtl.tex[0]);
16146+ pass_desc.colorAttachments[i].resolveLevel = (NSUInteger)resolve_view->cmn.img.mip_level;
16147+ switch (resolve_img->cmn.type) {
16148+ case SG_IMAGETYPE_CUBE:
16149+ case SG_IMAGETYPE_ARRAY:
16150+ pass_desc.colorAttachments[i].resolveSlice = (NSUInteger)resolve_view->cmn.img.slice;
16151+ break;
16152+ case SG_IMAGETYPE_3D:
16153+ pass_desc.colorAttachments[i].resolveDepthPlane = (NSUInteger)resolve_view->cmn.img.slice;
16154+ break;
16155+ default: break;
16156+ }
16157+ }
16158+ }
16159+ if (atts->ds_view) {
16160+ const _sg_view_t* ds_view = atts->ds_view;
16161+ const _sg_image_t* ds_img = _sg_image_ref_ptr(&ds_view->cmn.img.ref);
16162+ SOKOL_ASSERT(ds_img->slot.state == SG_RESOURCESTATE_VALID);
16163+ SOKOL_ASSERT(ds_img->cmn.active_slot == 0);
16164+ SOKOL_ASSERT(ds_img->mtl.tex[0] != _SG_MTL_INVALID_SLOT_INDEX);
16165+ pass_desc.depthAttachment.texture = _sg_mtl_id(ds_img->mtl.tex[0]);
16166+ pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action);
16167+ pass_desc.depthAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false);
16168+ pass_desc.depthAttachment.clearDepth = action->depth.clear_value;
16169+ switch (ds_img->cmn.type) {
16170+ case SG_IMAGETYPE_CUBE:
16171+ case SG_IMAGETYPE_ARRAY:
16172+ pass_desc.depthAttachment.slice = (NSUInteger)ds_view->cmn.img.slice;
16173+ break;
16174+ case SG_IMAGETYPE_3D:
16175+ pass_desc.depthAttachment.resolveDepthPlane = (NSUInteger)ds_view->cmn.img.slice;
16176+ break;
16177+ default: break;
16178+ }
16179+ if (_sg_is_depth_stencil_format(ds_img->cmn.pixel_format)) {
16180+ pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_img->mtl.tex[0]);
16181+ pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action);
16182+ pass_desc.stencilAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false);
16183+ pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value;
16184+ switch (ds_img->cmn.type) {
16185+ case SG_IMAGETYPE_CUBE:
16186+ case SG_IMAGETYPE_ARRAY:
16187+ pass_desc.stencilAttachment.slice = (NSUInteger)ds_view->cmn.img.slice;
16188+ break;
16189+ case SG_IMAGETYPE_3D:
16190+ pass_desc.stencilAttachment.resolveDepthPlane = (NSUInteger)ds_view->cmn.img.slice;
16191+ break;
16192+ default: break;
16193+ }
16194+ }
16195+ }
16196+ } else {
16197+ // setup pass descriptor for swapchain rendering
16198+ //
16199+ // NOTE: at least in macOS Sonoma this no longer seems to be the case, the
16200+ // current drawable is also valid in a minimized window
16201+ // ===
16202+ // an MTKView current_drawable will not be valid if window is minimized, don't do any rendering in this case
16203+ if (0 == swapchain->metal.current_drawable) {
16204+ _sg.cur_pass.valid = false;
16205+ return;
16206+ }
16207+ // pin the swapchain resources into memory so that they outlive their command buffer
16208+ // (this is necessary because the command buffer doesn't retain references)
16209+ int pass_desc_ref = _sg_mtl_add_resource(pass_desc);
16210+ _sg_mtl_release_resource(_sg.frame_index, pass_desc_ref);
16211+
16212+ _sg.mtl.cur_drawable = (__bridge id<CAMetalDrawable>) swapchain->metal.current_drawable;
16213+ if (swapchain->sample_count > 1) {
16214+ // multi-sampling: render into msaa texture, resolve into drawable texture
16215+ id<MTLTexture> msaa_tex = (__bridge id<MTLTexture>) swapchain->metal.msaa_color_texture;
16216+ SOKOL_ASSERT(msaa_tex != nil);
16217+ pass_desc.colorAttachments[0].texture = msaa_tex;
16218+ pass_desc.colorAttachments[0].resolveTexture = _sg.mtl.cur_drawable.texture;
16219+ pass_desc.colorAttachments[0].storeAction = MTLStoreActionMultisampleResolve;
16220+ } else {
16221+ // non-msaa: render into current_drawable
16222+ pass_desc.colorAttachments[0].texture = _sg.mtl.cur_drawable.texture;
16223+ pass_desc.colorAttachments[0].storeAction = MTLStoreActionStore;
16224+ }
16225+ pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].load_action);
16226+ const sg_color c = action->colors[0].clear_value;
16227+ pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a);
16228+
16229+ // optional depth-stencil texture
16230+ if (swapchain->metal.depth_stencil_texture) {
16231+ id<MTLTexture> ds_tex = (__bridge id<MTLTexture>) swapchain->metal.depth_stencil_texture;
16232+ SOKOL_ASSERT(ds_tex != nil);
16233+ pass_desc.depthAttachment.texture = ds_tex;
16234+ pass_desc.depthAttachment.storeAction = MTLStoreActionDontCare;
16235+ pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action);
16236+ pass_desc.depthAttachment.clearDepth = action->depth.clear_value;
16237+ if (_sg_is_depth_stencil_format(swapchain->depth_format)) {
16238+ pass_desc.stencilAttachment.texture = ds_tex;
16239+ pass_desc.stencilAttachment.storeAction = MTLStoreActionDontCare;
16240+ pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action);
16241+ pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value;
16242+ }
16243+ }
16244+ }
16245+
16246+ // NOTE: at least in macOS Sonoma, the following is no longer the case, a valid
16247+ // render command encoder is also returned in a minimized window
16248+ // ===
16249+ // create a render command encoder, this might return nil if window is minimized
16250+ _sg.mtl.render_cmd_encoder = [_sg.mtl.cmd_buffer renderCommandEncoderWithDescriptor:pass_desc];
16251+ if (nil == _sg.mtl.render_cmd_encoder) {
16252+ _sg.cur_pass.valid = false;
16253+ return;
16254+ }
16255+
16256+ #if defined(SOKOL_DEBUG)
16257+ if (pass->label) {
16258+ _sg.mtl.render_cmd_encoder.label = [NSString stringWithUTF8String:pass->label];
16259+ }
16260+ #endif
16261+}
16262+
16263+_SOKOL_PRIVATE void _sg_mtl_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) {
16264+ SOKOL_ASSERT(pass && atts);
16265+ SOKOL_ASSERT(_sg.mtl.cmd_queue);
16266+ SOKOL_ASSERT(nil == _sg.mtl.compute_cmd_encoder);
16267+ SOKOL_ASSERT(nil == _sg.mtl.render_cmd_encoder);
16268+ SOKOL_ASSERT(nil == _sg.mtl.cur_drawable);
16269+ _sg_mtl_clear_state_cache();
16270+
16271+ // if this is the first pass in the frame, create one command buffer and blit-cmd-encoder for the entire frame
16272+ if (nil == _sg.mtl.cmd_buffer) {
16273+ // block until the oldest frame in flight has finished
16274+ dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER);
16275+ if (_sg.desc.metal.use_command_buffer_with_retained_references) {
16276+ _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBuffer];
16277+ } else {
16278+ _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences];
16279+ }
16280+ [_sg.mtl.cmd_buffer enqueue];
16281+ [_sg.mtl.cmd_buffer addCompletedHandler:^(id<MTLCommandBuffer> cmd_buf) {
16282+ // NOTE: this code is called on a different thread!
16283+ _SOKOL_UNUSED(cmd_buf);
16284+ dispatch_semaphore_signal(_sg.mtl.sem);
16285+ }];
16286+ }
16287+
16288+ // if this is first pass in frame, get uniform buffer base pointer
16289+ if (0 == _sg.mtl.cur_ub_base_ptr) {
16290+ _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents];
16291+ }
16292+
16293+ if (pass->compute) {
16294+ _sg_mtl_begin_compute_pass(pass);
16295+ } else {
16296+ _sg_mtl_begin_render_pass(pass, atts);
16297+ }
16298+
16299+ // bind uniform buffers, those bindings remain valid for the entire pass
16300+ if (_sg.cur_pass.valid) {
16301+ _sg_mtl_bind_uniform_buffers();
16302+ }
16303+}
16304+
16305+_SOKOL_PRIVATE void _sg_mtl_end_pass(const _sg_attachments_ptrs_t* atts) {
16306+ _SOKOL_UNUSED(atts);
16307+ if (nil != _sg.mtl.render_cmd_encoder) {
16308+ [_sg.mtl.render_cmd_encoder endEncoding];
16309+ // NOTE: MTLRenderCommandEncoder is autoreleased
16310+ _sg.mtl.render_cmd_encoder = nil;
16311+ }
16312+ if (nil != _sg.mtl.compute_cmd_encoder) {
16313+ [_sg.mtl.compute_cmd_encoder endEncoding];
16314+ // NOTE: MTLComputeCommandEncoder is autoreleased
16315+ _sg.mtl.compute_cmd_encoder = nil;
16316+ }
16317+ // if this is a swapchain pass, present the drawable
16318+ if (nil != _sg.mtl.cur_drawable) {
16319+ [_sg.mtl.cmd_buffer presentDrawable:_sg.mtl.cur_drawable];
16320+ _sg.mtl.cur_drawable = nil;
16321+ }
16322+}
16323+
16324+_SOKOL_PRIVATE void _sg_mtl_commit(void) {
16325+ SOKOL_ASSERT(nil == _sg.mtl.render_cmd_encoder);
16326+ SOKOL_ASSERT(nil == _sg.mtl.compute_cmd_encoder);
16327+
16328+ // commit the frame's command buffer
16329+ if (_sg.mtl.cmd_buffer) {
16330+ [_sg.mtl.cmd_buffer commit];
16331+ }
16332+
16333+ // garbage-collect resources pending for release
16334+ _sg_mtl_garbage_collect(_sg.frame_index);
16335+
16336+ // rotate uniform buffer slot
16337+ if (++_sg.mtl.cur_frame_rotate_index >= SG_NUM_INFLIGHT_FRAMES) {
16338+ _sg.mtl.cur_frame_rotate_index = 0;
16339+ }
16340+ _sg.mtl.cur_ub_offset = 0;
16341+ _sg.mtl.cur_ub_base_ptr = 0;
16342+ // NOTE: MTLCommandBuffer is autoreleased
16343+ _sg.mtl.cmd_buffer = nil;
16344+}
16345+
16346+_SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) {
16347+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16348+ SOKOL_ASSERT(_sg.cur_pass.dim.height > 0);
16349+ MTLViewport vp;
16350+ vp.originX = (double) x;
16351+ vp.originY = (double) (origin_top_left ? y : (_sg.cur_pass.dim.height - (y + h)));
16352+ vp.width = (double) w;
16353+ vp.height = (double) h;
16354+ vp.znear = 0.0;
16355+ vp.zfar = 1.0;
16356+ [_sg.mtl.render_cmd_encoder setViewport:vp];
16357+}
16358+
16359+_SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) {
16360+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16361+ SOKOL_ASSERT(_sg.cur_pass.dim.width > 0);
16362+ SOKOL_ASSERT(_sg.cur_pass.dim.height > 0);
16363+ // clip against framebuffer rect
16364+ const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.dim.width, _sg.cur_pass.dim.height);
16365+ MTLScissorRect r;
16366+ r.x = (NSUInteger)clip.x;
16367+ r.y = (NSUInteger) (origin_top_left ? clip.y : (_sg.cur_pass.dim.height - (clip.y + clip.h)));
16368+ r.width = (NSUInteger)clip.w;
16369+ r.height = (NSUInteger)clip.h;
16370+ [_sg.mtl.render_cmd_encoder setScissorRect:r];
16371+}
16372+
16373+_SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) {
16374+ SOKOL_ASSERT(pip);
16375+ if (!_sg_sref_slot_eql(&_sg.mtl.cache.cur_pip, &pip->slot)) {
16376+ _sg.mtl.cache.cur_pip = _sg_sref(&pip->slot);
16377+ if (pip->cmn.is_compute) {
16378+ SOKOL_ASSERT(_sg.cur_pass.is_compute);
16379+ SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder);
16380+ SOKOL_ASSERT(pip->mtl.cps != _SG_MTL_INVALID_SLOT_INDEX);
16381+ [_sg.mtl.compute_cmd_encoder setComputePipelineState:_sg_mtl_id(pip->mtl.cps)];
16382+ } else {
16383+ SOKOL_ASSERT(!_sg.cur_pass.is_compute);
16384+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16385+ sg_color c = pip->cmn.blend_color;
16386+ [_sg.mtl.render_cmd_encoder setBlendColorRed:c.r green:c.g blue:c.b alpha:c.a];
16387+ _sg_stats_inc(metal.pipeline.num_set_blend_color);
16388+ [_sg.mtl.render_cmd_encoder setCullMode:pip->mtl.cull_mode];
16389+ _sg_stats_inc(metal.pipeline.num_set_cull_mode);
16390+ [_sg.mtl.render_cmd_encoder setFrontFacingWinding:pip->mtl.winding];
16391+ _sg_stats_inc(metal.pipeline.num_set_front_facing_winding);
16392+ [_sg.mtl.render_cmd_encoder setStencilReferenceValue:pip->mtl.stencil_ref];
16393+ _sg_stats_inc(metal.pipeline.num_set_stencil_reference_value);
16394+ [_sg.mtl.render_cmd_encoder setDepthBias:pip->cmn.depth.bias slopeScale:pip->cmn.depth.bias_slope_scale clamp:pip->cmn.depth.bias_clamp];
16395+ _sg_stats_inc(metal.pipeline.num_set_depth_bias);
16396+ SOKOL_ASSERT(pip->mtl.rps != _SG_MTL_INVALID_SLOT_INDEX);
16397+ [_sg.mtl.render_cmd_encoder setRenderPipelineState:_sg_mtl_id(pip->mtl.rps)];
16398+ _sg_stats_inc(metal.pipeline.num_set_render_pipeline_state);
16399+ SOKOL_ASSERT(pip->mtl.dss != _SG_MTL_INVALID_SLOT_INDEX);
16400+ [_sg.mtl.render_cmd_encoder setDepthStencilState:_sg_mtl_id(pip->mtl.dss)];
16401+ _sg_stats_inc(metal.pipeline.num_set_depth_stencil_state);
16402+ }
16403+ }
16404+}
16405+
16406+_SOKOL_PRIVATE int _sg_mtl_cache_buf_cmp(const _sg_mtl_cache_buf_t* item, const _sg_slot_t* slot, int active_slot, int offset) {
16407+ int res = _SG_MTL_CACHE_CMP_EQUAL;
16408+ if (!_sg_sref_slot_eql(&item->sref, slot)) {
16409+ res |= _SG_MTL_CACHE_CMP_SREF;
16410+ }
16411+ if (item->active_slot != active_slot) {
16412+ res |= _SG_MTL_CACHE_CMP_ACTIVESLOT;
16413+ }
16414+ if (item->offset != offset) {
16415+ res |= _SG_MTL_CACHE_CMP_OFFSET;
16416+ }
16417+ return res;
16418+}
16419+
16420+_SOKOL_PRIVATE void _sg_mtl_cache_buf_upd(_sg_mtl_cache_buf_t* item, const _sg_slot_t* slot, int active_slot, int offset) {
16421+ item->sref = _sg_sref(slot);
16422+ item->offset = offset;
16423+ item->active_slot = active_slot;
16424+}
16425+
16426+_SOKOL_PRIVATE int _sg_mtl_cache_tex_cmp(const _sg_mtl_cache_tex_t* item, const _sg_slot_t* slot, int active_slot) {
16427+ int res = _SG_MTL_CACHE_CMP_EQUAL;
16428+ if (!_sg_sref_slot_eql(&item->sref, slot)) {
16429+ res |= _SG_MTL_CACHE_CMP_SREF;
16430+ }
16431+ if (item->active_slot != active_slot) {
16432+ res |= _SG_MTL_CACHE_CMP_ACTIVESLOT;
16433+ }
16434+ return res;
16435+}
16436+
16437+_SOKOL_PRIVATE void _sg_mtl_cache_tex_upd(_sg_mtl_cache_tex_t* item, const _sg_slot_t* slot, int active_slot) {
16438+ item->sref = _sg_sref(slot);
16439+ item->active_slot = active_slot;
16440+}
16441+
16442+
16443+_SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_ptrs_t* bnd) {
16444+ SOKOL_ASSERT(bnd);
16445+ SOKOL_ASSERT(bnd->pip);
16446+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader);
16447+
16448+ // don't set vertex- and index-buffers in compute passes
16449+ if (!_sg.cur_pass.is_compute) {
16450+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16451+ // store index buffer binding, this will be needed later in sg_draw()
16452+ _sg.mtl.cache.cur_ibuf = _sg_buffer_ref(bnd->ib);
16453+ _sg.mtl.cache.cur_ibuf_offset = bnd->ib_offset;
16454+ if (bnd->ib) {
16455+ SOKOL_ASSERT(bnd->pip->cmn.index_type != SG_INDEXTYPE_NONE);
16456+ } else {
16457+ SOKOL_ASSERT(bnd->pip->cmn.index_type == SG_INDEXTYPE_NONE);
16458+ }
16459+ // apply vertex buffers
16460+ for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) {
16461+ const _sg_buffer_t* vb = bnd->vbs[i];
16462+ if (vb == 0) {
16463+ continue;
16464+ }
16465+ const NSUInteger mtl_slot = _sg_mtl_vertexbuffer_bindslot(i);
16466+ SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_BUFFER_BINDINGS);
16467+ const int active_slot = vb->cmn.active_slot;
16468+ SOKOL_ASSERT(vb->mtl.buf[active_slot] != _SG_MTL_INVALID_SLOT_INDEX);
16469+ const int offset = bnd->vb_offsets[i];
16470+ _sg_mtl_cache_buf_t* cache_item = &_sg.mtl.cache.cur_vsbufs[i];
16471+ const int cmp = _sg_mtl_cache_buf_cmp(cache_item, &vb->slot, active_slot, offset);
16472+ if (cmp != _SG_MTL_CACHE_CMP_EQUAL) {
16473+ _sg_mtl_cache_buf_upd(cache_item, &vb->slot, active_slot, offset);
16474+ if (0 == (cmp & ~_SG_MTL_CACHE_CMP_OFFSET)) {
16475+ // only vertex buffer offset has changed
16476+ [_sg.mtl.render_cmd_encoder setVertexBufferOffset:(NSUInteger)offset atIndex:mtl_slot];
16477+ _sg_stats_inc(metal.bindings.num_set_vertex_buffer_offset);
16478+ } else {
16479+ [_sg.mtl.render_cmd_encoder setVertexBuffer:_sg_mtl_id(vb->mtl.buf[active_slot]) offset:(NSUInteger)offset atIndex:mtl_slot];
16480+ _sg_stats_inc(metal.bindings.num_set_vertex_buffer);
16481+ }
16482+ } else {
16483+ _sg_stats_inc(metal.bindings.num_skip_redundant_vertex_buffer);
16484+ }
16485+ }
16486+ }
16487+
16488+ // apply view bindings (textures, storage images, storage buffers)
16489+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
16490+ const _sg_view_t* view = bnd->views[i];
16491+ if (0 == view) {
16492+ continue;
16493+ }
16494+ const _sg_shader_view_t* shd_view = &shd->cmn.views[i];
16495+ const sg_shader_stage stage = shd_view->stage;
16496+ SOKOL_ASSERT((stage == SG_SHADERSTAGE_VERTEX)
16497+ || (stage == SG_SHADERSTAGE_FRAGMENT)
16498+ || (stage == SG_SHADERSTAGE_COMPUTE));
16499+ SOKOL_ASSERT((shd_view->view_type == SG_VIEWTYPE_TEXTURE)
16500+ || (shd_view->view_type == SG_VIEWTYPE_STORAGEBUFFER)
16501+ || (shd_view->view_type == SG_VIEWTYPE_STORAGEIMAGE));
16502+ const NSUInteger mtl_slot = shd->mtl.view_buffer_texture_n[i];
16503+
16504+ // same handling for textures and storage images
16505+ if ((shd_view->view_type == SG_VIEWTYPE_TEXTURE) || (shd_view->view_type == SG_VIEWTYPE_STORAGEIMAGE)) {
16506+ SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_TEXTURE_BINDINGS);
16507+ const int active_slot = _sg_image_ref_ptr(&view->cmn.img.ref)->cmn.active_slot;
16508+ SOKOL_ASSERT(view->mtl.tex_view[active_slot] != _SG_MTL_INVALID_SLOT_INDEX);
16509+ if (stage == SG_SHADERSTAGE_VERTEX) {
16510+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16511+ _sg_mtl_cache_tex_t* cache_item = &_sg.mtl.cache.cur_vstexs[mtl_slot];
16512+ const int cmp = _sg_mtl_cache_tex_cmp(cache_item, &view->slot, active_slot);
16513+ if (cmp != _SG_MTL_CACHE_CMP_EQUAL) {
16514+ _sg_mtl_cache_tex_upd(cache_item, &view->slot, active_slot);
16515+ [_sg.mtl.render_cmd_encoder setVertexTexture:_sg_mtl_id(view->mtl.tex_view[active_slot]) atIndex:mtl_slot];
16516+ _sg_stats_inc(metal.bindings.num_set_vertex_texture);
16517+ } else {
16518+ _sg_stats_inc(metal.bindings.num_skip_redundant_vertex_texture);
16519+ }
16520+ } else if (stage == SG_SHADERSTAGE_FRAGMENT) {
16521+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16522+ _sg_mtl_cache_tex_t* cache_item = &_sg.mtl.cache.cur_fstexs[mtl_slot];
16523+ const int cmp = _sg_mtl_cache_tex_cmp(cache_item, &view->slot, active_slot);
16524+ if (cmp != _SG_MTL_CACHE_CMP_EQUAL) {
16525+ _sg_mtl_cache_tex_upd(cache_item, &view->slot, active_slot);
16526+ [_sg.mtl.render_cmd_encoder setFragmentTexture:_sg_mtl_id(view->mtl.tex_view[active_slot]) atIndex:mtl_slot];
16527+ _sg_stats_inc(metal.bindings.num_set_fragment_texture);
16528+ } else {
16529+ _sg_stats_inc(metal.bindings.num_skip_redundant_fragment_texture);
16530+ }
16531+ } else if (stage == SG_SHADERSTAGE_COMPUTE) {
16532+ SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder);
16533+ _sg_mtl_cache_tex_t* cache_item = &_sg.mtl.cache.cur_cstexs[mtl_slot];
16534+ const int cmp = _sg_mtl_cache_tex_cmp(cache_item, &view->slot, active_slot);
16535+ if (cmp != _SG_MTL_CACHE_CMP_EQUAL) {
16536+ _sg_mtl_cache_tex_upd(cache_item, &view->slot, active_slot);
16537+ [_sg.mtl.compute_cmd_encoder setTexture:_sg_mtl_id(view->mtl.tex_view[active_slot]) atIndex:mtl_slot];
16538+ _sg_stats_inc(metal.bindings.num_set_compute_texture);
16539+ } else {
16540+ _sg_stats_inc(metal.bindings.num_skip_redundant_compute_texture);
16541+ }
16542+ } else SOKOL_UNREACHABLE;
16543+ } else if (shd_view->view_type == SG_VIEWTYPE_STORAGEBUFFER) {
16544+ SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_UB_SBUF_BINDINGS);
16545+ const _sg_buffer_t* sbuf = _sg_buffer_ref_ptr(&view->cmn.buf.ref);
16546+ const int active_slot = sbuf->cmn.active_slot;
16547+ SOKOL_ASSERT(sbuf->mtl.buf[sbuf->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX);
16548+ const int offset = view->cmn.buf.offset;
16549+ if (stage == SG_SHADERSTAGE_VERTEX) {
16550+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16551+ _sg_mtl_cache_buf_t* cache_item = &_sg.mtl.cache.cur_vsbufs[mtl_slot];
16552+ const int cmp = _sg_mtl_cache_buf_cmp(cache_item, &sbuf->slot, active_slot, offset);
16553+ if (cmp != _SG_MTL_CACHE_CMP_EQUAL) {
16554+ _sg_mtl_cache_buf_upd(cache_item, &sbuf->slot, active_slot, offset);
16555+ if (0 == (cmp & ~_SG_MTL_CACHE_CMP_OFFSET)) {
16556+ // only offset has changed
16557+ [_sg.mtl.render_cmd_encoder setVertexBufferOffset:(NSUInteger)offset atIndex:mtl_slot];
16558+ _sg_stats_inc(metal.bindings.num_set_vertex_buffer_offset);
16559+ } else {
16560+ [_sg.mtl.render_cmd_encoder setVertexBuffer:_sg_mtl_id(sbuf->mtl.buf[sbuf->cmn.active_slot]) offset:(NSUInteger)offset atIndex:mtl_slot];
16561+ _sg_stats_inc(metal.bindings.num_set_vertex_buffer);
16562+ }
16563+ } else {
16564+ _sg_stats_inc(metal.bindings.num_skip_redundant_vertex_buffer);
16565+ }
16566+ } else if (stage == SG_SHADERSTAGE_FRAGMENT) {
16567+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16568+ _sg_mtl_cache_buf_t* cache_item = &_sg.mtl.cache.cur_fsbufs[mtl_slot];
16569+ const int cmp = _sg_mtl_cache_buf_cmp(cache_item, &sbuf->slot, active_slot, offset);
16570+ if (cmp != _SG_MTL_CACHE_CMP_EQUAL) {
16571+ _sg_mtl_cache_buf_upd(cache_item, &sbuf->slot, active_slot, offset);
16572+ if (0 == (cmp & ~_SG_MTL_CACHE_CMP_OFFSET)) {
16573+ // only offset has changed
16574+ [_sg.mtl.render_cmd_encoder setFragmentBufferOffset:(NSUInteger)offset atIndex:mtl_slot];
16575+ _sg_stats_inc(metal.bindings.num_set_fragment_buffer_offset);
16576+ } else {
16577+ [_sg.mtl.render_cmd_encoder setFragmentBuffer:_sg_mtl_id(sbuf->mtl.buf[active_slot]) offset:(NSUInteger)offset atIndex:mtl_slot];
16578+ _sg_stats_inc(metal.bindings.num_set_fragment_buffer);
16579+ }
16580+ } else {
16581+ _sg_stats_inc(metal.bindings.num_skip_redundant_fragment_buffer);
16582+ }
16583+ } else if (stage == SG_SHADERSTAGE_COMPUTE) {
16584+ SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder);
16585+ _sg_mtl_cache_buf_t* cache_item = &_sg.mtl.cache.cur_csbufs[mtl_slot];
16586+ const int cmp = _sg_mtl_cache_buf_cmp(cache_item, &sbuf->slot, active_slot, offset);
16587+ if (cmp != _SG_MTL_CACHE_CMP_EQUAL) {
16588+ _sg_mtl_cache_buf_upd(cache_item, &sbuf->slot, active_slot, offset);
16589+ if (0 == (cmp & ~_SG_MTL_CACHE_CMP_OFFSET)) {
16590+ // only offset has changed
16591+ [_sg.mtl.compute_cmd_encoder setBufferOffset:(NSUInteger)offset atIndex:mtl_slot];
16592+ _sg_stats_inc(metal.bindings.num_set_compute_buffer_offset);
16593+ } else {
16594+ [_sg.mtl.compute_cmd_encoder setBuffer:_sg_mtl_id(sbuf->mtl.buf[active_slot]) offset:(NSUInteger)offset atIndex:mtl_slot];
16595+ _sg_stats_inc(metal.bindings.num_set_compute_buffer);
16596+ }
16597+ } else {
16598+ _sg_stats_inc(metal.bindings.num_skip_redundant_compute_buffer);
16599+ }
16600+ }
16601+ } else SOKOL_UNREACHABLE;
16602+ }
16603+
16604+ // apply sampler bindings
16605+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
16606+ const _sg_sampler_t* smp = bnd->smps[i];
16607+ if (smp == 0) {
16608+ continue;
16609+ }
16610+ SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX);
16611+ const sg_shader_stage stage = shd->cmn.samplers[i].stage;
16612+ SOKOL_ASSERT((stage == SG_SHADERSTAGE_VERTEX) || (stage == SG_SHADERSTAGE_FRAGMENT) || (stage == SG_SHADERSTAGE_COMPUTE));
16613+ const NSUInteger mtl_slot = shd->mtl.smp_sampler_n[i];
16614+ SOKOL_ASSERT(mtl_slot < _SG_MTL_MAX_STAGE_SAMPLER_BINDINGS);
16615+ if (stage == SG_SHADERSTAGE_VERTEX) {
16616+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16617+ if (!_sg_sref_slot_eql(&_sg.mtl.cache.cur_vssmps[mtl_slot], &smp->slot)) {
16618+ _sg.mtl.cache.cur_vssmps[mtl_slot] = _sg_sref(&smp->slot);
16619+ [_sg.mtl.render_cmd_encoder setVertexSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:mtl_slot];
16620+ _sg_stats_inc(metal.bindings.num_set_vertex_sampler_state);
16621+ } else {
16622+ _sg_stats_inc(metal.bindings.num_skip_redundant_vertex_sampler_state);
16623+ }
16624+ } else if (stage == SG_SHADERSTAGE_FRAGMENT) {
16625+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16626+ if (!_sg_sref_slot_eql(&_sg.mtl.cache.cur_fssmps[mtl_slot], &smp->slot)) {
16627+ _sg.mtl.cache.cur_fssmps[mtl_slot] = _sg_sref(&smp->slot);
16628+ [_sg.mtl.render_cmd_encoder setFragmentSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:mtl_slot];
16629+ _sg_stats_inc(metal.bindings.num_set_fragment_sampler_state);
16630+ } else {
16631+ _sg_stats_inc(metal.bindings.num_skip_redundant_fragment_sampler_state);
16632+ }
16633+ } else if (stage == SG_SHADERSTAGE_COMPUTE) {
16634+ SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder);
16635+ if (!_sg_sref_slot_eql(&_sg.mtl.cache.cur_cssmps[mtl_slot], &smp->slot)) {
16636+ _sg.mtl.cache.cur_cssmps[mtl_slot] = _sg_sref(&smp->slot);
16637+ [_sg.mtl.compute_cmd_encoder setSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:mtl_slot];
16638+ _sg_stats_inc(metal.bindings.num_set_compute_sampler_state);
16639+ } else {
16640+ _sg_stats_inc(metal.bindings.num_skip_redundant_compute_sampler_state);
16641+ }
16642+ } else SOKOL_UNREACHABLE;
16643+ }
16644+ return true;
16645+}
16646+
16647+_SOKOL_PRIVATE void _sg_mtl_apply_uniforms(int ub_slot, const sg_range* data) {
16648+ SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS));
16649+ SOKOL_ASSERT(((size_t)_sg.mtl.cur_ub_offset + data->size) <= (size_t)_sg.mtl.ub_size);
16650+ SOKOL_ASSERT((_sg.mtl.cur_ub_offset & (_SG_MTL_UB_ALIGN-1)) == 0);
16651+ const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip);
16652+ SOKOL_ASSERT(pip);
16653+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
16654+ SOKOL_ASSERT(data->size == shd->cmn.uniform_blocks[ub_slot].size);
16655+
16656+ const sg_shader_stage stage = shd->cmn.uniform_blocks[ub_slot].stage;
16657+ const NSUInteger mtl_slot = shd->mtl.ub_buffer_n[ub_slot];
16658+
16659+ // copy to global uniform buffer, record offset into cmd encoder, and advance offset
16660+ uint8_t* dst = &_sg.mtl.cur_ub_base_ptr[_sg.mtl.cur_ub_offset];
16661+ memcpy(dst, data->ptr, data->size);
16662+ if (stage == SG_SHADERSTAGE_VERTEX) {
16663+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16664+ [_sg.mtl.render_cmd_encoder setVertexBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:mtl_slot];
16665+ _sg_stats_inc(metal.uniforms.num_set_vertex_buffer_offset);
16666+ } else if (stage == SG_SHADERSTAGE_FRAGMENT) {
16667+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16668+ [_sg.mtl.render_cmd_encoder setFragmentBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:mtl_slot];
16669+ _sg_stats_inc(metal.uniforms.num_set_fragment_buffer_offset);
16670+ } else if (stage == SG_SHADERSTAGE_COMPUTE) {
16671+ SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder);
16672+ [_sg.mtl.compute_cmd_encoder setBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:mtl_slot];
16673+ _sg_stats_inc(metal.uniforms.num_set_compute_buffer_offset);
16674+ } else {
16675+ SOKOL_UNREACHABLE;
16676+ }
16677+ _sg.mtl.cur_ub_offset = _sg_roundup(_sg.mtl.cur_ub_offset + (int)data->size, _SG_MTL_UB_ALIGN);
16678+}
16679+
16680+_SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) {
16681+ SOKOL_ASSERT(nil != _sg.mtl.render_cmd_encoder);
16682+ const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip);
16683+ SOKOL_ASSERT(pip);
16684+ if (_sg.use_indexed_draw) {
16685+ // indexed rendering
16686+ const _sg_buffer_t* ib = _sg_buffer_ref_ptr(&_sg.mtl.cache.cur_ibuf);
16687+ SOKOL_ASSERT(ib && (ib->mtl.buf[ib->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX));
16688+ const NSUInteger index_buffer_offset = (NSUInteger) (_sg.mtl.cache.cur_ibuf_offset + base_element * pip->mtl.index_size);
16689+ [_sg.mtl.render_cmd_encoder drawIndexedPrimitives:pip->mtl.prim_type
16690+ indexCount:(NSUInteger)num_elements
16691+ indexType:pip->mtl.index_type
16692+ indexBuffer:_sg_mtl_id(ib->mtl.buf[ib->cmn.active_slot])
16693+ indexBufferOffset:index_buffer_offset
16694+ instanceCount:(NSUInteger)num_instances
16695+ baseVertex:base_vertex
16696+ baseInstance:(NSUInteger)base_instance];
16697+ } else {
16698+ // non-indexed rendering
16699+ [_sg.mtl.render_cmd_encoder drawPrimitives:pip->mtl.prim_type
16700+ vertexStart:(NSUInteger)base_element
16701+ vertexCount:(NSUInteger)num_elements
16702+ instanceCount:(NSUInteger)num_instances
16703+ baseInstance:(NSUInteger)base_instance];
16704+ }
16705+}
16706+
16707+_SOKOL_PRIVATE void _sg_mtl_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) {
16708+ SOKOL_ASSERT(nil != _sg.mtl.compute_cmd_encoder);
16709+ const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip);
16710+ SOKOL_ASSERT(pip);
16711+ const MTLSize thread_groups = MTLSizeMake(
16712+ (NSUInteger)num_groups_x,
16713+ (NSUInteger)num_groups_y,
16714+ (NSUInteger)num_groups_z);
16715+ const MTLSize threads_per_threadgroup = pip->mtl.threads_per_threadgroup;
16716+ [_sg.mtl.compute_cmd_encoder dispatchThreadgroups:thread_groups threadsPerThreadgroup:threads_per_threadgroup];
16717+}
16718+
16719+_SOKOL_PRIVATE void _sg_mtl_update_buffer(_sg_buffer_t* buf, const sg_range* data) {
16720+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
16721+ if (++buf->cmn.active_slot >= buf->cmn.num_slots) {
16722+ buf->cmn.active_slot = 0;
16723+ }
16724+ __unsafe_unretained id<MTLBuffer> mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]);
16725+ void* dst_ptr = [mtl_buf contents];
16726+ memcpy(dst_ptr, data->ptr, data->size);
16727+ #if defined(_SG_TARGET_MACOS)
16728+ if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) {
16729+ [mtl_buf didModifyRange:NSMakeRange(0, data->size)];
16730+ }
16731+ #endif
16732+}
16733+
16734+_SOKOL_PRIVATE void _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) {
16735+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
16736+ if (new_frame) {
16737+ if (++buf->cmn.active_slot >= buf->cmn.num_slots) {
16738+ buf->cmn.active_slot = 0;
16739+ }
16740+ }
16741+ __unsafe_unretained id<MTLBuffer> mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]);
16742+ uint8_t* dst_ptr = (uint8_t*) [mtl_buf contents];
16743+ dst_ptr += buf->cmn.append_pos;
16744+ memcpy(dst_ptr, data->ptr, data->size);
16745+ #if defined(_SG_TARGET_MACOS)
16746+ if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) {
16747+ [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)];
16748+ }
16749+ #endif
16750+}
16751+
16752+_SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data* data) {
16753+ SOKOL_ASSERT(img && data);
16754+ if (++img->cmn.active_slot >= img->cmn.num_slots) {
16755+ img->cmn.active_slot = 0;
16756+ }
16757+ __unsafe_unretained id<MTLTexture> mtl_tex = _sg_mtl_id(img->mtl.tex[img->cmn.active_slot]);
16758+ _sg_mtl_copy_image_data(img, mtl_tex, data);
16759+}
16760+
16761+_SOKOL_PRIVATE void _sg_mtl_push_debug_group(const char* name) {
16762+ SOKOL_ASSERT(name);
16763+ if (_sg.mtl.render_cmd_encoder) {
16764+ [_sg.mtl.render_cmd_encoder pushDebugGroup:[NSString stringWithUTF8String:name]];
16765+ } else if (_sg.mtl.compute_cmd_encoder) {
16766+ [_sg.mtl.compute_cmd_encoder pushDebugGroup:[NSString stringWithUTF8String:name]];
16767+ }
16768+}
16769+
16770+_SOKOL_PRIVATE void _sg_mtl_pop_debug_group(void) {
16771+ if (_sg.mtl.render_cmd_encoder) {
16772+ [_sg.mtl.render_cmd_encoder popDebugGroup];
16773+ } else if (_sg.mtl.compute_cmd_encoder) {
16774+ [_sg.mtl.compute_cmd_encoder popDebugGroup];
16775+ }
16776+}
16777+
16778+#pragma clang diagnostic pop // Intel Mac deprecations
16779+
16780+// ██ ██ ███████ ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████
16781+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██
16782+// ██ █ ██ █████ ██████ ██ ███ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██
16783+// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
16784+// ███ ███ ███████ ██████ ██████ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████
16785+//
16786+// >>webgpu
16787+// >>wgpu
16788+#elif defined(SOKOL_WGPU)
16789+
16790+_SOKOL_PRIVATE WGPUStringView _sg_wgpu_stringview(const char* str) {
16791+ WGPUStringView res;
16792+ if (str) {
16793+ res.data = str;
16794+ res.length = strlen(str);
16795+ } else {
16796+ res.data = 0;
16797+ res.length = 0;
16798+ }
16799+ return res;
16800+}
16801+
16802+_SOKOL_PRIVATE WGPUOptionalBool _sg_wgpu_optional_bool(bool b) {
16803+ return b ? WGPUOptionalBool_True : WGPUOptionalBool_False;
16804+}
16805+
16806+_SOKOL_PRIVATE WGPUBufferUsage _sg_wgpu_buffer_usage(const sg_buffer_usage* usg) {
16807+ int res = 0;
16808+ if (usg->vertex_buffer) {
16809+ res |= (int)WGPUBufferUsage_Vertex;
16810+ }
16811+ if (usg->index_buffer) {
16812+ res |= (int)WGPUBufferUsage_Index;
16813+ }
16814+ if (usg->storage_buffer) {
16815+ res |= (int)WGPUBufferUsage_Storage;
16816+ }
16817+ if (!usg->immutable) {
16818+ res |= (int)WGPUBufferUsage_CopyDst;
16819+ }
16820+ return (WGPUBufferUsage)res;
16821+}
16822+
16823+_SOKOL_PRIVATE WGPULoadOp _sg_wgpu_load_op(WGPUTextureView view, sg_load_action a) {
16824+ if (0 == view) {
16825+ return WGPULoadOp_Undefined;
16826+ } else switch (a) {
16827+ case SG_LOADACTION_CLEAR:
16828+ case SG_LOADACTION_DONTCARE:
16829+ return WGPULoadOp_Clear;
16830+ case SG_LOADACTION_LOAD:
16831+ return WGPULoadOp_Load;
16832+ default:
16833+ SOKOL_UNREACHABLE;
16834+ return WGPULoadOp_Force32;
16835+ }
16836+}
16837+
16838+_SOKOL_PRIVATE WGPUStoreOp _sg_wgpu_store_op(WGPUTextureView view, sg_store_action a) {
16839+ if (0 == view) {
16840+ return WGPUStoreOp_Undefined;
16841+ } else switch (a) {
16842+ case SG_STOREACTION_STORE:
16843+ return WGPUStoreOp_Store;
16844+ case SG_STOREACTION_DONTCARE:
16845+ return WGPUStoreOp_Discard;
16846+ default:
16847+ SOKOL_UNREACHABLE;
16848+ return WGPUStoreOp_Force32;
16849+ }
16850+}
16851+
16852+_SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_texture_view_dimension(sg_image_type t) {
16853+ switch (t) {
16854+ case SG_IMAGETYPE_2D: return WGPUTextureViewDimension_2D;
16855+ case SG_IMAGETYPE_CUBE: return WGPUTextureViewDimension_Cube;
16856+ case SG_IMAGETYPE_3D: return WGPUTextureViewDimension_3D;
16857+ case SG_IMAGETYPE_ARRAY: return WGPUTextureViewDimension_2DArray;
16858+ default: SOKOL_UNREACHABLE; return WGPUTextureViewDimension_Force32;
16859+ }
16860+}
16861+
16862+_SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_attachment_view_dimension(sg_image_type t) {
16863+ switch (t) {
16864+ case SG_IMAGETYPE_2D: return WGPUTextureViewDimension_2D;
16865+ case SG_IMAGETYPE_CUBE: return WGPUTextureViewDimension_2DArray; // not a bug
16866+ case SG_IMAGETYPE_3D: return WGPUTextureViewDimension_2D; // not a bug
16867+ case SG_IMAGETYPE_ARRAY: return WGPUTextureViewDimension_2DArray;
16868+ default: SOKOL_UNREACHABLE; return WGPUTextureViewDimension_Force32;
16869+ }
16870+}
16871+
16872+_SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_texture_dimension(sg_image_type t) {
16873+ if (SG_IMAGETYPE_3D == t) {
16874+ return WGPUTextureDimension_3D;
16875+ } else {
16876+ return WGPUTextureDimension_2D;
16877+ }
16878+}
16879+
16880+_SOKOL_PRIVATE WGPUTextureSampleType _sg_wgpu_texture_sample_type(sg_image_sample_type t, bool msaa) {
16881+ switch (t) {
16882+ case SG_IMAGESAMPLETYPE_FLOAT: return msaa ? WGPUTextureSampleType_UnfilterableFloat : WGPUTextureSampleType_Float;
16883+ case SG_IMAGESAMPLETYPE_DEPTH: return WGPUTextureSampleType_Depth;
16884+ case SG_IMAGESAMPLETYPE_SINT: return WGPUTextureSampleType_Sint;
16885+ case SG_IMAGESAMPLETYPE_UINT: return WGPUTextureSampleType_Uint;
16886+ case SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT: return WGPUTextureSampleType_UnfilterableFloat;
16887+ default: SOKOL_UNREACHABLE; return WGPUTextureSampleType_Force32;
16888+ }
16889+}
16890+
16891+_SOKOL_PRIVATE WGPUSamplerBindingType _sg_wgpu_sampler_binding_type(sg_sampler_type t) {
16892+ switch (t) {
16893+ case SG_SAMPLERTYPE_FILTERING: return WGPUSamplerBindingType_Filtering;
16894+ case SG_SAMPLERTYPE_COMPARISON: return WGPUSamplerBindingType_Comparison;
16895+ case SG_SAMPLERTYPE_NONFILTERING: return WGPUSamplerBindingType_NonFiltering;
16896+ default: SOKOL_UNREACHABLE; return WGPUSamplerBindingType_Force32;
16897+ }
16898+}
16899+
16900+_SOKOL_PRIVATE WGPUAddressMode _sg_wgpu_sampler_address_mode(sg_wrap m) {
16901+ switch (m) {
16902+ case SG_WRAP_REPEAT:
16903+ return WGPUAddressMode_Repeat;
16904+ case SG_WRAP_CLAMP_TO_EDGE:
16905+ case SG_WRAP_CLAMP_TO_BORDER:
16906+ return WGPUAddressMode_ClampToEdge;
16907+ case SG_WRAP_MIRRORED_REPEAT:
16908+ return WGPUAddressMode_MirrorRepeat;
16909+ default:
16910+ SOKOL_UNREACHABLE;
16911+ return WGPUAddressMode_Force32;
16912+ }
16913+}
16914+
16915+_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmag_filter(sg_filter f) {
16916+ switch (f) {
16917+ case SG_FILTER_NEAREST:
16918+ return WGPUFilterMode_Nearest;
16919+ case SG_FILTER_LINEAR:
16920+ return WGPUFilterMode_Linear;
16921+ default:
16922+ SOKOL_UNREACHABLE;
16923+ return WGPUFilterMode_Force32;
16924+ }
16925+}
16926+
16927+_SOKOL_PRIVATE WGPUMipmapFilterMode _sg_wgpu_sampler_mipmap_filter(sg_filter f) {
16928+ switch (f) {
16929+ case SG_FILTER_NEAREST:
16930+ return WGPUMipmapFilterMode_Nearest;
16931+ case SG_FILTER_LINEAR:
16932+ return WGPUMipmapFilterMode_Linear;
16933+ default:
16934+ SOKOL_UNREACHABLE;
16935+ return WGPUMipmapFilterMode_Force32;
16936+ }
16937+}
16938+
16939+_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_indexformat(sg_index_type t) {
16940+ // NOTE: there's no WGPUIndexFormat_None
16941+ return (t == SG_INDEXTYPE_UINT16) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32;
16942+}
16943+
16944+_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_stripindexformat(sg_primitive_type prim_type, sg_index_type idx_type) {
16945+ if (idx_type == SG_INDEXTYPE_NONE) {
16946+ return WGPUIndexFormat_Undefined;
16947+ } else if ((prim_type == SG_PRIMITIVETYPE_LINE_STRIP) || (prim_type == SG_PRIMITIVETYPE_TRIANGLE_STRIP)) {
16948+ return _sg_wgpu_indexformat(idx_type);
16949+ } else {
16950+ return WGPUIndexFormat_Undefined;
16951+ }
16952+}
16953+
16954+_SOKOL_PRIVATE WGPUVertexStepMode _sg_wgpu_stepmode(sg_vertex_step s) {
16955+ return (s == SG_VERTEXSTEP_PER_VERTEX) ? WGPUVertexStepMode_Vertex : WGPUVertexStepMode_Instance;
16956+}
16957+
16958+_SOKOL_PRIVATE WGPUVertexFormat _sg_wgpu_vertexformat(sg_vertex_format f) {
16959+ switch (f) {
16960+ case SG_VERTEXFORMAT_FLOAT: return WGPUVertexFormat_Float32;
16961+ case SG_VERTEXFORMAT_FLOAT2: return WGPUVertexFormat_Float32x2;
16962+ case SG_VERTEXFORMAT_FLOAT3: return WGPUVertexFormat_Float32x3;
16963+ case SG_VERTEXFORMAT_FLOAT4: return WGPUVertexFormat_Float32x4;
16964+ case SG_VERTEXFORMAT_INT: return WGPUVertexFormat_Sint32;
16965+ case SG_VERTEXFORMAT_INT2: return WGPUVertexFormat_Sint32x2;
16966+ case SG_VERTEXFORMAT_INT3: return WGPUVertexFormat_Sint32x3;
16967+ case SG_VERTEXFORMAT_INT4: return WGPUVertexFormat_Sint32x4;
16968+ case SG_VERTEXFORMAT_UINT: return WGPUVertexFormat_Uint32;
16969+ case SG_VERTEXFORMAT_UINT2: return WGPUVertexFormat_Uint32x2;
16970+ case SG_VERTEXFORMAT_UINT3: return WGPUVertexFormat_Uint32x3;
16971+ case SG_VERTEXFORMAT_UINT4: return WGPUVertexFormat_Uint32x4;
16972+ case SG_VERTEXFORMAT_BYTE4: return WGPUVertexFormat_Sint8x4;
16973+ case SG_VERTEXFORMAT_BYTE4N: return WGPUVertexFormat_Snorm8x4;
16974+ case SG_VERTEXFORMAT_UBYTE4: return WGPUVertexFormat_Uint8x4;
16975+ case SG_VERTEXFORMAT_UBYTE4N: return WGPUVertexFormat_Unorm8x4;
16976+ case SG_VERTEXFORMAT_SHORT2: return WGPUVertexFormat_Sint16x2;
16977+ case SG_VERTEXFORMAT_SHORT2N: return WGPUVertexFormat_Snorm16x2;
16978+ case SG_VERTEXFORMAT_USHORT2: return WGPUVertexFormat_Uint16x2;
16979+ case SG_VERTEXFORMAT_USHORT2N: return WGPUVertexFormat_Unorm16x2;
16980+ case SG_VERTEXFORMAT_SHORT4: return WGPUVertexFormat_Sint16x4;
16981+ case SG_VERTEXFORMAT_SHORT4N: return WGPUVertexFormat_Snorm16x4;
16982+ case SG_VERTEXFORMAT_USHORT4: return WGPUVertexFormat_Uint16x4;
16983+ case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_Unorm16x4;
16984+ case SG_VERTEXFORMAT_UINT10_N2: return WGPUVertexFormat_Unorm10_10_10_2;
16985+ case SG_VERTEXFORMAT_HALF2: return WGPUVertexFormat_Float16x2;
16986+ case SG_VERTEXFORMAT_HALF4: return WGPUVertexFormat_Float16x4;
16987+ default:
16988+ SOKOL_UNREACHABLE;
16989+ return WGPUVertexFormat_Force32;
16990+ }
16991+}
16992+
16993+_SOKOL_PRIVATE WGPUPrimitiveTopology _sg_wgpu_topology(sg_primitive_type t) {
16994+ switch (t) {
16995+ case SG_PRIMITIVETYPE_POINTS: return WGPUPrimitiveTopology_PointList;
16996+ case SG_PRIMITIVETYPE_LINES: return WGPUPrimitiveTopology_LineList;
16997+ case SG_PRIMITIVETYPE_LINE_STRIP: return WGPUPrimitiveTopology_LineStrip;
16998+ case SG_PRIMITIVETYPE_TRIANGLES: return WGPUPrimitiveTopology_TriangleList;
16999+ case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return WGPUPrimitiveTopology_TriangleStrip;
17000+ default:
17001+ SOKOL_UNREACHABLE;
17002+ return WGPUPrimitiveTopology_Force32;
17003+ }
17004+}
17005+
17006+_SOKOL_PRIVATE WGPUFrontFace _sg_wgpu_frontface(sg_face_winding fw) {
17007+ return (fw == SG_FACEWINDING_CCW) ? WGPUFrontFace_CCW : WGPUFrontFace_CW;
17008+}
17009+
17010+_SOKOL_PRIVATE WGPUCullMode _sg_wgpu_cullmode(sg_cull_mode cm) {
17011+ switch (cm) {
17012+ case SG_CULLMODE_NONE: return WGPUCullMode_None;
17013+ case SG_CULLMODE_FRONT: return WGPUCullMode_Front;
17014+ case SG_CULLMODE_BACK: return WGPUCullMode_Back;
17015+ default:
17016+ SOKOL_UNREACHABLE;
17017+ return WGPUCullMode_Force32;
17018+ }
17019+}
17020+
17021+_SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) {
17022+ switch (p) {
17023+ case SG_PIXELFORMAT_NONE: return WGPUTextureFormat_Undefined;
17024+ case SG_PIXELFORMAT_R8: return WGPUTextureFormat_R8Unorm;
17025+ case SG_PIXELFORMAT_R8SN: return WGPUTextureFormat_R8Snorm;
17026+ case SG_PIXELFORMAT_R8UI: return WGPUTextureFormat_R8Uint;
17027+ case SG_PIXELFORMAT_R8SI: return WGPUTextureFormat_R8Sint;
17028+ case SG_PIXELFORMAT_R16: return WGPUTextureFormat_R16Unorm;
17029+ case SG_PIXELFORMAT_R16SN: return WGPUTextureFormat_R16Snorm;
17030+ case SG_PIXELFORMAT_R16UI: return WGPUTextureFormat_R16Uint;
17031+ case SG_PIXELFORMAT_R16SI: return WGPUTextureFormat_R16Sint;
17032+ case SG_PIXELFORMAT_R16F: return WGPUTextureFormat_R16Float;
17033+ case SG_PIXELFORMAT_RG8: return WGPUTextureFormat_RG8Unorm;
17034+ case SG_PIXELFORMAT_RG8SN: return WGPUTextureFormat_RG8Snorm;
17035+ case SG_PIXELFORMAT_RG8UI: return WGPUTextureFormat_RG8Uint;
17036+ case SG_PIXELFORMAT_RG8SI: return WGPUTextureFormat_RG8Sint;
17037+ case SG_PIXELFORMAT_R32UI: return WGPUTextureFormat_R32Uint;
17038+ case SG_PIXELFORMAT_R32SI: return WGPUTextureFormat_R32Sint;
17039+ case SG_PIXELFORMAT_R32F: return WGPUTextureFormat_R32Float;
17040+ case SG_PIXELFORMAT_RG16: return WGPUTextureFormat_RG16Unorm;
17041+ case SG_PIXELFORMAT_RG16SN: return WGPUTextureFormat_RG16Snorm;
17042+ case SG_PIXELFORMAT_RG16UI: return WGPUTextureFormat_RG16Uint;
17043+ case SG_PIXELFORMAT_RG16SI: return WGPUTextureFormat_RG16Sint;
17044+ case SG_PIXELFORMAT_RG16F: return WGPUTextureFormat_RG16Float;
17045+ case SG_PIXELFORMAT_RGBA8: return WGPUTextureFormat_RGBA8Unorm;
17046+ case SG_PIXELFORMAT_SRGB8A8: return WGPUTextureFormat_RGBA8UnormSrgb;
17047+ case SG_PIXELFORMAT_RGBA8SN: return WGPUTextureFormat_RGBA8Snorm;
17048+ case SG_PIXELFORMAT_RGBA8UI: return WGPUTextureFormat_RGBA8Uint;
17049+ case SG_PIXELFORMAT_RGBA8SI: return WGPUTextureFormat_RGBA8Sint;
17050+ case SG_PIXELFORMAT_BGRA8: return WGPUTextureFormat_BGRA8Unorm;
17051+ case SG_PIXELFORMAT_SBGR8A8: return WGPUTextureFormat_BGRA8UnormSrgb;
17052+ case SG_PIXELFORMAT_RGB10A2: return WGPUTextureFormat_RGB10A2Unorm;
17053+ case SG_PIXELFORMAT_RG11B10F: return WGPUTextureFormat_RG11B10Ufloat;
17054+ case SG_PIXELFORMAT_RGB9E5: return WGPUTextureFormat_RGB9E5Ufloat;
17055+ case SG_PIXELFORMAT_RG32UI: return WGPUTextureFormat_RG32Uint;
17056+ case SG_PIXELFORMAT_RG32SI: return WGPUTextureFormat_RG32Sint;
17057+ case SG_PIXELFORMAT_RG32F: return WGPUTextureFormat_RG32Float;
17058+ case SG_PIXELFORMAT_RGBA16: return WGPUTextureFormat_RGBA16Unorm;
17059+ case SG_PIXELFORMAT_RGBA16SN: return WGPUTextureFormat_RGBA16Snorm;
17060+ case SG_PIXELFORMAT_RGBA16UI: return WGPUTextureFormat_RGBA16Uint;
17061+ case SG_PIXELFORMAT_RGBA16SI: return WGPUTextureFormat_RGBA16Sint;
17062+ case SG_PIXELFORMAT_RGBA16F: return WGPUTextureFormat_RGBA16Float;
17063+ case SG_PIXELFORMAT_RGBA32UI: return WGPUTextureFormat_RGBA32Uint;
17064+ case SG_PIXELFORMAT_RGBA32SI: return WGPUTextureFormat_RGBA32Sint;
17065+ case SG_PIXELFORMAT_RGBA32F: return WGPUTextureFormat_RGBA32Float;
17066+ case SG_PIXELFORMAT_DEPTH: return WGPUTextureFormat_Depth32Float;
17067+ case SG_PIXELFORMAT_DEPTH_STENCIL: return WGPUTextureFormat_Depth32FloatStencil8;
17068+ case SG_PIXELFORMAT_BC1_RGBA: return WGPUTextureFormat_BC1RGBAUnorm;
17069+ case SG_PIXELFORMAT_BC2_RGBA: return WGPUTextureFormat_BC2RGBAUnorm;
17070+ case SG_PIXELFORMAT_BC3_RGBA: return WGPUTextureFormat_BC3RGBAUnorm;
17071+ case SG_PIXELFORMAT_BC3_SRGBA: return WGPUTextureFormat_BC3RGBAUnormSrgb;
17072+ case SG_PIXELFORMAT_BC4_R: return WGPUTextureFormat_BC4RUnorm;
17073+ case SG_PIXELFORMAT_BC4_RSN: return WGPUTextureFormat_BC4RSnorm;
17074+ case SG_PIXELFORMAT_BC5_RG: return WGPUTextureFormat_BC5RGUnorm;
17075+ case SG_PIXELFORMAT_BC5_RGSN: return WGPUTextureFormat_BC5RGSnorm;
17076+ case SG_PIXELFORMAT_BC6H_RGBF: return WGPUTextureFormat_BC6HRGBFloat;
17077+ case SG_PIXELFORMAT_BC6H_RGBUF: return WGPUTextureFormat_BC6HRGBUfloat;
17078+ case SG_PIXELFORMAT_BC7_RGBA: return WGPUTextureFormat_BC7RGBAUnorm;
17079+ case SG_PIXELFORMAT_BC7_SRGBA: return WGPUTextureFormat_BC7RGBAUnormSrgb;
17080+ case SG_PIXELFORMAT_ETC2_RGB8: return WGPUTextureFormat_ETC2RGB8Unorm;
17081+ case SG_PIXELFORMAT_ETC2_RGB8A1: return WGPUTextureFormat_ETC2RGB8A1Unorm;
17082+ case SG_PIXELFORMAT_ETC2_RGBA8: return WGPUTextureFormat_ETC2RGBA8Unorm;
17083+ case SG_PIXELFORMAT_ETC2_SRGB8: return WGPUTextureFormat_ETC2RGB8UnormSrgb;
17084+ case SG_PIXELFORMAT_ETC2_SRGB8A8: return WGPUTextureFormat_ETC2RGBA8UnormSrgb;
17085+ case SG_PIXELFORMAT_EAC_R11: return WGPUTextureFormat_EACR11Unorm;
17086+ case SG_PIXELFORMAT_EAC_R11SN: return WGPUTextureFormat_EACR11Snorm;
17087+ case SG_PIXELFORMAT_EAC_RG11: return WGPUTextureFormat_EACRG11Unorm;
17088+ case SG_PIXELFORMAT_EAC_RG11SN: return WGPUTextureFormat_EACRG11Snorm;
17089+ case SG_PIXELFORMAT_ASTC_4x4_RGBA: return WGPUTextureFormat_ASTC4x4Unorm;
17090+ case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return WGPUTextureFormat_ASTC4x4UnormSrgb;
17091+ default:
17092+ SOKOL_UNREACHABLE;
17093+ return WGPUTextureFormat_Force32;
17094+ }
17095+}
17096+
17097+_SOKOL_PRIVATE WGPUCompareFunction _sg_wgpu_comparefunc(sg_compare_func f) {
17098+ switch (f) {
17099+ case SG_COMPAREFUNC_NEVER: return WGPUCompareFunction_Never;
17100+ case SG_COMPAREFUNC_LESS: return WGPUCompareFunction_Less;
17101+ case SG_COMPAREFUNC_EQUAL: return WGPUCompareFunction_Equal;
17102+ case SG_COMPAREFUNC_LESS_EQUAL: return WGPUCompareFunction_LessEqual;
17103+ case SG_COMPAREFUNC_GREATER: return WGPUCompareFunction_Greater;
17104+ case SG_COMPAREFUNC_NOT_EQUAL: return WGPUCompareFunction_NotEqual;
17105+ case SG_COMPAREFUNC_GREATER_EQUAL: return WGPUCompareFunction_GreaterEqual;
17106+ case SG_COMPAREFUNC_ALWAYS: return WGPUCompareFunction_Always;
17107+ default:
17108+ SOKOL_UNREACHABLE;
17109+ return WGPUCompareFunction_Force32;
17110+ }
17111+}
17112+
17113+_SOKOL_PRIVATE WGPUStencilOperation _sg_wgpu_stencilop(sg_stencil_op op) {
17114+ switch (op) {
17115+ case SG_STENCILOP_KEEP: return WGPUStencilOperation_Keep;
17116+ case SG_STENCILOP_ZERO: return WGPUStencilOperation_Zero;
17117+ case SG_STENCILOP_REPLACE: return WGPUStencilOperation_Replace;
17118+ case SG_STENCILOP_INCR_CLAMP: return WGPUStencilOperation_IncrementClamp;
17119+ case SG_STENCILOP_DECR_CLAMP: return WGPUStencilOperation_DecrementClamp;
17120+ case SG_STENCILOP_INVERT: return WGPUStencilOperation_Invert;
17121+ case SG_STENCILOP_INCR_WRAP: return WGPUStencilOperation_IncrementWrap;
17122+ case SG_STENCILOP_DECR_WRAP: return WGPUStencilOperation_DecrementWrap;
17123+ default:
17124+ SOKOL_UNREACHABLE;
17125+ return WGPUStencilOperation_Force32;
17126+ }
17127+}
17128+
17129+_SOKOL_PRIVATE WGPUBlendOperation _sg_wgpu_blendop(sg_blend_op op) {
17130+ switch (op) {
17131+ case SG_BLENDOP_ADD: return WGPUBlendOperation_Add;
17132+ case SG_BLENDOP_SUBTRACT: return WGPUBlendOperation_Subtract;
17133+ case SG_BLENDOP_REVERSE_SUBTRACT: return WGPUBlendOperation_ReverseSubtract;
17134+ case SG_BLENDOP_MIN: return WGPUBlendOperation_Min;
17135+ case SG_BLENDOP_MAX: return WGPUBlendOperation_Max;
17136+ default:
17137+ SOKOL_UNREACHABLE;
17138+ return WGPUBlendOperation_Force32;
17139+ }
17140+}
17141+
17142+_SOKOL_PRIVATE WGPUBlendFactor _sg_wgpu_blendfactor(sg_blend_factor f) {
17143+ switch (f) {
17144+ case SG_BLENDFACTOR_ZERO: return WGPUBlendFactor_Zero;
17145+ case SG_BLENDFACTOR_ONE: return WGPUBlendFactor_One;
17146+ case SG_BLENDFACTOR_SRC_COLOR: return WGPUBlendFactor_Src;
17147+ case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return WGPUBlendFactor_OneMinusSrc;
17148+ case SG_BLENDFACTOR_SRC_ALPHA: return WGPUBlendFactor_SrcAlpha;
17149+ case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return WGPUBlendFactor_OneMinusSrcAlpha;
17150+ case SG_BLENDFACTOR_DST_COLOR: return WGPUBlendFactor_Dst;
17151+ case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return WGPUBlendFactor_OneMinusDst;
17152+ case SG_BLENDFACTOR_DST_ALPHA: return WGPUBlendFactor_DstAlpha;
17153+ case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return WGPUBlendFactor_OneMinusDstAlpha;
17154+ case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return WGPUBlendFactor_SrcAlphaSaturated;
17155+ case SG_BLENDFACTOR_BLEND_COLOR: return WGPUBlendFactor_Constant;
17156+ case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return WGPUBlendFactor_OneMinusConstant;
17157+ // FIXME: separate blend alpha value not supported?
17158+ case SG_BLENDFACTOR_BLEND_ALPHA: return WGPUBlendFactor_Constant;
17159+ case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return WGPUBlendFactor_OneMinusConstant;
17160+ case SG_BLENDFACTOR_SRC1_COLOR: return WGPUBlendFactor_Src1 ;
17161+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: return WGPUBlendFactor_OneMinusSrc1;
17162+ case SG_BLENDFACTOR_SRC1_ALPHA: return WGPUBlendFactor_Src1Alpha;
17163+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: return WGPUBlendFactor_OneMinusSrc1Alpha;
17164+ default:
17165+ SOKOL_UNREACHABLE;
17166+ return WGPUBlendFactor_Force32;
17167+ }
17168+}
17169+
17170+_SOKOL_PRIVATE WGPUColorWriteMask _sg_wgpu_colorwritemask(sg_color_mask m) {
17171+ int res = 0;
17172+ if (0 != (m & SG_COLORMASK_R)) {
17173+ res |= (int)WGPUColorWriteMask_Red;
17174+ }
17175+ if (0 != (m & SG_COLORMASK_G)) {
17176+ res |= (int)WGPUColorWriteMask_Green;
17177+ }
17178+ if (0 != (m & SG_COLORMASK_B)) {
17179+ res |= (int)WGPUColorWriteMask_Blue;
17180+ }
17181+ if (0 != (m & SG_COLORMASK_A)) {
17182+ res |= (int)WGPUColorWriteMask_Alpha;
17183+ }
17184+ return (WGPUColorWriteMask)res;
17185+}
17186+
17187+_SOKOL_PRIVATE WGPUShaderStage _sg_wgpu_shader_stage(sg_shader_stage stage) {
17188+ switch (stage) {
17189+ case SG_SHADERSTAGE_VERTEX: return WGPUShaderStage_Vertex;
17190+ case SG_SHADERSTAGE_FRAGMENT: return WGPUShaderStage_Fragment;
17191+ case SG_SHADERSTAGE_COMPUTE: return WGPUShaderStage_Compute;
17192+ default: SOKOL_UNREACHABLE; return WGPUShaderStage_None;
17193+ }
17194+}
17195+
17196+_SOKOL_PRIVATE void _sg_wgpu_init_caps(void) {
17197+ _sg.backend = SG_BACKEND_WGPU;
17198+ _sg.features.origin_top_left = true;
17199+ _sg.features.image_clamp_to_border = false;
17200+ _sg.features.mrt_independent_blend_state = true;
17201+ _sg.features.mrt_independent_write_mask = true;
17202+ _sg.features.compute = true;
17203+ _sg.features.msaa_texture_bindings = true;
17204+ _sg.features.draw_base_vertex = true;
17205+ _sg.features.draw_base_instance = true;
17206+ _sg.features.dual_source_blending = wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_DualSourceBlending);
17207+ _sg.features.vertexformat_int10_n2 = false;
17208+
17209+ wgpuDeviceGetLimits(_sg.wgpu.dev, &_sg.wgpu.limits);
17210+
17211+ const WGPULimits* l = &_sg.wgpu.limits;
17212+ _sg.limits.max_image_size_2d = (int) l->maxTextureDimension2D;
17213+ _sg.limits.max_image_size_cube = (int) l->maxTextureDimension2D; // not a bug, see: https://github.com/gpuweb/gpuweb/issues/1327
17214+ _sg.limits.max_image_size_3d = (int) l->maxTextureDimension3D;
17215+ _sg.limits.max_image_size_array = (int) l->maxTextureDimension2D;
17216+ _sg.limits.max_image_array_layers = (int) l->maxTextureArrayLayers;
17217+ _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES;
17218+ _sg.limits.max_color_attachments = _sg_min((int)l->maxColorAttachments, SG_MAX_COLOR_ATTACHMENTS);
17219+ _sg.limits.max_texture_bindings_per_stage = _sg_min((int)l->maxSampledTexturesPerShaderStage, SG_MAX_VIEW_BINDSLOTS);
17220+ _sg.limits.max_storage_buffer_bindings_per_stage = _sg_min((int)l->maxStorageBuffersPerShaderStage, SG_MAX_VIEW_BINDSLOTS);
17221+ _sg.limits.max_storage_image_bindings_per_stage = _sg_min((int)l->maxStorageTexturesPerShaderStage, SG_MAX_VIEW_BINDSLOTS);
17222+
17223+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]);
17224+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]);
17225+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]);
17226+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]);
17227+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]);
17228+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]);
17229+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]);
17230+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]);
17231+ _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]);
17232+
17233+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]);
17234+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]);
17235+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]);
17236+
17237+ // FIXME: can be made renderable via extension
17238+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]);
17239+
17240+ // NOTE: msaa rendering is possible in WebGPU, but no resolve
17241+ // which is a combination that's not currently supported in sokol-gfx
17242+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R8UI]);
17243+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R8SI]);
17244+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG8UI]);
17245+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG8SI]);
17246+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]);
17247+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]);
17248+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R16UI]);
17249+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R16SI]);
17250+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG16UI]);
17251+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG16SI]);
17252+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]);
17253+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]);
17254+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]);
17255+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]);
17256+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]);
17257+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]);
17258+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]);
17259+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]);
17260+
17261+ if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_Float32Filterable)) {
17262+ _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_R32F]);
17263+ _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RG32F]);
17264+ _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
17265+ } else {
17266+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32F]);
17267+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32F]);
17268+ _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
17269+ }
17270+ if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_Float32Blendable)) {
17271+ _sg.formats[SG_PIXELFORMAT_R32F].blend = true;
17272+ _sg.formats[SG_PIXELFORMAT_RG32F].blend = true;
17273+ _sg.formats[SG_PIXELFORMAT_RGBA32F].blend = true;
17274+ }
17275+ if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureFormatsTier1)) {
17276+ _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R16]);
17277+ _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R16SN]);
17278+ _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG16]);
17279+ _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG16SN]);
17280+ _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RGBA16]);
17281+ _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]);
17282+ _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R8SN]);
17283+ _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG8SN]);
17284+ _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]);
17285+ }
17286+
17287+ _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]);
17288+ _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]);
17289+
17290+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]);
17291+
17292+ if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionBC)) {
17293+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]);
17294+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]);
17295+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]);
17296+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]);
17297+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]);
17298+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]);
17299+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]);
17300+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]);
17301+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]);
17302+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]);
17303+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]);
17304+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]);
17305+ }
17306+ if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionETC2)) {
17307+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]);
17308+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]);
17309+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]);
17310+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]);
17311+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]);
17312+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]);
17313+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]);
17314+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]);
17315+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]);
17316+ }
17317+
17318+ if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionASTC)) {
17319+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]);
17320+ _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]);
17321+ }
17322+
17323+ // see: https://github.com/gpuweb/gpuweb/issues/513
17324+ // NOTE: can't express read-only/write-only vs read-write in sokol-gfx
17325+ // e.g. some of the below formats are only read-write with texture-tier-2
17326+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]);
17327+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]);
17328+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]);
17329+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]);
17330+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]);
17331+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]);
17332+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]);
17333+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32UI]);
17334+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32SI]);
17335+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R32F]);
17336+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32UI]);
17337+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32SI]);
17338+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RG32F]);
17339+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]);
17340+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]);
17341+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]);
17342+ if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureFormatsTier2)) {
17343+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R8]);
17344+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R8UI]);
17345+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R8SI]);
17346+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16UI]);
17347+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16SI]);
17348+ _sg_pixelformat_compute_all(&_sg.formats[SG_PIXELFORMAT_R16F]);
17349+ }
17350+}
17351+
17352+_SOKOL_PRIVATE void _sg_wgpu_uniform_system_init(const sg_desc* desc) {
17353+ SOKOL_ASSERT(0 == _sg.wgpu.uniform.staging);
17354+ SOKOL_ASSERT(0 == _sg.wgpu.uniform.buf);
17355+
17356+ // Add the max-uniform-update size (64 KB) to the requested buffer size,
17357+ // this is to prevent validation errors in the WebGPU implementation
17358+ // if the entire buffer size is used per frame. 64 KB is the allowed
17359+ // max uniform update size on NVIDIA
17360+ //
17361+ // FIXME: is this still needed?
17362+ _sg.wgpu.uniform.num_bytes = (uint32_t)(desc->uniform_buffer_size + _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE);
17363+ _sg.wgpu.uniform.staging = (uint8_t*)_sg_malloc(_sg.wgpu.uniform.num_bytes);
17364+
17365+ _SG_STRUCT(WGPUBufferDescriptor, ub_desc);
17366+ ub_desc.size = _sg.wgpu.uniform.num_bytes;
17367+ ub_desc.usage = WGPUBufferUsage_Uniform|WGPUBufferUsage_CopyDst;
17368+ _sg.wgpu.uniform.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &ub_desc);
17369+ SOKOL_ASSERT(_sg.wgpu.uniform.buf);
17370+}
17371+
17372+_SOKOL_PRIVATE void _sg_wgpu_uniform_system_discard(void) {
17373+ if (_sg.wgpu.uniform.buf) {
17374+ wgpuBufferRelease(_sg.wgpu.uniform.buf);
17375+ _sg.wgpu.uniform.buf = 0;
17376+ }
17377+ if (_sg.wgpu.uniform.staging) {
17378+ _sg_free(_sg.wgpu.uniform.staging);
17379+ _sg.wgpu.uniform.staging = 0;
17380+ }
17381+}
17382+
17383+_SOKOL_PRIVATE void _sg_wgpu_uniform_system_set_bindgroup(void) {
17384+ SOKOL_ASSERT(_sg.wgpu.uniform.dirty);
17385+ _sg.wgpu.uniform.dirty = false;
17386+ const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip);
17387+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
17388+ // NOTE: dynamic offsets must be in binding order, not in BindGroupEntry order
17389+ SOKOL_ASSERT(shd->wgpu.ub_num_dynoffsets < SG_MAX_UNIFORMBLOCK_BINDSLOTS);
17390+ _SG_STRUCT(uint32_t, dyn_offsets[SG_MAX_UNIFORMBLOCK_BINDSLOTS]);
17391+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
17392+ if (shd->cmn.uniform_blocks[i].stage == SG_SHADERSTAGE_NONE) {
17393+ continue;
17394+ }
17395+ uint8_t dynoffset_index = shd->wgpu.ub_dynoffsets[i];
17396+ SOKOL_ASSERT(dynoffset_index < shd->wgpu.ub_num_dynoffsets);
17397+ dyn_offsets[dynoffset_index] = _sg.wgpu.uniform.bind_offsets[i];
17398+ }
17399+ if (_sg.cur_pass.is_compute) {
17400+ SOKOL_ASSERT(_sg.wgpu.cpass_enc);
17401+ wgpuComputePassEncoderSetBindGroup(_sg.wgpu.cpass_enc,
17402+ _SG_WGPU_UB_BINDGROUP_INDEX,
17403+ shd->wgpu.bg_ub,
17404+ shd->wgpu.ub_num_dynoffsets,
17405+ dyn_offsets);
17406+ } else {
17407+ SOKOL_ASSERT(_sg.wgpu.rpass_enc);
17408+ wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.rpass_enc,
17409+ _SG_WGPU_UB_BINDGROUP_INDEX,
17410+ shd->wgpu.bg_ub,
17411+ shd->wgpu.ub_num_dynoffsets,
17412+ dyn_offsets);
17413+ }
17414+}
17415+
17416+_SOKOL_PRIVATE void _sg_wgpu_uniform_system_on_apply_pipeline(void) {
17417+ _sg.wgpu.uniform.dirty = false;
17418+}
17419+
17420+_SOKOL_PRIVATE void _sg_wgpu_uniform_system_on_commit(void) {
17421+ wgpuQueueWriteBuffer(_sg.wgpu.queue, _sg.wgpu.uniform.buf, 0, _sg.wgpu.uniform.staging, _sg.wgpu.uniform.offset);
17422+ _sg_stats_add(wgpu.uniforms.size_write_buffer, _sg.wgpu.uniform.offset);
17423+ _sg.wgpu.uniform.offset = 0;
17424+ _sg_clear(_sg.wgpu.uniform.bind_offsets, sizeof(_sg.wgpu.uniform.bind_offsets));
17425+}
17426+
17427+_SOKOL_PRIVATE void _sg_wgpu_bindgroups_pool_init(const sg_desc* desc) {
17428+ SOKOL_ASSERT((desc->wgpu.bindgroups_cache_size > 0) && (desc->wgpu.bindgroups_cache_size < _SG_MAX_POOL_SIZE));
17429+ _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool;
17430+ SOKOL_ASSERT(0 == p->bindgroups);
17431+ const int pool_size = desc->wgpu.bindgroups_cache_size;
17432+ _sg_pool_init(&p->pool, pool_size);
17433+ size_t pool_byte_size = sizeof(_sg_wgpu_bindgroup_t) * (size_t)p->pool.size;
17434+ p->bindgroups = (_sg_wgpu_bindgroup_t*) _sg_malloc_clear(pool_byte_size);
17435+}
17436+
17437+_SOKOL_PRIVATE void _sg_wgpu_bindgroups_pool_discard(void) {
17438+ _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool;
17439+ SOKOL_ASSERT(p->bindgroups);
17440+ _sg_free(p->bindgroups); p->bindgroups = 0;
17441+ _sg_pool_discard(&p->pool);
17442+}
17443+
17444+_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_bindgroup_at(uint32_t bg_id) {
17445+ SOKOL_ASSERT(SG_INVALID_ID != bg_id);
17446+ _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool;
17447+ int slot_index = _sg_slot_index(bg_id);
17448+ SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pool.size));
17449+ return &p->bindgroups[slot_index];
17450+}
17451+
17452+_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_lookup_bindgroup(uint32_t bg_id) {
17453+ if (SG_INVALID_ID != bg_id) {
17454+ _sg_wgpu_bindgroup_t* bg = _sg_wgpu_bindgroup_at(bg_id);
17455+ if (bg->slot.id == bg_id) {
17456+ return bg;
17457+ }
17458+ }
17459+ return 0;
17460+}
17461+
17462+_SOKOL_PRIVATE _sg_wgpu_bindgroup_handle_t _sg_wgpu_alloc_bindgroup(void) {
17463+ _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool;
17464+ _sg_wgpu_bindgroup_handle_t res;
17465+ int slot_index = _sg_pool_alloc_index(&p->pool);
17466+ if (_SG_INVALID_SLOT_INDEX != slot_index) {
17467+ res.id = _sg_slot_alloc(&p->pool, &p->bindgroups[slot_index].slot, slot_index);
17468+ } else {
17469+ res.id = SG_INVALID_ID;
17470+ _SG_ERROR(WGPU_BINDGROUPS_POOL_EXHAUSTED);
17471+ }
17472+ return res;
17473+}
17474+
17475+_SOKOL_PRIVATE void _sg_wgpu_dealloc_bindgroup(_sg_wgpu_bindgroup_t* bg) {
17476+ SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_ALLOC) && (bg->slot.id != SG_INVALID_ID));
17477+ _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool;
17478+ _sg_pool_free_index(&p->pool, _sg_slot_index(bg->slot.id));
17479+ _sg_slot_reset(&bg->slot);
17480+}
17481+
17482+_SOKOL_PRIVATE void _sg_wgpu_reset_bindgroup_to_alloc_state(_sg_wgpu_bindgroup_t* bg) {
17483+ SOKOL_ASSERT(bg);
17484+ _sg_slot_t slot = bg->slot;
17485+ _sg_clear(bg, sizeof(_sg_wgpu_bindgroup_t));
17486+ bg->slot = slot;
17487+ bg->slot.state = SG_RESOURCESTATE_ALLOC;
17488+}
17489+
17490+// MurmurHash64B (see: https://github.com/aappleby/smhasher/blob/61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash2.cpp#L142)
17491+_SOKOL_PRIVATE uint64_t _sg_wgpu_hash(const void* key, int len, uint64_t seed) {
17492+ const uint32_t m = 0x5bd1e995;
17493+ const int r = 24;
17494+ uint32_t h1 = (uint32_t)seed ^ (uint32_t)len;
17495+ uint32_t h2 = (uint32_t)(seed >> 32);
17496+ const uint32_t * data = (const uint32_t *)key;
17497+ while (len >= 8) {
17498+ uint32_t k1 = *data++;
17499+ k1 *= m; k1 ^= k1 >> r; k1 *= m;
17500+ h1 *= m; h1 ^= k1;
17501+ len -= 4;
17502+ uint32_t k2 = *data++;
17503+ k2 *= m; k2 ^= k2 >> r; k2 *= m;
17504+ h2 *= m; h2 ^= k2;
17505+ len -= 4;
17506+ }
17507+ if (len >= 4) {
17508+ uint32_t k1 = *data++;
17509+ k1 *= m; k1 ^= k1 >> r; k1 *= m;
17510+ h1 *= m; h1 ^= k1;
17511+ len -= 4;
17512+ }
17513+ switch(len) {
17514+ case 3: h2 ^= (uint32_t)(((unsigned char*)data)[2] << 16);
17515+ // fall through
17516+ case 2: h2 ^= (uint32_t)(((unsigned char*)data)[1] << 8);
17517+ // fall through
17518+ case 1: h2 ^= ((unsigned char*)data)[0];
17519+ // fall through
17520+ h2 *= m;
17521+ };
17522+ h1 ^= h2 >> 18; h1 *= m;
17523+ h2 ^= h1 >> 22; h2 *= m;
17524+ h1 ^= h2 >> 17; h1 *= m;
17525+ h2 ^= h1 >> 19; h2 *= m;
17526+ uint64_t h = h1;
17527+ h = (h << 32) | h2;
17528+ return h;
17529+}
17530+
17531+_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_item(_sg_wgpu_bindgroups_cache_item_type_t type, uint8_t wgpu_binding, uint32_t id, uint32_t uninit_count) {
17532+ const uint64_t bb = wgpu_binding;
17533+ const uint64_t t = type & 3;
17534+ const uint64_t ccccc = uninit_count & ((1 << 22) - 1);
17535+ const uint64_t iiiiiiii = id;
17536+ return (bb << 56) | (t << 54) | (ccccc << 32) | iiiiiiii;
17537+}
17538+
17539+_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_pip_item(const _sg_slot_t* slot) {
17540+ return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE, 0xFF, slot->id, slot->uninit_count);
17541+}
17542+
17543+_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_view_item(uint8_t wgpu_binding, const _sg_slot_t* slot) {
17544+ return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_VIEW, wgpu_binding, slot->id, slot->uninit_count);
17545+}
17546+
17547+_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_sampler_item(uint8_t wgpu_binding, const _sg_slot_t* slot) {
17548+ return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER, wgpu_binding, slot->id, slot->uninit_count);
17549+}
17550+
17551+_SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* key, const _sg_bindings_ptrs_t* bnd) {
17552+ SOKOL_ASSERT(bnd);
17553+ SOKOL_ASSERT(bnd->pip);
17554+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader);
17555+
17556+ _sg_clear(key->items, sizeof(key->items));
17557+ key->items[0] = _sg_wgpu_bindgroups_cache_pip_item(&bnd->pip->slot);
17558+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
17559+ if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) {
17560+ continue;
17561+ }
17562+ SOKOL_ASSERT(bnd->views[i]);
17563+ const size_t item_idx = i + 1;
17564+ SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS);
17565+ SOKOL_ASSERT(0 == key->items[item_idx]);
17566+ const uint8_t wgpu_binding = shd->wgpu.view_grp1_bnd_n[i];
17567+ key->items[item_idx] = _sg_wgpu_bindgroups_cache_view_item(wgpu_binding, &bnd->views[i]->slot);
17568+ }
17569+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
17570+ if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) {
17571+ continue;
17572+ }
17573+ SOKOL_ASSERT(bnd->smps[i]);
17574+ const size_t item_idx = i + 1 + SG_MAX_VIEW_BINDSLOTS;
17575+ SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS);
17576+ SOKOL_ASSERT(0 == key->items[item_idx]);
17577+ const uint8_t wgpu_binding = shd->wgpu.smp_grp1_bnd_n[i];
17578+ key->items[item_idx] = _sg_wgpu_bindgroups_cache_sampler_item(wgpu_binding, &bnd->smps[i]->slot);
17579+ }
17580+ key->hash = _sg_wgpu_hash(&key->items, (int)sizeof(key->items), 0x1234567887654321);
17581+}
17582+
17583+_SOKOL_PRIVATE bool _sg_wgpu_compare_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* k0, _sg_wgpu_bindgroups_cache_key_t* k1) {
17584+ SOKOL_ASSERT(k0 && k1);
17585+ if (k0->hash != k1->hash) {
17586+ return false;
17587+ }
17588+ if (memcmp(&k0->items, &k1->items, sizeof(k0->items)) != 0) {
17589+ _sg_stats_inc(wgpu.bindings.num_bindgroup_cache_hash_vs_key_mismatch);
17590+ return false;
17591+ }
17592+ return true;
17593+}
17594+
17595+_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_create_bindgroup(_sg_bindings_ptrs_t* bnd) {
17596+ SOKOL_ASSERT(_sg.wgpu.dev);
17597+ SOKOL_ASSERT(bnd->pip);
17598+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader);
17599+ _sg_stats_inc(wgpu.bindings.num_create_bindgroup);
17600+ _sg_wgpu_bindgroup_handle_t bg_id = _sg_wgpu_alloc_bindgroup();
17601+ if (bg_id.id == SG_INVALID_ID) {
17602+ return 0;
17603+ }
17604+ _sg_wgpu_bindgroup_t* bg = _sg_wgpu_bindgroup_at(bg_id.id);
17605+ SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_ALLOC));
17606+
17607+ // create wgpu bindgroup object (also see _sg_wgpu_create_shader())
17608+ WGPUBindGroupLayout bgl = shd->wgpu.bgl_view_smp;
17609+ SOKOL_ASSERT(bgl);
17610+ _SG_STRUCT(WGPUBindGroupEntry, bg_entries[_SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES]);
17611+ size_t bgl_index = 0;
17612+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
17613+ if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) {
17614+ continue;
17615+ }
17616+ const _sg_view_t* view = bnd->views[i];
17617+ SOKOL_ASSERT(view);
17618+ SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES);
17619+ WGPUBindGroupEntry* bg_entry = &bg_entries[bgl_index];
17620+ bg_entry->binding = shd->wgpu.view_grp1_bnd_n[i];
17621+ if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) {
17622+ const _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref);
17623+ SOKOL_ASSERT(buf->wgpu.buf);
17624+ SOKOL_ASSERT(view->cmn.buf.offset < buf->cmn.size);
17625+ bg_entry->buffer = buf->wgpu.buf;
17626+ bg_entry->offset = (uint64_t)view->cmn.buf.offset;
17627+ bg_entry->size = (uint64_t)(buf->cmn.size - view->cmn.buf.offset);
17628+ } else {
17629+ SOKOL_ASSERT(view->wgpu.view);
17630+ bg_entry->textureView = view->wgpu.view;
17631+ }
17632+ bgl_index += 1;
17633+ }
17634+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
17635+ if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) {
17636+ continue;
17637+ }
17638+ SOKOL_ASSERT(bnd->smps[i]);
17639+ SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES);
17640+ WGPUBindGroupEntry* bg_entry = &bg_entries[bgl_index];
17641+ bg_entry->binding = shd->wgpu.smp_grp1_bnd_n[i];
17642+ bg_entry->sampler = bnd->smps[i]->wgpu.smp;
17643+ bgl_index += 1;
17644+ }
17645+ _SG_STRUCT(WGPUBindGroupDescriptor, bg_desc);
17646+ bg_desc.layout = bgl;
17647+ bg_desc.entryCount = bgl_index;
17648+ bg_desc.entries = bg_entries;
17649+ bg->bindgroup = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc);
17650+ if (bg->bindgroup == 0) {
17651+ _SG_ERROR(WGPU_CREATEBINDGROUP_FAILED);
17652+ bg->slot.state = SG_RESOURCESTATE_FAILED;
17653+ return bg;
17654+ }
17655+ _sg_wgpu_init_bindgroups_cache_key(&bg->key, bnd);
17656+ bg->slot.state = SG_RESOURCESTATE_VALID;
17657+ return bg;
17658+}
17659+
17660+_SOKOL_PRIVATE void _sg_wgpu_discard_bindgroup(_sg_wgpu_bindgroup_t* bg) {
17661+ SOKOL_ASSERT(bg);
17662+ _sg_stats_inc(wgpu.bindings.num_discard_bindgroup);
17663+ if (bg->slot.state == SG_RESOURCESTATE_VALID) {
17664+ if (bg->bindgroup) {
17665+ wgpuBindGroupRelease(bg->bindgroup);
17666+ bg->bindgroup = 0;
17667+ }
17668+ _sg_wgpu_reset_bindgroup_to_alloc_state(bg);
17669+ SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_ALLOC);
17670+ }
17671+ if (bg->slot.state == SG_RESOURCESTATE_ALLOC) {
17672+ _sg_wgpu_dealloc_bindgroup(bg);
17673+ SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_INITIAL);
17674+ }
17675+}
17676+
17677+_SOKOL_PRIVATE void _sg_wgpu_discard_all_bindgroups(void) {
17678+ _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool;
17679+ for (int i = 0; i < p->pool.size; i++) {
17680+ sg_resource_state state = p->bindgroups[i].slot.state;
17681+ if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) {
17682+ _sg_wgpu_discard_bindgroup(&p->bindgroups[i]);
17683+ }
17684+ }
17685+}
17686+
17687+_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_init(const sg_desc* desc) {
17688+ SOKOL_ASSERT(desc);
17689+ SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.num == 0);
17690+ SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.index_mask == 0);
17691+ SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items == 0);
17692+ const int num = desc->wgpu.bindgroups_cache_size;
17693+ if (num <= 1) {
17694+ _SG_PANIC(WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE);
17695+ }
17696+ if (!_sg_ispow2(num)) {
17697+ _SG_PANIC(WGPU_BINDGROUPSCACHE_SIZE_POW2);
17698+ }
17699+ _sg.wgpu.bindgroups_cache.num = (uint32_t)desc->wgpu.bindgroups_cache_size;
17700+ _sg.wgpu.bindgroups_cache.index_mask = _sg.wgpu.bindgroups_cache.num - 1;
17701+ size_t size_in_bytes = sizeof(_sg_wgpu_bindgroup_handle_t) * (size_t)num;
17702+ _sg.wgpu.bindgroups_cache.items = (_sg_wgpu_bindgroup_handle_t*)_sg_malloc_clear(size_in_bytes);
17703+}
17704+
17705+_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_discard(void) {
17706+ if (_sg.wgpu.bindgroups_cache.items) {
17707+ _sg_free(_sg.wgpu.bindgroups_cache.items);
17708+ _sg.wgpu.bindgroups_cache.items = 0;
17709+ }
17710+ _sg.wgpu.bindgroups_cache.num = 0;
17711+ _sg.wgpu.bindgroups_cache.index_mask = 0;
17712+}
17713+
17714+_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_set(uint64_t hash, uint32_t bg_id) {
17715+ uint32_t index = hash & _sg.wgpu.bindgroups_cache.index_mask;
17716+ SOKOL_ASSERT(index < _sg.wgpu.bindgroups_cache.num);
17717+ SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items);
17718+ _sg.wgpu.bindgroups_cache.items[index].id = bg_id;
17719+}
17720+
17721+_SOKOL_PRIVATE uint32_t _sg_wgpu_bindgroups_cache_get(uint64_t hash) {
17722+ uint32_t index = hash & _sg.wgpu.bindgroups_cache.index_mask;
17723+ SOKOL_ASSERT(index < _sg.wgpu.bindgroups_cache.num);
17724+ SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items);
17725+ return _sg.wgpu.bindgroups_cache.items[index].id;
17726+}
17727+
17728+// called from wgpu resource destroy functions to also invalidate any
17729+// bindgroups cache slot and bindgroup referencing that resource
17730+_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_invalidate(_sg_wgpu_bindgroups_cache_item_type_t type, const _sg_slot_t* slot) {
17731+ const uint64_t key_mask = _sg_wgpu_bindgroups_cache_item(type, 0xFF, 0xFFFFFFFF, 0xFFFFFFFF);
17732+ const uint64_t key_item = _sg_wgpu_bindgroups_cache_item(type, 0, slot->id, slot->uninit_count) & key_mask;
17733+ SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items);
17734+ for (uint32_t cache_item_idx = 0; cache_item_idx < _sg.wgpu.bindgroups_cache.num; cache_item_idx++) {
17735+ const uint32_t bg_id = _sg.wgpu.bindgroups_cache.items[cache_item_idx].id;
17736+ if (bg_id != SG_INVALID_ID) {
17737+ _sg_wgpu_bindgroup_t* bg = _sg_wgpu_lookup_bindgroup(bg_id);
17738+ SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_VALID));
17739+ // check if resource is in bindgroup, if yes discard bindgroup and invalidate cache slot
17740+ bool invalidate_cache_item = false;
17741+ for (int key_item_idx = 0; key_item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS; key_item_idx++) {
17742+ if ((bg->key.items[key_item_idx] & key_mask) == key_item) {
17743+ invalidate_cache_item = true;
17744+ break;
17745+ }
17746+ }
17747+ if (invalidate_cache_item) {
17748+ _sg_wgpu_discard_bindgroup(bg); bg = 0;
17749+ _sg_wgpu_bindgroups_cache_set(cache_item_idx, SG_INVALID_ID);
17750+ _sg_stats_inc(wgpu.bindings.num_bindgroup_cache_invalidates);
17751+ }
17752+ }
17753+ }
17754+}
17755+
17756+_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_clear(void) {
17757+ memset(&_sg.wgpu.bindings_cache, 0, sizeof(_sg.wgpu.bindings_cache));
17758+}
17759+
17760+_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_vb_dirty(size_t index, const _sg_buffer_t* vb, uint64_t offset) {
17761+ SOKOL_ASSERT(index < SG_MAX_VERTEXBUFFER_BINDSLOTS);
17762+ if (vb) {
17763+ return (_sg.wgpu.bindings_cache.vbs[index].buffer.id != vb->slot.id)
17764+ || (_sg.wgpu.bindings_cache.vbs[index].offset != offset);
17765+ } else {
17766+ return _sg.wgpu.bindings_cache.vbs[index].buffer.id != SG_INVALID_ID;
17767+ }
17768+}
17769+
17770+_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_vb_update(size_t index, const _sg_buffer_t* vb, uint64_t offset) {
17771+ SOKOL_ASSERT(index < SG_MAX_VERTEXBUFFER_BINDSLOTS);
17772+ if (vb) {
17773+ _sg.wgpu.bindings_cache.vbs[index].buffer.id = vb->slot.id;
17774+ _sg.wgpu.bindings_cache.vbs[index].offset = offset;
17775+ } else {
17776+ _sg.wgpu.bindings_cache.vbs[index].buffer.id = SG_INVALID_ID;
17777+ _sg.wgpu.bindings_cache.vbs[index].offset = 0;
17778+ }
17779+}
17780+
17781+_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_ib_dirty(const _sg_buffer_t* ib, uint64_t offset) {
17782+ if (ib) {
17783+ return (_sg.wgpu.bindings_cache.ib.buffer.id != ib->slot.id)
17784+ || (_sg.wgpu.bindings_cache.ib.offset != offset);
17785+ } else {
17786+ return _sg.wgpu.bindings_cache.ib.buffer.id != SG_INVALID_ID;
17787+ }
17788+}
17789+
17790+_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_ib_update(const _sg_buffer_t* ib, uint64_t offset) {
17791+ if (ib) {
17792+ _sg.wgpu.bindings_cache.ib.buffer.id = ib->slot.id;
17793+ _sg.wgpu.bindings_cache.ib.offset = offset;
17794+ } else {
17795+ _sg.wgpu.bindings_cache.ib.buffer.id = SG_INVALID_ID;
17796+ _sg.wgpu.bindings_cache.ib.offset = 0;
17797+ }
17798+}
17799+
17800+_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_bg_dirty(const _sg_wgpu_bindgroup_t* bg) {
17801+ if (bg) {
17802+ return _sg.wgpu.bindings_cache.bg.id != bg->slot.id;
17803+ } else {
17804+ return _sg.wgpu.bindings_cache.bg.id != SG_INVALID_ID;
17805+ }
17806+}
17807+
17808+_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_bg_update(const _sg_wgpu_bindgroup_t* bg) {
17809+ if (bg) {
17810+ _sg.wgpu.bindings_cache.bg.id = bg->slot.id;
17811+ } else {
17812+ _sg.wgpu.bindings_cache.bg.id = SG_INVALID_ID;
17813+ }
17814+}
17815+
17816+_SOKOL_PRIVATE void _sg_wgpu_set_bindgroup(uint32_t bg_idx, _sg_wgpu_bindgroup_t* bg) {
17817+ if (_sg_wgpu_bindings_cache_bg_dirty(bg)) {
17818+ _sg_wgpu_bindings_cache_bg_update(bg);
17819+ _sg_stats_inc(wgpu.bindings.num_set_bindgroup);
17820+ if (_sg.cur_pass.is_compute) {
17821+ SOKOL_ASSERT(_sg.wgpu.cpass_enc);
17822+ if (bg) {
17823+ SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_VALID);
17824+ SOKOL_ASSERT(bg->bindgroup);
17825+ wgpuComputePassEncoderSetBindGroup(_sg.wgpu.cpass_enc, bg_idx, bg->bindgroup, 0, 0);
17826+ } else {
17827+ wgpuComputePassEncoderSetBindGroup(_sg.wgpu.cpass_enc, bg_idx, 0, 0, 0);
17828+ }
17829+ } else {
17830+ SOKOL_ASSERT(_sg.wgpu.rpass_enc);
17831+ if (bg) {
17832+ SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_VALID);
17833+ SOKOL_ASSERT(bg->bindgroup);
17834+ wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.rpass_enc, bg_idx, bg->bindgroup, 0, 0);
17835+ } else {
17836+ wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.rpass_enc, bg_idx, 0, 0, 0);
17837+ }
17838+ }
17839+ } else {
17840+ _sg_stats_inc(wgpu.bindings.num_skip_redundant_bindgroup);
17841+ }
17842+}
17843+
17844+_SOKOL_PRIVATE bool _sg_wgpu_apply_bindings_bindgroup(_sg_bindings_ptrs_t* bnd) {
17845+ if (!_sg.desc.wgpu.disable_bindgroups_cache) {
17846+ _sg_wgpu_bindgroup_t* bg = 0;
17847+ _sg_wgpu_bindgroups_cache_key_t key;
17848+ _sg_wgpu_init_bindgroups_cache_key(&key, bnd);
17849+ uint32_t bg_id = _sg_wgpu_bindgroups_cache_get(key.hash);
17850+ if (bg_id != SG_INVALID_ID) {
17851+ // potential cache hit
17852+ bg = _sg_wgpu_lookup_bindgroup(bg_id);
17853+ SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_VALID));
17854+ if (!_sg_wgpu_compare_bindgroups_cache_key(&key, &bg->key)) {
17855+ // cache collision, need to delete cached bindgroup
17856+ _sg_stats_inc(wgpu.bindings.num_bindgroup_cache_collisions);
17857+ _sg_wgpu_discard_bindgroup(bg);
17858+ _sg_wgpu_bindgroups_cache_set(key.hash, SG_INVALID_ID);
17859+ bg = 0;
17860+ } else {
17861+ _sg_stats_inc(wgpu.bindings.num_bindgroup_cache_hits);
17862+ }
17863+ } else {
17864+ _sg_stats_inc(wgpu.bindings.num_bindgroup_cache_misses);
17865+ }
17866+ if (bg == 0) {
17867+ // either no cache entry yet, or cache collision, create new bindgroup and store in cache
17868+ bg = _sg_wgpu_create_bindgroup(bnd);
17869+ _sg_wgpu_bindgroups_cache_set(key.hash, bg->slot.id);
17870+ }
17871+ if (bg && bg->slot.state == SG_RESOURCESTATE_VALID) {
17872+ _sg_wgpu_set_bindgroup(_SG_WGPU_VIEW_SMP_BINDGROUP_INDEX, bg);
17873+ } else {
17874+ return false;
17875+ }
17876+ } else {
17877+ // bindgroups cache disabled, create and destroy bindgroup on the fly (expensive!)
17878+ _sg_wgpu_bindgroup_t* bg = _sg_wgpu_create_bindgroup(bnd);
17879+ if (bg) {
17880+ if (bg->slot.state == SG_RESOURCESTATE_VALID) {
17881+ _sg_wgpu_set_bindgroup(_SG_WGPU_VIEW_SMP_BINDGROUP_INDEX, bg);
17882+ }
17883+ _sg_wgpu_discard_bindgroup(bg);
17884+ } else {
17885+ return false;
17886+ }
17887+ }
17888+ return true;
17889+}
17890+
17891+_SOKOL_PRIVATE bool _sg_wgpu_apply_index_buffer(_sg_bindings_ptrs_t* bnd) {
17892+ SOKOL_ASSERT(_sg.wgpu.rpass_enc);
17893+ const _sg_buffer_t* ib = bnd->ib;
17894+ uint64_t offset = (uint64_t)bnd->ib_offset;
17895+ if (_sg_wgpu_bindings_cache_ib_dirty(ib, offset)) {
17896+ _sg_wgpu_bindings_cache_ib_update(ib, offset);
17897+ if (ib) {
17898+ const WGPUIndexFormat format = _sg_wgpu_indexformat(bnd->pip->cmn.index_type);
17899+ const uint64_t buf_size = (uint64_t)ib->cmn.size;
17900+ SOKOL_ASSERT(buf_size > offset);
17901+ const uint64_t max_bytes = buf_size - offset;
17902+ wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.rpass_enc, ib->wgpu.buf, format, offset, max_bytes);
17903+ /*
17904+ NOTE: as per webgpu spec setIndexBuffer does not accept a null pointer
17905+ } else {
17906+ wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.rpass_enc, 0, WGPUIndexFormat_Undefined, 0, 0);
17907+ */
17908+ }
17909+ _sg_stats_inc(wgpu.bindings.num_set_index_buffer);
17910+ } else {
17911+ _sg_stats_inc(wgpu.bindings.num_skip_redundant_index_buffer);
17912+ }
17913+ return true;
17914+}
17915+
17916+_SOKOL_PRIVATE bool _sg_wgpu_apply_vertex_buffers(_sg_bindings_ptrs_t* bnd) {
17917+ SOKOL_ASSERT(_sg.wgpu.rpass_enc);
17918+ for (uint32_t slot = 0; slot < SG_MAX_VERTEXBUFFER_BINDSLOTS; slot++) {
17919+ const _sg_buffer_t* vb = bnd->vbs[slot];
17920+ const uint64_t offset = (uint64_t)bnd->vb_offsets[slot];
17921+ if (_sg_wgpu_bindings_cache_vb_dirty(slot, vb, offset)) {
17922+ _sg_wgpu_bindings_cache_vb_update(slot, vb, offset);
17923+ if (vb) {
17924+ const uint64_t buf_size = (uint64_t)vb->cmn.size;
17925+ SOKOL_ASSERT(buf_size > offset);
17926+ const uint64_t max_bytes = buf_size - offset;
17927+ wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.rpass_enc, slot, vb->wgpu.buf, offset, max_bytes);
17928+ } else {
17929+ wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.rpass_enc, slot, 0, 0, 0);
17930+ }
17931+ _sg_stats_inc(wgpu.bindings.num_set_vertex_buffer);
17932+ } else {
17933+ _sg_stats_inc(wgpu.bindings.num_skip_redundant_vertex_buffer);
17934+ }
17935+ }
17936+ return true;
17937+}
17938+
17939+_SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) {
17940+ SOKOL_ASSERT(desc);
17941+ SOKOL_ASSERT(desc->environment.wgpu.device);
17942+ SOKOL_ASSERT(desc->uniform_buffer_size > 0);
17943+ _sg.wgpu.valid = true;
17944+ _sg.wgpu.dev = (WGPUDevice) desc->environment.wgpu.device;
17945+ _sg.wgpu.queue = wgpuDeviceGetQueue(_sg.wgpu.dev);
17946+ SOKOL_ASSERT(_sg.wgpu.queue);
17947+
17948+ _sg_wgpu_init_caps();
17949+ _sg_wgpu_uniform_system_init(desc);
17950+ _sg_wgpu_bindgroups_pool_init(desc);
17951+ _sg_wgpu_bindgroups_cache_init(desc);
17952+ _sg_wgpu_bindings_cache_clear();
17953+}
17954+
17955+_SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) {
17956+ SOKOL_ASSERT(_sg.wgpu.valid);
17957+ _sg.wgpu.valid = false;
17958+ _sg_wgpu_discard_all_bindgroups();
17959+ _sg_wgpu_bindgroups_cache_discard();
17960+ _sg_wgpu_bindgroups_pool_discard();
17961+ _sg_wgpu_uniform_system_discard();
17962+ // the command encoder is usually released in sg_commit()
17963+ if (_sg.wgpu.cmd_enc) {
17964+ wgpuCommandEncoderRelease(_sg.wgpu.cmd_enc); _sg.wgpu.cmd_enc = 0;
17965+ }
17966+ wgpuQueueRelease(_sg.wgpu.queue); _sg.wgpu.queue = 0;
17967+}
17968+
17969+_SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) {
17970+ _sg_wgpu_bindings_cache_clear();
17971+}
17972+
17973+_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) {
17974+ SOKOL_ASSERT(buf && desc);
17975+ SOKOL_ASSERT(buf->cmn.size > 0);
17976+ const bool injected = (0 != desc->wgpu_buffer);
17977+ if (injected) {
17978+ buf->wgpu.buf = (WGPUBuffer) desc->wgpu_buffer;
17979+ wgpuBufferAddRef(buf->wgpu.buf);
17980+ } else {
17981+ // buffer mapping size must be multiple of 4, so round up buffer size (only a problem
17982+ // with index buffers containing odd number of indices)
17983+ const uint64_t wgpu_buf_size = _sg_roundup_u64((uint64_t)buf->cmn.size, 4);
17984+ const bool map_at_creation = buf->cmn.usage.immutable && (desc->data.ptr);
17985+
17986+ _SG_STRUCT(WGPUBufferDescriptor, wgpu_buf_desc);
17987+ wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(&buf->cmn.usage);
17988+ wgpu_buf_desc.size = wgpu_buf_size;
17989+ wgpu_buf_desc.mappedAtCreation = map_at_creation;
17990+ wgpu_buf_desc.label = _sg_wgpu_stringview(desc->label);
17991+ buf->wgpu.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &wgpu_buf_desc);
17992+ if (0 == buf->wgpu.buf) {
17993+ _SG_ERROR(WGPU_CREATE_BUFFER_FAILED);
17994+ return SG_RESOURCESTATE_FAILED;
17995+ }
17996+ if (map_at_creation) {
17997+ SOKOL_ASSERT(desc->data.ptr && (desc->data.size > 0));
17998+ SOKOL_ASSERT(desc->data.size <= (size_t)buf->cmn.size);
17999+ // FIXME: inefficient on WASM
18000+ void* ptr = wgpuBufferGetMappedRange(buf->wgpu.buf, 0, wgpu_buf_size);
18001+ SOKOL_ASSERT(ptr);
18002+ memcpy(ptr, desc->data.ptr, desc->data.size);
18003+ wgpuBufferUnmap(buf->wgpu.buf);
18004+ }
18005+ }
18006+ return SG_RESOURCESTATE_VALID;
18007+}
18008+
18009+_SOKOL_PRIVATE void _sg_wgpu_discard_buffer(_sg_buffer_t* buf) {
18010+ SOKOL_ASSERT(buf);
18011+ if (buf->wgpu.buf) {
18012+ wgpuBufferRelease(buf->wgpu.buf);
18013+ }
18014+}
18015+
18016+_SOKOL_PRIVATE void _sg_wgpu_copy_buffer_data(const _sg_buffer_t* buf, uint64_t offset, const sg_range* data) {
18017+ SOKOL_ASSERT((offset + data->size) <= (size_t)buf->cmn.size);
18018+ // WebGPU's write-buffer requires the size to be a multiple of four, so we may need to split the copy
18019+ // operation into two writeBuffer calls
18020+ uint64_t clamped_size = data->size & ~3UL;
18021+ uint64_t extra_size = data->size & 3UL;
18022+ SOKOL_ASSERT(extra_size < 4);
18023+ wgpuQueueWriteBuffer(_sg.wgpu.queue, buf->wgpu.buf, offset, data->ptr, clamped_size);
18024+ if (extra_size > 0) {
18025+ const uint64_t extra_src_offset = clamped_size;
18026+ const uint64_t extra_dst_offset = offset + clamped_size;
18027+ uint8_t extra_data[4] = { 0 };
18028+ const uint8_t* extra_src_ptr = ((uint8_t*)data->ptr) + extra_src_offset;
18029+ for (size_t i = 0; i < extra_size; i++) {
18030+ extra_data[i] = extra_src_ptr[i];
18031+ }
18032+ wgpuQueueWriteBuffer(_sg.wgpu.queue, buf->wgpu.buf, extra_dst_offset, extra_data, 4);
18033+ }
18034+}
18035+
18036+_SOKOL_PRIVATE void _sg_wgpu_copy_image_data(const _sg_image_t* img, const sg_image_data* data) {
18037+ _SG_STRUCT(WGPUTexelCopyBufferLayout, wgpu_layout);
18038+ _SG_STRUCT(WGPUTexelCopyTextureInfo, wgpu_copy_tex);
18039+ wgpu_copy_tex.texture = img->wgpu.tex;
18040+ wgpu_copy_tex.aspect = WGPUTextureAspect_All;
18041+ _SG_STRUCT(WGPUExtent3D, wgpu_extent);
18042+ for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) {
18043+ wgpu_copy_tex.mipLevel = (uint32_t)mip_index;
18044+ int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index);
18045+ int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index);
18046+ int mip_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices;
18047+ const int row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1);
18048+ const int num_rows = _sg_num_rows(img->cmn.pixel_format, mip_height);
18049+ if (_sg_is_compressed_pixel_format(img->cmn.pixel_format)) {
18050+ mip_width = _sg_roundup(mip_width, 4);
18051+ mip_height = _sg_roundup(mip_height, 4);
18052+ }
18053+ wgpu_layout.bytesPerRow = (uint32_t)row_pitch;
18054+ wgpu_layout.rowsPerImage = (uint32_t)num_rows;
18055+ wgpu_extent.width = (uint32_t)mip_width;
18056+ wgpu_extent.height = (uint32_t)mip_height;
18057+ wgpu_extent.depthOrArrayLayers = (uint32_t)mip_slices;
18058+ const sg_range* mip_data = &data->mip_levels[mip_index];
18059+ wgpuQueueWriteTexture(_sg.wgpu.queue, &wgpu_copy_tex, mip_data->ptr, mip_data->size, &wgpu_layout, &wgpu_extent);
18060+ }
18061+}
18062+
18063+_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const sg_image_desc* desc) {
18064+ SOKOL_ASSERT(img && desc);
18065+ const bool injected = (0 != desc->wgpu_texture);
18066+ if (injected) {
18067+ img->wgpu.tex = (WGPUTexture)desc->wgpu_texture;
18068+ wgpuTextureAddRef(img->wgpu.tex);
18069+ } else {
18070+ _SG_STRUCT(WGPUTextureDescriptor, wgpu_tex_desc);
18071+ wgpu_tex_desc.label = _sg_wgpu_stringview(desc->label);
18072+ wgpu_tex_desc.usage = WGPUTextureUsage_TextureBinding|WGPUTextureUsage_CopyDst;
18073+ if (desc->usage.color_attachment || desc->usage.resolve_attachment || desc->usage.depth_stencil_attachment) {
18074+ wgpu_tex_desc.usage |= WGPUTextureUsage_RenderAttachment;
18075+ }
18076+ if (desc->usage.storage_image) {
18077+ wgpu_tex_desc.usage |= WGPUTextureUsage_StorageBinding;
18078+ }
18079+ wgpu_tex_desc.dimension = _sg_wgpu_texture_dimension(img->cmn.type);
18080+ wgpu_tex_desc.size.width = (uint32_t) img->cmn.width;
18081+ wgpu_tex_desc.size.height = (uint32_t) img->cmn.height;
18082+ wgpu_tex_desc.size.depthOrArrayLayers = (uint32_t) img->cmn.num_slices;
18083+ wgpu_tex_desc.format = _sg_wgpu_textureformat(img->cmn.pixel_format);
18084+ wgpu_tex_desc.mipLevelCount = (uint32_t) img->cmn.num_mipmaps;
18085+ wgpu_tex_desc.sampleCount = (uint32_t) img->cmn.sample_count;
18086+ img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc);
18087+ if (0 == img->wgpu.tex) {
18088+ _SG_ERROR(WGPU_CREATE_TEXTURE_FAILED);
18089+ return SG_RESOURCESTATE_FAILED;
18090+ }
18091+ if (desc->data.mip_levels[0].ptr) {
18092+ _sg_wgpu_copy_image_data(img, &desc->data);
18093+ }
18094+ }
18095+ return SG_RESOURCESTATE_VALID;
18096+}
18097+
18098+_SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) {
18099+ SOKOL_ASSERT(img);
18100+ if (img->wgpu.tex) {
18101+ wgpuTextureRelease(img->wgpu.tex);
18102+ img->wgpu.tex = 0;
18103+ }
18104+}
18105+
18106+_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) {
18107+ SOKOL_ASSERT(smp && desc);
18108+ SOKOL_ASSERT(_sg.wgpu.dev);
18109+ const bool injected = (0 != desc->wgpu_sampler);
18110+ if (injected) {
18111+ smp->wgpu.smp = (WGPUSampler) desc->wgpu_sampler;
18112+ wgpuSamplerAddRef(smp->wgpu.smp);
18113+ } else {
18114+ _SG_STRUCT(WGPUSamplerDescriptor, wgpu_desc);
18115+ wgpu_desc.label = _sg_wgpu_stringview(desc->label);
18116+ wgpu_desc.addressModeU = _sg_wgpu_sampler_address_mode(desc->wrap_u);
18117+ wgpu_desc.addressModeV = _sg_wgpu_sampler_address_mode(desc->wrap_v);
18118+ wgpu_desc.addressModeW = _sg_wgpu_sampler_address_mode(desc->wrap_w);
18119+ wgpu_desc.magFilter = _sg_wgpu_sampler_minmag_filter(desc->mag_filter);
18120+ wgpu_desc.minFilter = _sg_wgpu_sampler_minmag_filter(desc->min_filter);
18121+ wgpu_desc.mipmapFilter = _sg_wgpu_sampler_mipmap_filter(desc->mipmap_filter);
18122+ wgpu_desc.lodMinClamp = desc->min_lod;
18123+ wgpu_desc.lodMaxClamp = desc->max_lod;
18124+ wgpu_desc.compare = _sg_wgpu_comparefunc(desc->compare);
18125+ if (wgpu_desc.compare == WGPUCompareFunction_Never) {
18126+ wgpu_desc.compare = WGPUCompareFunction_Undefined;
18127+ }
18128+ wgpu_desc.maxAnisotropy = (uint16_t)desc->max_anisotropy;
18129+ smp->wgpu.smp = wgpuDeviceCreateSampler(_sg.wgpu.dev, &wgpu_desc);
18130+ if (0 == smp->wgpu.smp) {
18131+ _SG_ERROR(WGPU_CREATE_SAMPLER_FAILED);
18132+ return SG_RESOURCESTATE_FAILED;
18133+ }
18134+ }
18135+ return SG_RESOURCESTATE_VALID;
18136+}
18137+
18138+_SOKOL_PRIVATE void _sg_wgpu_discard_sampler(_sg_sampler_t* smp) {
18139+ SOKOL_ASSERT(smp);
18140+ _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER, &smp->slot);
18141+ if (smp->wgpu.smp) {
18142+ wgpuSamplerRelease(smp->wgpu.smp);
18143+ smp->wgpu.smp = 0;
18144+ }
18145+}
18146+
18147+_SOKOL_PRIVATE _sg_wgpu_shader_func_t _sg_wgpu_create_shader_func(const sg_shader_function* func, const char* label) {
18148+ SOKOL_ASSERT(func);
18149+ SOKOL_ASSERT(func->source);
18150+ SOKOL_ASSERT(func->entry);
18151+
18152+ _SG_STRUCT(_sg_wgpu_shader_func_t, res);
18153+ _sg_strcpy(&res.entry, func->entry);
18154+
18155+ _SG_STRUCT(WGPUShaderSourceWGSL, wgpu_shdsrc_wgsl);
18156+ wgpu_shdsrc_wgsl.chain.sType = WGPUSType_ShaderSourceWGSL;
18157+ wgpu_shdsrc_wgsl.code = _sg_wgpu_stringview(func->source);
18158+
18159+ _SG_STRUCT(WGPUShaderModuleDescriptor, wgpu_shdmod_desc);
18160+ wgpu_shdmod_desc.nextInChain = &wgpu_shdsrc_wgsl.chain;
18161+ wgpu_shdmod_desc.label = _sg_wgpu_stringview(label);
18162+
18163+ // NOTE: if compilation fails we won't actually find out in this call since
18164+ // it always returns a valid module handle, and the GetCompilationInfo() call
18165+ // is asynchronous
18166+ res.module = wgpuDeviceCreateShaderModule(_sg.wgpu.dev, &wgpu_shdmod_desc);
18167+ if (0 == res.module) {
18168+ _SG_ERROR(WGPU_CREATE_SHADER_MODULE_FAILED);
18169+ }
18170+ return res;
18171+}
18172+
18173+_SOKOL_PRIVATE void _sg_wgpu_discard_shader_func(_sg_wgpu_shader_func_t* func) {
18174+ if (func->module) {
18175+ wgpuShaderModuleRelease(func->module);
18176+ func->module = 0;
18177+ }
18178+}
18179+
18180+typedef struct { uint8_t sokol_slot, wgpu_slot; } _sg_wgpu_dynoffset_mapping_t;
18181+
18182+_SOKOL_PRIVATE int _sg_wgpu_dynoffset_cmp(const void* a, const void* b) {
18183+ const _sg_wgpu_dynoffset_mapping_t* aa = (const _sg_wgpu_dynoffset_mapping_t*)a;
18184+ const _sg_wgpu_dynoffset_mapping_t* bb = (const _sg_wgpu_dynoffset_mapping_t*)b;
18185+ if (aa->wgpu_slot < bb->wgpu_slot) return -1;
18186+ else if (aa->wgpu_slot > bb->wgpu_slot) return 1;
18187+ return 0;
18188+}
18189+
18190+// NOTE: this is an out-of-range check for WGSL bindslots that's also active in release mode
18191+_SOKOL_PRIVATE bool _sg_wgpu_ensure_wgsl_bindslot_ranges(const sg_shader_desc* desc) {
18192+ SOKOL_ASSERT(desc);
18193+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
18194+ const sg_shader_uniform_block* ub = &desc->uniform_blocks[i];
18195+ if (ub->stage != SG_SHADERSTAGE_NONE) {
18196+ if (ub->wgsl_group0_binding_n >= _SG_WGPU_MAX_UB_BINDGROUP_WGSL_SLOTS) {
18197+ _SG_ERROR(WGPU_UNIFORMBLOCK_WGSL_GROUP0_BINDING_OUT_OF_RANGE);
18198+ return false;
18199+ }
18200+ }
18201+ }
18202+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
18203+ const sg_shader_view* view = &desc->views[i];
18204+ if (view->texture.stage != SG_SHADERSTAGE_NONE) {
18205+ if (view->texture.wgsl_group1_binding_n >= _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_WGSL_SLOTS) {
18206+ _SG_ERROR(WGPU_TEXTURE_WGSL_GROUP1_BINDING_OUT_OF_RANGE);
18207+ return false;
18208+ }
18209+ }
18210+ if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) {
18211+ if (view->storage_buffer.wgsl_group1_binding_n >= _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_WGSL_SLOTS) {
18212+ _SG_ERROR(WGPU_STORAGEBUFFER_WGSL_GROUP1_BINDING_OUT_OF_RANGE);
18213+ return false;
18214+ }
18215+ }
18216+ if (view->storage_image.stage != SG_SHADERSTAGE_NONE) {
18217+ if (view->storage_image.wgsl_group1_binding_n >= _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_WGSL_SLOTS) {
18218+ _SG_ERROR(WGPU_STORAGEIMAGE_WGSL_GROUP1_BINDING_OUT_OF_RANGE);
18219+ return false;
18220+ }
18221+ }
18222+ }
18223+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
18224+ const sg_shader_sampler* smp = &desc->samplers[i];
18225+ if (smp->stage != SG_SHADERSTAGE_NONE) {
18226+ if (smp->wgsl_group1_binding_n >= _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_WGSL_SLOTS) {
18227+ _SG_ERROR(WGPU_SAMPLER_WGSL_GROUP1_BINDING_OUT_OF_RANGE);
18228+ return false;
18229+ }
18230+ }
18231+ }
18232+ return true;
18233+}
18234+
18235+_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) {
18236+ SOKOL_ASSERT(shd && desc);
18237+ SOKOL_ASSERT(shd->wgpu.vertex_func.module == 0);
18238+ SOKOL_ASSERT(shd->wgpu.fragment_func.module == 0);
18239+ SOKOL_ASSERT(shd->wgpu.compute_func.module == 0);
18240+ SOKOL_ASSERT(shd->wgpu.bgl_ub == 0);
18241+ SOKOL_ASSERT(shd->wgpu.bg_ub == 0);
18242+ SOKOL_ASSERT(shd->wgpu.bgl_view_smp == 0);
18243+
18244+ // do a release-mode bounds-check on wgsl bindslots, even though out-of-range
18245+ // bindslots can't cause out-of-bounds accesses in the wgpu backend, this
18246+ // is done to be consistent with the other backends
18247+ if (!_sg_wgpu_ensure_wgsl_bindslot_ranges(desc)) {
18248+ return SG_RESOURCESTATE_FAILED;
18249+ }
18250+
18251+ // build shader modules
18252+ bool shd_valid = true;
18253+ if (desc->vertex_func.source) {
18254+ shd->wgpu.vertex_func = _sg_wgpu_create_shader_func(&desc->vertex_func, desc->label);
18255+ shd_valid &= shd->wgpu.vertex_func.module != 0;
18256+ }
18257+ if (desc->fragment_func.source) {
18258+ shd->wgpu.fragment_func = _sg_wgpu_create_shader_func(&desc->fragment_func, desc->label);
18259+ shd_valid &= shd->wgpu.fragment_func.module != 0;
18260+ }
18261+ if (desc->compute_func.source) {
18262+ shd->wgpu.compute_func = _sg_wgpu_create_shader_func(&desc->compute_func, desc->label);
18263+ shd_valid &= shd->wgpu.compute_func.module != 0;
18264+ }
18265+ if (!shd_valid) {
18266+ _sg_wgpu_discard_shader_func(&shd->wgpu.vertex_func);
18267+ _sg_wgpu_discard_shader_func(&shd->wgpu.fragment_func);
18268+ _sg_wgpu_discard_shader_func(&shd->wgpu.compute_func);
18269+ return SG_RESOURCESTATE_FAILED;
18270+ }
18271+
18272+ // create bind group layout and bind group for uniform blocks
18273+ // NOTE also need to create a mapping of sokol ub bind slots to array indices
18274+ // for the dynamic offsets array in the setBindGroup call
18275+ SOKOL_ASSERT(_SG_WGPU_MAX_UB_BINDGROUP_ENTRIES <= _SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES);
18276+ _SG_STRUCT(WGPUBindGroupLayoutEntry, bgl_entries[_SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES]);
18277+ _SG_STRUCT(WGPUBindGroupLayoutDescriptor, bgl_desc);
18278+ _SG_STRUCT(WGPUBindGroupEntry, bg_entries[_SG_WGPU_MAX_VIEW_SMP_BINDGROUP_ENTRIES]);
18279+ _SG_STRUCT(WGPUBindGroupDescriptor, bg_desc);
18280+ _SG_STRUCT(_sg_wgpu_dynoffset_mapping_t, dynoffset_map[SG_MAX_UNIFORMBLOCK_BINDSLOTS]);
18281+ size_t bgl_index = 0;
18282+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
18283+ if (shd->cmn.uniform_blocks[i].stage == SG_SHADERSTAGE_NONE) {
18284+ continue;
18285+ }
18286+ shd->wgpu.ub_grp0_bnd_n[i] = desc->uniform_blocks[i].wgsl_group0_binding_n;
18287+ WGPUBindGroupEntry* bg_entry = &bg_entries[bgl_index];
18288+ WGPUBindGroupLayoutEntry* bgl_entry = &bgl_entries[bgl_index];
18289+ bgl_entry->binding = shd->wgpu.ub_grp0_bnd_n[i];
18290+ bgl_entry->visibility = _sg_wgpu_shader_stage(shd->cmn.uniform_blocks[i].stage);
18291+ bgl_entry->buffer.type = WGPUBufferBindingType_Uniform;
18292+ bgl_entry->buffer.hasDynamicOffset = true;
18293+ bg_entry->binding = bgl_entry->binding;
18294+ bg_entry->buffer = _sg.wgpu.uniform.buf;
18295+ bg_entry->size = _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE;
18296+ dynoffset_map[i].sokol_slot = (uint8_t)i;
18297+ dynoffset_map[i].wgpu_slot = (uint8_t)bgl_entry->binding;
18298+ bgl_index += 1;
18299+ }
18300+ bgl_desc.entryCount = bgl_index;
18301+ bgl_desc.entries = bgl_entries;
18302+ shd->wgpu.bgl_ub = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc);
18303+ SOKOL_ASSERT(shd->wgpu.bgl_ub);
18304+ bg_desc.layout = shd->wgpu.bgl_ub;
18305+ bg_desc.entryCount = bgl_index;
18306+ bg_desc.entries = bg_entries;
18307+ shd->wgpu.bg_ub = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc);
18308+ SOKOL_ASSERT(shd->wgpu.bg_ub);
18309+
18310+ // sort the dynoffset_map by wgpu bindings, this is because the
18311+ // dynamic offsets of the WebGPU setBindGroup call must be in
18312+ // 'binding order', not 'bindgroup entry order'
18313+ qsort(dynoffset_map, bgl_index, sizeof(_sg_wgpu_dynoffset_mapping_t), _sg_wgpu_dynoffset_cmp);
18314+ shd->wgpu.ub_num_dynoffsets = (uint8_t)bgl_index;
18315+ for (uint8_t i = 0; i < bgl_index; i++) {
18316+ const uint8_t sokol_slot = dynoffset_map[i].sokol_slot;
18317+ shd->wgpu.ub_dynoffsets[sokol_slot] = i;
18318+ }
18319+
18320+ // create bind group layout for textures, storage buffers/images and samplers
18321+ _sg_clear(bgl_entries, sizeof(bgl_entries));
18322+ _sg_clear(&bgl_desc, sizeof(bgl_desc));
18323+ bgl_index = 0;
18324+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
18325+ if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) {
18326+ continue;
18327+ }
18328+ WGPUBindGroupLayoutEntry* bgl_entry = &bgl_entries[bgl_index];
18329+ bgl_entry->visibility = _sg_wgpu_shader_stage(shd->cmn.views[i].stage);
18330+ if (shd->cmn.views[i].view_type == SG_VIEWTYPE_TEXTURE) {
18331+ shd->wgpu.view_grp1_bnd_n[i] = desc->views[i].texture.wgsl_group1_binding_n;
18332+ const bool msaa = shd->cmn.views[i].multisampled;
18333+ bgl_entry->texture.viewDimension = _sg_wgpu_texture_view_dimension(shd->cmn.views[i].image_type);
18334+ bgl_entry->texture.sampleType = _sg_wgpu_texture_sample_type(shd->cmn.views[i].sample_type, msaa);
18335+ bgl_entry->texture.multisampled = msaa;
18336+ } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEBUFFER) {
18337+ shd->wgpu.view_grp1_bnd_n[i] = desc->views[i].storage_buffer.wgsl_group1_binding_n;
18338+ if (shd->cmn.views[i].sbuf_readonly) {
18339+ bgl_entry->buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
18340+ } else {
18341+ bgl_entry->buffer.type = WGPUBufferBindingType_Storage;
18342+ }
18343+ } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEIMAGE) {
18344+ shd->wgpu.view_grp1_bnd_n[i] = desc->views[i].storage_image.wgsl_group1_binding_n;
18345+ if (shd->cmn.views[i].simg_writeonly) {
18346+ bgl_entry->storageTexture.access = WGPUStorageTextureAccess_WriteOnly;
18347+ } else {
18348+ bgl_entry->storageTexture.access = WGPUStorageTextureAccess_ReadWrite;
18349+ }
18350+ bgl_entry->storageTexture.format = _sg_wgpu_textureformat(shd->cmn.views[i].access_format);
18351+ bgl_entry->storageTexture.viewDimension = _sg_wgpu_texture_view_dimension(shd->cmn.views[i].image_type);
18352+ } else {
18353+ SOKOL_UNREACHABLE;
18354+ }
18355+ bgl_entry->binding = shd->wgpu.view_grp1_bnd_n[i];
18356+ bgl_index += 1;
18357+ }
18358+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
18359+ if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) {
18360+ continue;
18361+ }
18362+ shd->wgpu.smp_grp1_bnd_n[i] = desc->samplers[i].wgsl_group1_binding_n;
18363+ WGPUBindGroupLayoutEntry* bgl_entry = &bgl_entries[bgl_index];
18364+ bgl_entry->binding = shd->wgpu.smp_grp1_bnd_n[i];
18365+ bgl_entry->visibility = _sg_wgpu_shader_stage(shd->cmn.samplers[i].stage);
18366+ bgl_entry->sampler.type = _sg_wgpu_sampler_binding_type(shd->cmn.samplers[i].sampler_type);
18367+ bgl_index += 1;
18368+ }
18369+ bgl_desc.entryCount = bgl_index;
18370+ bgl_desc.entries = bgl_entries;
18371+ shd->wgpu.bgl_view_smp = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc);
18372+ if (shd->wgpu.bgl_view_smp == 0) {
18373+ _SG_ERROR(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED);
18374+ return SG_RESOURCESTATE_FAILED;
18375+ }
18376+ return SG_RESOURCESTATE_VALID;
18377+}
18378+
18379+_SOKOL_PRIVATE void _sg_wgpu_discard_shader(_sg_shader_t* shd) {
18380+ SOKOL_ASSERT(shd);
18381+ _sg_wgpu_discard_shader_func(&shd->wgpu.vertex_func);
18382+ _sg_wgpu_discard_shader_func(&shd->wgpu.fragment_func);
18383+ _sg_wgpu_discard_shader_func(&shd->wgpu.compute_func);
18384+ if (shd->wgpu.bgl_ub) {
18385+ wgpuBindGroupLayoutRelease(shd->wgpu.bgl_ub);
18386+ shd->wgpu.bgl_ub = 0;
18387+ }
18388+ if (shd->wgpu.bg_ub) {
18389+ wgpuBindGroupRelease(shd->wgpu.bg_ub);
18390+ shd->wgpu.bg_ub = 0;
18391+ }
18392+ if (shd->wgpu.bgl_view_smp) {
18393+ wgpuBindGroupLayoutRelease(shd->wgpu.bgl_view_smp);
18394+ shd->wgpu.bgl_view_smp = 0;
18395+ }
18396+}
18397+
18398+_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) {
18399+ SOKOL_ASSERT(pip && desc);
18400+
18401+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
18402+ SOKOL_ASSERT(shd->wgpu.bgl_ub);
18403+ SOKOL_ASSERT(shd->wgpu.bgl_view_smp);
18404+
18405+ pip->wgpu.blend_color.r = (double) desc->blend_color.r;
18406+ pip->wgpu.blend_color.g = (double) desc->blend_color.g;
18407+ pip->wgpu.blend_color.b = (double) desc->blend_color.b;
18408+ pip->wgpu.blend_color.a = (double) desc->blend_color.a;
18409+
18410+ // - @group(0) for uniform blocks
18411+ // - @group(1) for all image, sampler and storagebuffer resources
18412+ size_t num_bgls = 2;
18413+ _SG_STRUCT(WGPUBindGroupLayout, wgpu_bgl[_SG_WGPU_MAX_BINDGROUPS]);
18414+ wgpu_bgl[_SG_WGPU_UB_BINDGROUP_INDEX ] = shd->wgpu.bgl_ub;
18415+ wgpu_bgl[_SG_WGPU_VIEW_SMP_BINDGROUP_INDEX] = shd->wgpu.bgl_view_smp;
18416+ _SG_STRUCT(WGPUPipelineLayoutDescriptor, wgpu_pl_desc);
18417+ wgpu_pl_desc.bindGroupLayoutCount = num_bgls;
18418+ wgpu_pl_desc.bindGroupLayouts = &wgpu_bgl[0];
18419+ const WGPUPipelineLayout wgpu_pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &wgpu_pl_desc);
18420+ if (0 == wgpu_pip_layout) {
18421+ _SG_ERROR(WGPU_CREATE_PIPELINE_LAYOUT_FAILED);
18422+ return SG_RESOURCESTATE_FAILED;
18423+ }
18424+ SOKOL_ASSERT(wgpu_pip_layout);
18425+
18426+ if (pip->cmn.is_compute) {
18427+ _SG_STRUCT(WGPUComputePipelineDescriptor, wgpu_pip_desc);
18428+ wgpu_pip_desc.label = _sg_wgpu_stringview(desc->label);
18429+ wgpu_pip_desc.layout = wgpu_pip_layout;
18430+ wgpu_pip_desc.compute.module = shd->wgpu.compute_func.module;
18431+ wgpu_pip_desc.compute.entryPoint = _sg_wgpu_stringview(shd->wgpu.compute_func.entry.buf);
18432+ pip->wgpu.cpip = wgpuDeviceCreateComputePipeline(_sg.wgpu.dev, &wgpu_pip_desc);
18433+ wgpuPipelineLayoutRelease(wgpu_pip_layout);
18434+ if (0 == pip->wgpu.cpip) {
18435+ _SG_ERROR(WGPU_CREATE_COMPUTE_PIPELINE_FAILED);
18436+ return SG_RESOURCESTATE_FAILED;
18437+ }
18438+ } else {
18439+ _SG_STRUCT(WGPUVertexBufferLayout, wgpu_vb_layouts[SG_MAX_VERTEXBUFFER_BINDSLOTS]);
18440+ _SG_STRUCT(WGPUVertexAttribute, wgpu_vtx_attrs[SG_MAX_VERTEXBUFFER_BINDSLOTS][SG_MAX_VERTEX_ATTRIBUTES]);
18441+ int wgpu_vb_num = 0;
18442+ for (int vb_idx = 0; vb_idx < SG_MAX_VERTEXBUFFER_BINDSLOTS; vb_idx++, wgpu_vb_num++) {
18443+ const sg_vertex_buffer_layout_state* vbl_state = &desc->layout.buffers[vb_idx];
18444+ if (0 == vbl_state->stride) {
18445+ break;
18446+ }
18447+ wgpu_vb_layouts[vb_idx].arrayStride = (uint64_t)vbl_state->stride;
18448+ wgpu_vb_layouts[vb_idx].stepMode = _sg_wgpu_stepmode(vbl_state->step_func);
18449+ wgpu_vb_layouts[vb_idx].attributes = &wgpu_vtx_attrs[vb_idx][0];
18450+ }
18451+ for (int va_idx = 0; va_idx < SG_MAX_VERTEX_ATTRIBUTES; va_idx++) {
18452+ const sg_vertex_attr_state* va_state = &desc->layout.attrs[va_idx];
18453+ if (SG_VERTEXFORMAT_INVALID == va_state->format) {
18454+ break;
18455+ }
18456+ const int vb_idx = va_state->buffer_index;
18457+ SOKOL_ASSERT(vb_idx < SG_MAX_VERTEXBUFFER_BINDSLOTS);
18458+ SOKOL_ASSERT(pip->cmn.vertex_buffer_layout_active[vb_idx]);
18459+ const size_t wgpu_attr_idx = wgpu_vb_layouts[vb_idx].attributeCount;
18460+ wgpu_vb_layouts[vb_idx].attributeCount += 1;
18461+ wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].format = _sg_wgpu_vertexformat(va_state->format);
18462+ wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].offset = (uint64_t)va_state->offset;
18463+ wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].shaderLocation = (uint32_t)va_idx;
18464+ }
18465+
18466+ _SG_STRUCT(WGPURenderPipelineDescriptor, wgpu_pip_desc);
18467+ _SG_STRUCT(WGPUDepthStencilState, wgpu_ds_state);
18468+ _SG_STRUCT(WGPUFragmentState, wgpu_frag_state);
18469+ _SG_STRUCT(WGPUColorTargetState, wgpu_ctgt_state[SG_MAX_COLOR_ATTACHMENTS]);
18470+ _SG_STRUCT(WGPUBlendState, wgpu_blend_state[SG_MAX_COLOR_ATTACHMENTS]);
18471+ wgpu_pip_desc.label = _sg_wgpu_stringview(desc->label);
18472+ wgpu_pip_desc.layout = wgpu_pip_layout;
18473+ wgpu_pip_desc.vertex.module = shd->wgpu.vertex_func.module;
18474+ wgpu_pip_desc.vertex.entryPoint = _sg_wgpu_stringview(shd->wgpu.vertex_func.entry.buf);
18475+ wgpu_pip_desc.vertex.bufferCount = (size_t)wgpu_vb_num;
18476+ wgpu_pip_desc.vertex.buffers = &wgpu_vb_layouts[0];
18477+ wgpu_pip_desc.primitive.topology = _sg_wgpu_topology(desc->primitive_type);
18478+ wgpu_pip_desc.primitive.stripIndexFormat = _sg_wgpu_stripindexformat(desc->primitive_type, desc->index_type);
18479+ wgpu_pip_desc.primitive.frontFace = _sg_wgpu_frontface(desc->face_winding);
18480+ wgpu_pip_desc.primitive.cullMode = _sg_wgpu_cullmode(desc->cull_mode);
18481+ if (SG_PIXELFORMAT_NONE != desc->depth.pixel_format) {
18482+ wgpu_ds_state.format = _sg_wgpu_textureformat(desc->depth.pixel_format);
18483+ wgpu_ds_state.depthWriteEnabled = _sg_wgpu_optional_bool(desc->depth.write_enabled);
18484+ wgpu_ds_state.depthCompare = _sg_wgpu_comparefunc(desc->depth.compare);
18485+ wgpu_ds_state.stencilFront.compare = _sg_wgpu_comparefunc(desc->stencil.front.compare);
18486+ wgpu_ds_state.stencilFront.failOp = _sg_wgpu_stencilop(desc->stencil.front.fail_op);
18487+ wgpu_ds_state.stencilFront.depthFailOp = _sg_wgpu_stencilop(desc->stencil.front.depth_fail_op);
18488+ wgpu_ds_state.stencilFront.passOp = _sg_wgpu_stencilop(desc->stencil.front.pass_op);
18489+ wgpu_ds_state.stencilBack.compare = _sg_wgpu_comparefunc(desc->stencil.back.compare);
18490+ wgpu_ds_state.stencilBack.failOp = _sg_wgpu_stencilop(desc->stencil.back.fail_op);
18491+ wgpu_ds_state.stencilBack.depthFailOp = _sg_wgpu_stencilop(desc->stencil.back.depth_fail_op);
18492+ wgpu_ds_state.stencilBack.passOp = _sg_wgpu_stencilop(desc->stencil.back.pass_op);
18493+ wgpu_ds_state.stencilReadMask = desc->stencil.read_mask;
18494+ wgpu_ds_state.stencilWriteMask = desc->stencil.write_mask;
18495+ wgpu_ds_state.depthBias = (int32_t)desc->depth.bias;
18496+ wgpu_ds_state.depthBiasSlopeScale = desc->depth.bias_slope_scale;
18497+ wgpu_ds_state.depthBiasClamp = desc->depth.bias_clamp;
18498+ wgpu_pip_desc.depthStencil = &wgpu_ds_state;
18499+ }
18500+ wgpu_pip_desc.multisample.count = (uint32_t)desc->sample_count;
18501+ wgpu_pip_desc.multisample.mask = 0xFFFFFFFF;
18502+ wgpu_pip_desc.multisample.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled;
18503+ if (desc->color_count > 0) {
18504+ wgpu_frag_state.module = shd->wgpu.fragment_func.module;
18505+ wgpu_frag_state.entryPoint = _sg_wgpu_stringview(shd->wgpu.fragment_func.entry.buf);
18506+ wgpu_frag_state.targetCount = (size_t)desc->color_count;
18507+ wgpu_frag_state.targets = &wgpu_ctgt_state[0];
18508+ for (int i = 0; i < desc->color_count; i++) {
18509+ SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS);
18510+ wgpu_ctgt_state[i].format = _sg_wgpu_textureformat(desc->colors[i].pixel_format);
18511+ wgpu_ctgt_state[i].writeMask = _sg_wgpu_colorwritemask(desc->colors[i].write_mask);
18512+ if (desc->colors[i].blend.enabled) {
18513+ wgpu_ctgt_state[i].blend = &wgpu_blend_state[i];
18514+ wgpu_blend_state[i].color.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_rgb);
18515+ wgpu_blend_state[i].color.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_rgb);
18516+ wgpu_blend_state[i].color.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_rgb);
18517+ wgpu_blend_state[i].alpha.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_alpha);
18518+ wgpu_blend_state[i].alpha.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_alpha);
18519+ wgpu_blend_state[i].alpha.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_alpha);
18520+ }
18521+ }
18522+ wgpu_pip_desc.fragment = &wgpu_frag_state;
18523+ }
18524+ pip->wgpu.rpip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &wgpu_pip_desc);
18525+ wgpuPipelineLayoutRelease(wgpu_pip_layout);
18526+ if (0 == pip->wgpu.rpip) {
18527+ _SG_ERROR(WGPU_CREATE_RENDER_PIPELINE_FAILED);
18528+ return SG_RESOURCESTATE_FAILED;
18529+ }
18530+ }
18531+ return SG_RESOURCESTATE_VALID;
18532+}
18533+
18534+_SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) {
18535+ SOKOL_ASSERT(pip);
18536+ _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE, &pip->slot);
18537+ if (pip->wgpu.rpip) {
18538+ wgpuRenderPipelineRelease(pip->wgpu.rpip);
18539+ pip->wgpu.rpip = 0;
18540+ }
18541+ if (pip->wgpu.cpip) {
18542+ wgpuComputePipelineRelease(pip->wgpu.cpip);
18543+ pip->wgpu.cpip = 0;
18544+ }
18545+}
18546+
18547+_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_view(_sg_view_t* view, const sg_view_desc* desc) {
18548+ SOKOL_ASSERT(view && desc);
18549+ if (view->cmn.type != SG_VIEWTYPE_STORAGEBUFFER) {
18550+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
18551+ SOKOL_ASSERT(img->wgpu.tex);
18552+ SOKOL_ASSERT(view->cmn.img.mip_level_count >= 1);
18553+ SOKOL_ASSERT(view->cmn.img.slice_count >= 1);
18554+ _SG_STRUCT(WGPUTextureViewDescriptor, wgpu_texview_desc);
18555+ wgpu_texview_desc.label = _sg_wgpu_stringview(desc->label);
18556+ wgpu_texview_desc.baseMipLevel = (uint32_t)view->cmn.img.mip_level;
18557+ wgpu_texview_desc.mipLevelCount = (uint32_t)view->cmn.img.mip_level_count;
18558+ wgpu_texview_desc.baseArrayLayer = (uint32_t)view->cmn.img.slice;
18559+ wgpu_texview_desc.arrayLayerCount = (uint32_t)view->cmn.img.slice_count;
18560+ if (view->cmn.type == SG_VIEWTYPE_TEXTURE) {
18561+ wgpu_texview_desc.dimension = _sg_wgpu_texture_view_dimension(img->cmn.type);
18562+ } else {
18563+ wgpu_texview_desc.dimension = _sg_wgpu_attachment_view_dimension(img->cmn.type);
18564+ }
18565+ if (view->cmn.type == SG_VIEWTYPE_DEPTHSTENCILATTACHMENT) {
18566+ wgpu_texview_desc.aspect = WGPUTextureAspect_All;
18567+ } else if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) {
18568+ wgpu_texview_desc.aspect = WGPUTextureAspect_DepthOnly;
18569+ } else {
18570+ wgpu_texview_desc.aspect = WGPUTextureAspect_All;
18571+ }
18572+ view->wgpu.view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_texview_desc);
18573+ if (0 == view->wgpu.view) {
18574+ _SG_ERROR(WGPU_CREATE_TEXTURE_VIEW_FAILED);
18575+ return SG_RESOURCESTATE_FAILED;
18576+ }
18577+ }
18578+ return SG_RESOURCESTATE_VALID;
18579+}
18580+
18581+_SOKOL_PRIVATE void _sg_wgpu_discard_view(_sg_view_t* view) {
18582+ SOKOL_ASSERT(view);
18583+ _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_VIEW, &view->slot);
18584+ if (view->wgpu.view) {
18585+ wgpuTextureViewRelease(view->wgpu.view);
18586+ view->wgpu.view = 0;
18587+ }
18588+}
18589+
18590+_SOKOL_PRIVATE void _sg_wgpu_init_color_att(WGPURenderPassColorAttachment* wgpu_att, const sg_color_attachment_action* action, WGPUTextureView color_view, WGPUTextureView resolve_view) {
18591+ wgpu_att->depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
18592+ wgpu_att->view = color_view;
18593+ wgpu_att->resolveTarget = resolve_view;
18594+ wgpu_att->loadOp = _sg_wgpu_load_op(color_view, action->load_action);
18595+ wgpu_att->storeOp = _sg_wgpu_store_op(color_view, action->store_action);
18596+ wgpu_att->clearValue.r = action->clear_value.r;
18597+ wgpu_att->clearValue.g = action->clear_value.g;
18598+ wgpu_att->clearValue.b = action->clear_value.b;
18599+ wgpu_att->clearValue.a = action->clear_value.a;
18600+}
18601+
18602+_SOKOL_PRIVATE void _sg_wgpu_init_ds_att(WGPURenderPassDepthStencilAttachment* wgpu_att, const sg_pass_action* action, sg_pixel_format fmt, WGPUTextureView view) {
18603+ wgpu_att->view = view;
18604+ wgpu_att->depthLoadOp = _sg_wgpu_load_op(view, action->depth.load_action);
18605+ wgpu_att->depthStoreOp = _sg_wgpu_store_op(view, action->depth.store_action);
18606+ wgpu_att->depthClearValue = action->depth.clear_value;
18607+ wgpu_att->depthReadOnly = false;
18608+ if (_sg_is_depth_stencil_format(fmt)) {
18609+ wgpu_att->stencilLoadOp = _sg_wgpu_load_op(view, action->stencil.load_action);
18610+ wgpu_att->stencilStoreOp = _sg_wgpu_store_op(view, action->stencil.store_action);
18611+ } else {
18612+ wgpu_att->stencilLoadOp = WGPULoadOp_Undefined;
18613+ wgpu_att->stencilStoreOp = WGPUStoreOp_Undefined;
18614+ }
18615+ wgpu_att->stencilClearValue = action->stencil.clear_value;
18616+ wgpu_att->stencilReadOnly = false;
18617+}
18618+
18619+_SOKOL_PRIVATE void _sg_wgpu_begin_compute_pass(const sg_pass* pass) {
18620+ _SG_STRUCT(WGPUComputePassDescriptor, wgpu_pass_desc);
18621+ wgpu_pass_desc.label = _sg_wgpu_stringview(pass->label);
18622+ _sg.wgpu.cpass_enc = wgpuCommandEncoderBeginComputePass(_sg.wgpu.cmd_enc, &wgpu_pass_desc);
18623+ SOKOL_ASSERT(_sg.wgpu.cpass_enc);
18624+ // clear initial bindings
18625+ wgpuComputePassEncoderSetBindGroup(_sg.wgpu.cpass_enc, _SG_WGPU_UB_BINDGROUP_INDEX, 0, 0, 0);
18626+ wgpuComputePassEncoderSetBindGroup(_sg.wgpu.cpass_enc, _SG_WGPU_VIEW_SMP_BINDGROUP_INDEX, 0, 0, 0);
18627+ _sg_stats_inc(wgpu.bindings.num_set_bindgroup);
18628+}
18629+
18630+_SOKOL_PRIVATE void _sg_wgpu_begin_render_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) {
18631+ const sg_swapchain* swapchain = &pass->swapchain;
18632+ const sg_pass_action* action = &pass->action;
18633+
18634+ _SG_STRUCT(WGPURenderPassDescriptor, wgpu_pass_desc);
18635+ _SG_STRUCT(WGPURenderPassColorAttachment, wgpu_color_att[SG_MAX_COLOR_ATTACHMENTS]);
18636+ _SG_STRUCT(WGPURenderPassDepthStencilAttachment, wgpu_ds_att);
18637+ wgpu_pass_desc.label = _sg_wgpu_stringview(pass->label);
18638+ if (!atts->empty) {
18639+ SOKOL_ASSERT(atts->num_color_views <= SG_MAX_COLOR_ATTACHMENTS);
18640+ for (int i = 0; i < atts->num_color_views; i++) {
18641+ SOKOL_ASSERT(atts->color_views[i]);
18642+ WGPUTextureView wgpu_color_view = atts->color_views[i]->wgpu.view;
18643+ WGPUTextureView wgpu_resolve_view = 0;
18644+ if (atts->resolve_views[i]) {
18645+ wgpu_resolve_view = atts->resolve_views[i]->wgpu.view;
18646+ }
18647+ _sg_wgpu_init_color_att(&wgpu_color_att[i], &action->colors[i], wgpu_color_view, wgpu_resolve_view);
18648+ }
18649+ wgpu_pass_desc.colorAttachmentCount = (size_t)atts->num_color_views;
18650+ wgpu_pass_desc.colorAttachments = &wgpu_color_att[0];
18651+ if (atts->ds_view) {
18652+ const _sg_image_t* img = _sg_image_ref_ptr(&atts->ds_view->cmn.img.ref);
18653+ WGPUTextureView wgpu_ds_view = atts->ds_view->wgpu.view;
18654+ SOKOL_ASSERT(wgpu_ds_view);
18655+ _sg_wgpu_init_ds_att(&wgpu_ds_att, action, img->cmn.pixel_format, wgpu_ds_view);
18656+ wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att;
18657+ }
18658+ } else {
18659+ WGPUTextureView wgpu_color_view = (WGPUTextureView) swapchain->wgpu.render_view;
18660+ WGPUTextureView wgpu_resolve_view = (WGPUTextureView) swapchain->wgpu.resolve_view;
18661+ WGPUTextureView wgpu_depth_stencil_view = (WGPUTextureView) swapchain->wgpu.depth_stencil_view;
18662+ _sg_wgpu_init_color_att(&wgpu_color_att[0], &action->colors[0], wgpu_color_view, wgpu_resolve_view);
18663+ wgpu_pass_desc.colorAttachmentCount = 1;
18664+ wgpu_pass_desc.colorAttachments = &wgpu_color_att[0];
18665+ if (wgpu_depth_stencil_view) {
18666+ SOKOL_ASSERT(swapchain->depth_format > SG_PIXELFORMAT_NONE);
18667+ _sg_wgpu_init_ds_att(&wgpu_ds_att, action, swapchain->depth_format, wgpu_depth_stencil_view);
18668+ wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att;
18669+ }
18670+ }
18671+ _sg.wgpu.rpass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.cmd_enc, &wgpu_pass_desc);
18672+ SOKOL_ASSERT(_sg.wgpu.rpass_enc);
18673+
18674+ wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.rpass_enc, _SG_WGPU_UB_BINDGROUP_INDEX, 0, 0, 0);
18675+ wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.rpass_enc, _SG_WGPU_VIEW_SMP_BINDGROUP_INDEX, 0, 0, 0);
18676+ _sg_stats_inc(wgpu.bindings.num_set_bindgroup);
18677+}
18678+
18679+_SOKOL_PRIVATE void _sg_wgpu_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) {
18680+ SOKOL_ASSERT(pass && atts);
18681+ SOKOL_ASSERT(_sg.wgpu.dev);
18682+ SOKOL_ASSERT(0 == _sg.wgpu.rpass_enc);
18683+ SOKOL_ASSERT(0 == _sg.wgpu.cpass_enc);
18684+
18685+ // first pass in the frame? create command encoder
18686+ if (0 == _sg.wgpu.cmd_enc) {
18687+ _SG_STRUCT(WGPUCommandEncoderDescriptor, cmd_enc_desc);
18688+ _sg.wgpu.cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc);
18689+ SOKOL_ASSERT(_sg.wgpu.cmd_enc);
18690+ }
18691+
18692+ _sg_wgpu_bindings_cache_clear();
18693+ if (pass->compute) {
18694+ _sg_wgpu_begin_compute_pass(pass);
18695+ } else {
18696+ _sg_wgpu_begin_render_pass(pass, atts);
18697+ }
18698+}
18699+
18700+_SOKOL_PRIVATE void _sg_wgpu_end_pass(const _sg_attachments_ptrs_t* atts) {
18701+ _SOKOL_UNUSED(atts);
18702+ if (_sg.wgpu.rpass_enc) {
18703+ wgpuRenderPassEncoderEnd(_sg.wgpu.rpass_enc);
18704+ wgpuRenderPassEncoderRelease(_sg.wgpu.rpass_enc);
18705+ _sg.wgpu.rpass_enc = 0;
18706+ }
18707+ if (_sg.wgpu.cpass_enc) {
18708+ wgpuComputePassEncoderEnd(_sg.wgpu.cpass_enc);
18709+ wgpuComputePassEncoderRelease(_sg.wgpu.cpass_enc);
18710+ _sg.wgpu.cpass_enc = 0;
18711+ }
18712+}
18713+
18714+_SOKOL_PRIVATE void _sg_wgpu_commit(void) {
18715+ if (!_sg.wgpu.cmd_enc) {
18716+ // no valid pass in this frame
18717+ return;
18718+ }
18719+ _sg_wgpu_uniform_system_on_commit();
18720+ _SG_STRUCT(WGPUCommandBufferDescriptor, cmd_buf_desc);
18721+ WGPUCommandBuffer wgpu_cmd_buf = wgpuCommandEncoderFinish(_sg.wgpu.cmd_enc, &cmd_buf_desc);
18722+ SOKOL_ASSERT(wgpu_cmd_buf);
18723+ wgpuCommandEncoderRelease(_sg.wgpu.cmd_enc);
18724+ _sg.wgpu.cmd_enc = 0;
18725+ wgpuQueueSubmit(_sg.wgpu.queue, 1, &wgpu_cmd_buf);
18726+ wgpuCommandBufferRelease(wgpu_cmd_buf);
18727+}
18728+
18729+_SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) {
18730+ SOKOL_ASSERT(_sg.wgpu.rpass_enc);
18731+ float xf = (float) x;
18732+ float yf = (float) (origin_top_left ? y : (_sg.cur_pass.dim.height - (y + h)));
18733+ float wf = (float) w;
18734+ float hf = (float) h;
18735+ wgpuRenderPassEncoderSetViewport(_sg.wgpu.rpass_enc, xf, yf, wf, hf, 0.0f, 1.0f);
18736+}
18737+
18738+_SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) {
18739+ SOKOL_ASSERT(_sg.wgpu.rpass_enc);
18740+ const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.dim.width, _sg.cur_pass.dim.height);
18741+ uint32_t sx = (uint32_t) clip.x;
18742+ uint32_t sy = (uint32_t) (origin_top_left ? clip.y : (_sg.cur_pass.dim.height - (clip.y + clip.h)));
18743+ uint32_t sw = (uint32_t) clip.w;
18744+ uint32_t sh = (uint32_t) clip.h;
18745+ wgpuRenderPassEncoderSetScissorRect(_sg.wgpu.rpass_enc, sx, sy, sw, sh);
18746+}
18747+
18748+_SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) {
18749+ SOKOL_ASSERT(pip);
18750+ _sg_wgpu_uniform_system_on_apply_pipeline();
18751+ if (pip->cmn.is_compute) {
18752+ SOKOL_ASSERT(_sg.cur_pass.is_compute);
18753+ SOKOL_ASSERT(pip->wgpu.cpip);
18754+ SOKOL_ASSERT(_sg.wgpu.cpass_enc);
18755+ wgpuComputePassEncoderSetPipeline(_sg.wgpu.cpass_enc, pip->wgpu.cpip);
18756+ } else {
18757+ SOKOL_ASSERT(!_sg.cur_pass.is_compute);
18758+ SOKOL_ASSERT(pip->wgpu.rpip);
18759+ SOKOL_ASSERT(_sg.wgpu.rpass_enc);
18760+ wgpuRenderPassEncoderSetPipeline(_sg.wgpu.rpass_enc, pip->wgpu.rpip);
18761+ wgpuRenderPassEncoderSetBlendConstant(_sg.wgpu.rpass_enc, &pip->wgpu.blend_color);
18762+ wgpuRenderPassEncoderSetStencilReference(_sg.wgpu.rpass_enc, pip->cmn.stencil.ref);
18763+ }
18764+}
18765+
18766+_SOKOL_PRIVATE bool _sg_wgpu_apply_bindings(_sg_bindings_ptrs_t* bnd) {
18767+ SOKOL_ASSERT(bnd);
18768+ bool retval = true;
18769+ if (!_sg.cur_pass.is_compute) {
18770+ retval &= _sg_wgpu_apply_index_buffer(bnd);
18771+ retval &= _sg_wgpu_apply_vertex_buffers(bnd);
18772+ }
18773+ retval &= _sg_wgpu_apply_bindings_bindgroup(bnd);
18774+ return retval;
18775+}
18776+
18777+_SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(int ub_slot, const sg_range* data) {
18778+ const uint32_t alignment = _sg.wgpu.limits.minUniformBufferOffsetAlignment;
18779+ SOKOL_ASSERT(_sg.wgpu.uniform.staging);
18780+ SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS));
18781+ SOKOL_ASSERT((_sg.wgpu.uniform.offset + data->size) <= _sg.wgpu.uniform.num_bytes);
18782+ SOKOL_ASSERT((_sg.wgpu.uniform.offset & (alignment - 1)) == 0);
18783+ SOKOL_ASSERT(data->size <= _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE);
18784+
18785+ _sg_stats_inc(wgpu.uniforms.num_set_bindgroup);
18786+ memcpy(_sg.wgpu.uniform.staging + _sg.wgpu.uniform.offset, data->ptr, data->size);
18787+ _sg.wgpu.uniform.bind_offsets[ub_slot] = _sg.wgpu.uniform.offset;
18788+ _sg.wgpu.uniform.offset = _sg_roundup_u32(_sg.wgpu.uniform.offset + (uint32_t)data->size, alignment);
18789+ _sg.wgpu.uniform.dirty = true;
18790+}
18791+
18792+_SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) {
18793+ SOKOL_ASSERT(_sg.wgpu.rpass_enc);
18794+ if (_sg.wgpu.uniform.dirty) {
18795+ _sg_wgpu_uniform_system_set_bindgroup();
18796+ }
18797+ if (_sg.use_indexed_draw) {
18798+ wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.rpass_enc,
18799+ (uint32_t)num_elements,
18800+ (uint32_t)num_instances,
18801+ (uint32_t)base_element,
18802+ base_vertex,
18803+ (uint32_t)base_instance);
18804+ } else {
18805+ wgpuRenderPassEncoderDraw(_sg.wgpu.rpass_enc,
18806+ (uint32_t)num_elements,
18807+ (uint32_t)num_instances,
18808+ (uint32_t)base_element,
18809+ (uint32_t)base_instance);
18810+ }
18811+}
18812+
18813+_SOKOL_PRIVATE void _sg_wgpu_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) {
18814+ SOKOL_ASSERT(_sg.wgpu.cpass_enc);
18815+ if (_sg.wgpu.uniform.dirty) {
18816+ _sg_wgpu_uniform_system_set_bindgroup();
18817+ }
18818+ wgpuComputePassEncoderDispatchWorkgroups(_sg.wgpu.cpass_enc,
18819+ (uint32_t)num_groups_x,
18820+ (uint32_t)num_groups_y,
18821+ (uint32_t)num_groups_z);
18822+}
18823+
18824+_SOKOL_PRIVATE void _sg_wgpu_update_buffer(_sg_buffer_t* buf, const sg_range* data) {
18825+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
18826+ _sg_wgpu_copy_buffer_data(buf, 0, data);
18827+}
18828+
18829+_SOKOL_PRIVATE void _sg_wgpu_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) {
18830+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
18831+ _SOKOL_UNUSED(new_frame);
18832+ _sg_wgpu_copy_buffer_data(buf, (uint64_t)buf->cmn.append_pos, data);
18833+}
18834+
18835+_SOKOL_PRIVATE void _sg_wgpu_update_image(_sg_image_t* img, const sg_image_data* data) {
18836+ SOKOL_ASSERT(img && data);
18837+ _sg_wgpu_copy_image_data(img, data);
18838+}
18839+
18840+// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████
18841+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██
18842+// ██ ██ ██ ██ ██ █████ ███████ ██ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██
18843+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
18844+// ████ ██████ ███████ ██ ██ ██ ██ ██ ████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████
18845+//
18846+// >>vulkan
18847+// >>vk
18848+#elif defined(SOKOL_VULKAN)
18849+
18850+_SOKOL_PRIVATE void _sg_vk_set_object_label(VkObjectType obj_type, uint64_t obj_handle, const char* label) {
18851+ #if defined(SOKOL_DEBUG)
18852+ SOKOL_ASSERT(_sg.vk.dev);
18853+ SOKOL_ASSERT(_sg.vk.ext.set_debug_utils_object_name_ext);
18854+ SOKOL_ASSERT(obj_handle != 0);
18855+ if (label) {
18856+ _SG_STRUCT(VkDebugUtilsObjectNameInfoEXT, name_info);
18857+ name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
18858+ name_info.objectType = obj_type;
18859+ name_info.objectHandle = obj_handle,
18860+ name_info.pObjectName = label;
18861+ VkResult res = _sg.vk.ext.set_debug_utils_object_name_ext(_sg.vk.dev, &name_info);
18862+ SOKOL_ASSERT(res == VK_SUCCESS);
18863+ }
18864+ #else
18865+ _SOKOL_UNUSED(obj_type);
18866+ _SOKOL_UNUSED(obj_handle);
18867+ _SOKOL_UNUSED(label);
18868+ #endif
18869+}
18870+
18871+_SOKOL_PRIVATE bool _sg_vk_is_read_access(_sg_vk_access_t access) {
18872+ _sg_vk_access_t read_bits =
18873+ _SG_VK_ACCESS_VERTEXBUFFER |
18874+ _SG_VK_ACCESS_INDEXBUFFER |
18875+ _SG_VK_ACCESS_STORAGEBUFFER_RO |
18876+ _SG_VK_ACCESS_TEXTURE |
18877+ _SG_VK_ACCESS_PRESENT;
18878+ return 0 == (access & ~read_bits);
18879+}
18880+
18881+_SOKOL_PRIVATE VkPipelineStageFlags2 _sg_vk_stage_mask(_sg_vk_access_t access, bool is_dst_access) {
18882+ access &= ~_SG_VK_ACCESS_DISCARD;
18883+ if (is_dst_access) {
18884+ SOKOL_ASSERT(access != _SG_VK_ACCESS_NONE);
18885+ }
18886+ VkPipelineStageFlags2 f = 0;
18887+ if (access == _SG_VK_ACCESS_NONE) {
18888+ return VK_PIPELINE_STAGE_2_NONE;
18889+ }
18890+ if (access & _SG_VK_ACCESS_PRESENT) {
18891+ return VK_PIPELINE_STAGE_2_NONE;
18892+ }
18893+ if (access & _SG_VK_ACCESS_STAGING) {
18894+ f |= VK_PIPELINE_STAGE_2_COPY_BIT;
18895+ }
18896+ if (access & _SG_VK_ACCESS_VERTEXBUFFER) {
18897+ f |= VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT;
18898+ }
18899+ if (access & _SG_VK_ACCESS_INDEXBUFFER) {
18900+ f |= VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT;
18901+ }
18902+ if (access & (_SG_VK_ACCESS_STORAGEBUFFER_RO|_SG_VK_ACCESS_TEXTURE)) {
18903+ f |= VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT |
18904+ VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT |
18905+ VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
18906+ }
18907+ if (access & _SG_VK_ACCESS_STORAGEBUFFER_RW) {
18908+ f |= VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
18909+ }
18910+ if (access & _SG_VK_ACCESS_STORAGEIMAGE) {
18911+ f |= VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
18912+ }
18913+ if (access & _SG_VK_ACCESS_COLOR_ATTACHMENT) {
18914+ f |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
18915+ }
18916+ if (access & _SG_VK_ACCESS_RESOLVE_ATTACHMENT) {
18917+ f |= VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
18918+ }
18919+ if (access & (_SG_VK_ACCESS_DEPTH_ATTACHMENT|_SG_VK_ACCESS_STENCIL_ATTACHMENT)) {
18920+ f |= VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT|VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT;
18921+ }
18922+ SOKOL_ASSERT(f != 0);
18923+ return f;
18924+}
18925+
18926+// return pipeline stages on 'before' side of a barrier
18927+_SOKOL_PRIVATE VkPipelineStageFlags2 _sg_vk_src_stage_mask(_sg_vk_access_t access) {
18928+ return _sg_vk_stage_mask(access, false);
18929+}
18930+
18931+// return pipeline stage on 'after side' of a barrier
18932+_SOKOL_PRIVATE VkPipelineStageFlags2 _sg_vk_dst_stage_mask(_sg_vk_access_t access) {
18933+ return _sg_vk_stage_mask(access, true);
18934+}
18935+
18936+_SOKOL_PRIVATE VkAccessFlags2 _sg_vk_access_mask(_sg_vk_access_t access, bool is_dst_access) {
18937+ access &= ~_SG_VK_ACCESS_DISCARD;
18938+ if (access == _SG_VK_ACCESS_NONE) {
18939+ return VK_ACCESS_2_NONE;
18940+ }
18941+ if (access & _SG_VK_ACCESS_PRESENT) {
18942+ return VK_ACCESS_2_NONE;
18943+ }
18944+ VkAccessFlags2 f = VK_ACCESS_2_NONE;
18945+ if (is_dst_access) {
18946+ // NOTE: read bits don't make sense for src-mask
18947+ if (access & _SG_VK_ACCESS_VERTEXBUFFER) {
18948+ f |= VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT;
18949+ }
18950+ if (access & _SG_VK_ACCESS_INDEXBUFFER) {
18951+ f |= VK_ACCESS_2_INDEX_READ_BIT;
18952+ }
18953+ if (access & _SG_VK_ACCESS_STORAGEBUFFER_RO) {
18954+ f |= VK_ACCESS_2_SHADER_STORAGE_READ_BIT;
18955+ }
18956+ if (access & _SG_VK_ACCESS_TEXTURE) {
18957+ f |= VK_ACCESS_2_SHADER_SAMPLED_READ_BIT;
18958+ }
18959+ }
18960+ if (access & _SG_VK_ACCESS_STAGING) {
18961+ f |= VK_ACCESS_2_TRANSFER_WRITE_BIT;
18962+ }
18963+ if (access & _SG_VK_ACCESS_STORAGEBUFFER_RW) {
18964+ f |= VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT;
18965+ }
18966+ if (access & _SG_VK_ACCESS_STORAGEIMAGE) {
18967+ f |= VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT;
18968+ }
18969+ if (access & _SG_VK_ACCESS_COLOR_ATTACHMENT) {
18970+ f |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
18971+ }
18972+ if (access & _SG_VK_ACCESS_RESOLVE_ATTACHMENT) {
18973+ f |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
18974+ }
18975+ if (access & (_SG_VK_ACCESS_DEPTH_ATTACHMENT | _SG_VK_ACCESS_STENCIL_ATTACHMENT)) {
18976+ f |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
18977+ if (is_dst_access) {
18978+ f |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
18979+ }
18980+ }
18981+ return f;
18982+}
18983+
18984+_SOKOL_PRIVATE VkAccessFlags2 _sg_vk_src_access_mask(_sg_vk_access_t access) {
18985+ return _sg_vk_access_mask(access, false);
18986+}
18987+
18988+_SOKOL_PRIVATE VkAccessFlags2 _sg_vk_dst_access_mask(_sg_vk_access_t access) {
18989+ return _sg_vk_access_mask(access, true);
18990+}
18991+
18992+_SOKOL_PRIVATE VkImageLayout _sg_vk_image_layout(_sg_vk_access_t access) {
18993+ // NOTE: "image layout transitions with VK_IMAGE_LAYOUT_UNDEFINED allow
18994+ // the implementation to discard the image subresource range"
18995+ if (access & _SG_VK_ACCESS_DISCARD) {
18996+ return VK_IMAGE_LAYOUT_UNDEFINED;
18997+ }
18998+ switch (access) {
18999+ case _SG_VK_ACCESS_NONE:
19000+ return VK_IMAGE_LAYOUT_UNDEFINED;
19001+ case _SG_VK_ACCESS_STAGING:
19002+ return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
19003+ case _SG_VK_ACCESS_TEXTURE:
19004+ return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
19005+ case _SG_VK_ACCESS_STORAGEIMAGE:
19006+ return VK_IMAGE_LAYOUT_GENERAL;
19007+ case _SG_VK_ACCESS_COLOR_ATTACHMENT:
19008+ case _SG_VK_ACCESS_RESOLVE_ATTACHMENT:
19009+ case _SG_VK_ACCESS_DEPTH_ATTACHMENT:
19010+ case _SG_VK_ACCESS_DEPTH_ATTACHMENT|_SG_VK_ACCESS_STENCIL_ATTACHMENT:
19011+ return VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
19012+ case _SG_VK_ACCESS_PRESENT:
19013+ return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
19014+ default:
19015+ SOKOL_UNREACHABLE;
19016+ return VK_IMAGE_LAYOUT_UNDEFINED;
19017+ }
19018+}
19019+
19020+_SOKOL_PRIVATE void _sg_vk_swapchain_beginpass_barrier(VkCommandBuffer cmd_buf, VkImage vkimg, _sg_vk_access_t pass_access) {
19021+ SOKOL_ASSERT(cmd_buf);
19022+ _SG_STRUCT(VkImageMemoryBarrier2, barrier);
19023+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
19024+ barrier.srcStageMask = _sg_vk_src_stage_mask(pass_access);
19025+ barrier.srcAccessMask = _sg_vk_src_access_mask(pass_access);
19026+ barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
19027+ barrier.dstStageMask = _sg_vk_dst_stage_mask(pass_access);
19028+ barrier.dstAccessMask = _sg_vk_dst_access_mask(pass_access);
19029+ barrier.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
19030+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
19031+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
19032+ barrier.image = vkimg;
19033+ if (0 != (pass_access & (_SG_VK_ACCESS_DEPTH_ATTACHMENT|_SG_VK_ACCESS_STENCIL_ATTACHMENT))) {
19034+ barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
19035+ if (0 != (pass_access & _SG_VK_ACCESS_STENCIL_ATTACHMENT)) {
19036+ barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
19037+ }
19038+ } else {
19039+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
19040+ }
19041+ barrier.subresourceRange.levelCount = 1;
19042+ barrier.subresourceRange.layerCount = 1;
19043+ _SG_STRUCT(VkDependencyInfo, dep_info);
19044+ dep_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
19045+ dep_info.imageMemoryBarrierCount = 1;
19046+ dep_info.pImageMemoryBarriers = &barrier;
19047+ vkCmdPipelineBarrier2(cmd_buf, &dep_info);
19048+ _sg_stats_inc(vk.num_cmd_pipeline_barrier);
19049+}
19050+
19051+_SOKOL_PRIVATE void _sg_vk_swapchain_endpass_barrier(VkCommandBuffer cmd_buf, VkImage vkimg, _sg_vk_access_t pass_access, bool present) {
19052+ SOKOL_ASSERT(cmd_buf);
19053+ _SG_STRUCT(VkImageMemoryBarrier2, barrier);
19054+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
19055+ barrier.srcStageMask = _sg_vk_src_stage_mask(pass_access);
19056+ barrier.srcAccessMask = _sg_vk_src_access_mask(pass_access);
19057+ barrier.oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
19058+ barrier.dstStageMask = VK_PIPELINE_STAGE_2_NONE;
19059+ barrier.dstAccessMask = VK_ACCESS_2_NONE;
19060+ if (present) {
19061+ barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
19062+ } else {
19063+ barrier.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
19064+ }
19065+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
19066+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
19067+ barrier.image = vkimg;
19068+ if (0 != (pass_access & (_SG_VK_ACCESS_DEPTH_ATTACHMENT|_SG_VK_ACCESS_STENCIL_ATTACHMENT))) {
19069+ barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
19070+ if (0 != (pass_access & _SG_VK_ACCESS_STENCIL_ATTACHMENT)) {
19071+ barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
19072+ }
19073+ } else {
19074+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
19075+ }
19076+ barrier.subresourceRange.levelCount = 1;
19077+ barrier.subresourceRange.layerCount = 1;
19078+ _SG_STRUCT(VkDependencyInfo, dep_info);
19079+ dep_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
19080+ dep_info.imageMemoryBarrierCount = 1;
19081+ dep_info.pImageMemoryBarriers = &barrier;
19082+ vkCmdPipelineBarrier2(cmd_buf, &dep_info);
19083+ _sg_stats_inc(vk.num_cmd_pipeline_barrier);
19084+}
19085+
19086+_SOKOL_PRIVATE void _sg_vk_image_barrier(VkCommandBuffer cmd_buf, _sg_image_t* img, _sg_vk_access_t new_access) {
19087+ SOKOL_ASSERT(cmd_buf && img && img->vk.img);
19088+ if (_sg_vk_is_read_access(img->vk.cur_access) && _sg_vk_is_read_access(new_access)) {
19089+ return;
19090+ }
19091+ _SG_STRUCT(VkImageMemoryBarrier2, barrier);
19092+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
19093+ barrier.srcStageMask = _sg_vk_src_stage_mask(img->vk.cur_access);
19094+ barrier.srcAccessMask = _sg_vk_src_access_mask(img->vk.cur_access);
19095+ barrier.oldLayout = _sg_vk_image_layout(img->vk.cur_access);
19096+ barrier.dstStageMask = _sg_vk_dst_stage_mask(new_access);
19097+ barrier.dstAccessMask = _sg_vk_dst_access_mask(new_access);
19098+ barrier.newLayout = _sg_vk_image_layout(new_access);
19099+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
19100+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
19101+ barrier.image = img->vk.img;
19102+ if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) {
19103+ barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
19104+ if (_sg_is_depth_stencil_format(img->cmn.pixel_format)) {
19105+ barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
19106+ }
19107+ } else {
19108+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
19109+ }
19110+ barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
19111+ barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
19112+ _SG_STRUCT(VkDependencyInfo, dep_info);
19113+ dep_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
19114+ dep_info.imageMemoryBarrierCount = 1;
19115+ dep_info.pImageMemoryBarriers = &barrier;
19116+ vkCmdPipelineBarrier2(cmd_buf, &dep_info);
19117+ _sg_stats_inc(vk.num_cmd_pipeline_barrier);
19118+ img->vk.cur_access = new_access;
19119+}
19120+
19121+_SOKOL_PRIVATE void _sg_vk_buffer_barrier(VkCommandBuffer cmd_buf, _sg_buffer_t* buf, _sg_vk_access_t new_access) {
19122+ SOKOL_ASSERT(cmd_buf && buf && buf->vk.buf);
19123+ if (_sg_vk_is_read_access(buf->vk.cur_access) && _sg_vk_is_read_access(new_access)) {
19124+ return;
19125+ }
19126+ _SG_STRUCT(VkBufferMemoryBarrier2, barrier);
19127+ barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2;
19128+ barrier.srcStageMask = _sg_vk_src_stage_mask(buf->vk.cur_access);
19129+ barrier.srcAccessMask = _sg_vk_src_access_mask(buf->vk.cur_access);
19130+ barrier.dstStageMask = _sg_vk_dst_stage_mask(new_access);
19131+ barrier.dstAccessMask = _sg_vk_dst_access_mask(new_access);
19132+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
19133+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
19134+ barrier.buffer = buf->vk.buf;
19135+ barrier.offset = 0;
19136+ barrier.size = VK_WHOLE_SIZE;
19137+ _SG_STRUCT(VkDependencyInfo, dep_info);
19138+ dep_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
19139+ dep_info.bufferMemoryBarrierCount = 1;
19140+ dep_info.pBufferMemoryBarriers = &barrier;
19141+ vkCmdPipelineBarrier2(cmd_buf, &dep_info);
19142+ _sg_stats_inc(vk.num_cmd_pipeline_barrier);
19143+ buf->vk.cur_access = new_access;
19144+}
19145+
19146+_SOKOL_PRIVATE void _sg_vk_barrier_on_begin_pass(VkCommandBuffer cmd_buf, const sg_pass* pass, const _sg_attachments_ptrs_t* atts, bool is_compute_pass) {
19147+ SOKOL_ASSERT(cmd_buf);
19148+ if (is_compute_pass) {
19149+ SOKOL_ASSERT(0 == _sg.vk.track.buffers.cur_slot);
19150+ SOKOL_ASSERT(0 == _sg.vk.track.images.cur_slot);
19151+ } else {
19152+ const bool is_swapchain_pass = atts->empty;
19153+ if (is_swapchain_pass) {
19154+ const sg_vulkan_swapchain* vk_swapchain = &pass->swapchain.vulkan;
19155+ SOKOL_ASSERT(vk_swapchain->render_image);
19156+ VkImage vk_color_image = (VkImage)vk_swapchain->render_image;
19157+ _sg_vk_swapchain_beginpass_barrier(cmd_buf, vk_color_image, _SG_VK_ACCESS_COLOR_ATTACHMENT);
19158+ if (_sg.cur_pass.swapchain.sample_count > 1) {
19159+ VkImage vk_resolve_image = (VkImage)vk_swapchain->resolve_image;
19160+ SOKOL_ASSERT(vk_resolve_image);
19161+ _sg_vk_swapchain_beginpass_barrier(cmd_buf, vk_resolve_image, _SG_VK_ACCESS_RESOLVE_ATTACHMENT);
19162+ }
19163+ if (vk_swapchain->depth_stencil_image) {
19164+ VkImage vk_ds_image = (VkImage)vk_swapchain->depth_stencil_image;
19165+ const bool has_stencil = _sg_is_depth_stencil_format(_sg.cur_pass.swapchain.depth_fmt);
19166+ _sg_vk_access_t access = _SG_VK_ACCESS_DEPTH_ATTACHMENT;
19167+ if (has_stencil) {
19168+ access |= _SG_VK_ACCESS_STENCIL_ATTACHMENT;
19169+ }
19170+ _sg_vk_swapchain_beginpass_barrier(cmd_buf, vk_ds_image, access);
19171+ }
19172+ } else {
19173+ SOKOL_ASSERT(atts->num_color_views <= SG_MAX_COLOR_ATTACHMENTS);
19174+ for (int i = 0; i < atts->num_color_views; i++) {
19175+ SOKOL_ASSERT(atts->color_views[i]);
19176+ _sg_image_t* color_image = _sg_image_ref_ptr(&atts->color_views[i]->cmn.img.ref);
19177+ if (pass->action.colors[i].load_action != SG_LOADACTION_LOAD) {
19178+ // don't need to preserve image content for clear and dontcare
19179+ color_image->vk.cur_access |= _SG_VK_ACCESS_DISCARD;
19180+ }
19181+ _sg_vk_image_barrier(cmd_buf, color_image, _SG_VK_ACCESS_COLOR_ATTACHMENT);
19182+ if (atts->resolve_views[i]) {
19183+ _sg_image_t* resolve_image = _sg_image_ref_ptr(&atts->resolve_views[i]->cmn.img.ref);
19184+ // never need to preserve content for resolve image
19185+ resolve_image->vk.cur_access |= _SG_VK_ACCESS_DISCARD;
19186+ _sg_vk_image_barrier(cmd_buf, resolve_image, _SG_VK_ACCESS_RESOLVE_ATTACHMENT);
19187+ }
19188+ }
19189+ if (atts->ds_view) {
19190+ _sg_image_t* ds_image = _sg_image_ref_ptr(&atts->ds_view->cmn.img.ref);
19191+ const bool has_stencil = _sg_is_depth_stencil_format(ds_image->cmn.pixel_format);
19192+ if ((pass->action.depth.load_action != SG_LOADACTION_LOAD) &&
19193+ (pass->action.stencil.load_action != SG_LOADACTION_LOAD))
19194+ {
19195+ // don't need to preserve image content for clear and dontcare
19196+ ds_image->vk.cur_access |= _SG_VK_ACCESS_DISCARD;
19197+ }
19198+ _sg_vk_access_t dst_access = _SG_VK_ACCESS_DEPTH_ATTACHMENT;
19199+ if (has_stencil) {
19200+ dst_access |= _SG_VK_ACCESS_STENCIL_ATTACHMENT;
19201+ }
19202+ _sg_vk_image_barrier(cmd_buf, ds_image, dst_access);
19203+ }
19204+ }
19205+ }
19206+}
19207+
19208+_SOKOL_PRIVATE void _sg_vk_barrier_on_apply_bindings(VkCommandBuffer cmd_buf, const _sg_bindings_ptrs_t* bnd, bool is_compute_pass) {
19209+ SOKOL_ASSERT(bnd);
19210+ if (is_compute_pass) {
19211+ SOKOL_ASSERT(bnd->pip);
19212+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
19213+ const _sg_view_t* view = bnd->views[i];
19214+ if (0 == view) {
19215+ continue;
19216+ } else if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) {
19217+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader);
19218+ _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref);
19219+ _sg_vk_access_t new_access = shd->cmn.views[i].sbuf_readonly
19220+ ? _SG_VK_ACCESS_STORAGEBUFFER_RO
19221+ : _SG_VK_ACCESS_STORAGEBUFFER_RW;
19222+ _sg_vk_buffer_barrier(cmd_buf, buf, new_access);
19223+ _sg_track_add(&_sg.vk.track.buffers, buf->slot.id);
19224+ } else if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) {
19225+ _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
19226+ _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_STORAGEIMAGE);
19227+ _sg_track_add(&_sg.vk.track.images, img->slot.id);
19228+ } else if (view->cmn.type == SG_VIEWTYPE_TEXTURE) {
19229+ _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
19230+ _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE);
19231+ _sg_track_add(&_sg.vk.track.images, img->slot.id);
19232+ } else {
19233+ SOKOL_UNREACHABLE;
19234+ }
19235+ }
19236+ } else {
19237+ // no transitions allowed in render passes, but check if resources are in
19238+ // correct access state
19239+ for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) {
19240+ if (bnd->vbs[i]) {
19241+ SOKOL_ASSERT(0 != (bnd->vbs[i]->vk.cur_access & _SG_VK_ACCESS_VERTEXBUFFER));
19242+ }
19243+ }
19244+ if (bnd->ib) {
19245+ SOKOL_ASSERT(0 != (bnd->ib->vk.cur_access & _SG_VK_ACCESS_INDEXBUFFER));
19246+ }
19247+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
19248+ const _sg_view_t* view = bnd->views[i];
19249+ if (0 == view) {
19250+ continue;
19251+ }
19252+ else if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) {
19253+ const _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref);
19254+ _SOKOL_UNUSED(buf);
19255+ SOKOL_ASSERT(0 != (buf->vk.cur_access & _SG_VK_ACCESS_STORAGEBUFFER_RO));
19256+ } else if (view->cmn.type == SG_VIEWTYPE_TEXTURE) {
19257+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
19258+ _SOKOL_UNUSED(img);
19259+ SOKOL_ASSERT(0 != (img->vk.cur_access & _SG_VK_ACCESS_TEXTURE));
19260+ } else {
19261+ SOKOL_UNREACHABLE;
19262+ }
19263+ }
19264+ }
19265+}
19266+
19267+_SOKOL_PRIVATE void _sg_vk_barrier_on_end_pass(VkCommandBuffer cmd_buf, const _sg_attachments_ptrs_t* atts, bool is_compute_pass) {
19268+ SOKOL_ASSERT(cmd_buf);
19269+ if (is_compute_pass) {
19270+ // transition all tracked buffers into vertex+index+sbuf-ro access
19271+ const _sg_vk_access_t new_buf_access = _SG_VK_ACCESS_VERTEXBUFFER|_SG_VK_ACCESS_INDEXBUFFER|_SG_VK_ACCESS_STORAGEBUFFER_RO;
19272+ for (int i = 0; i < _sg.vk.track.buffers.cur_slot; i++) {
19273+ const uint32_t buf_id = _sg.vk.track.buffers.slots[i];
19274+ _sg_buffer_t* buf = _sg_lookup_buffer(buf_id);
19275+ if (buf) {
19276+ _sg_vk_buffer_barrier(cmd_buf, buf, new_buf_access);
19277+ }
19278+ }
19279+ _sg_track_reset(&_sg.vk.track.buffers);
19280+
19281+ // transition all tracked images into texture access
19282+ const _sg_vk_access_t new_img_access = _SG_VK_ACCESS_TEXTURE;
19283+ for (int i = 0; i < _sg.vk.track.images.cur_slot; i++) {
19284+ const uint32_t img_id = _sg.vk.track.images.slots[i];
19285+ _sg_image_t* img = _sg_lookup_image(img_id);
19286+ if (img) {
19287+ _sg_vk_image_barrier(cmd_buf, img, new_img_access);
19288+ }
19289+ }
19290+ _sg_track_reset(&_sg.vk.track.images);
19291+ } else {
19292+ const bool is_swapchain_pass = atts->empty;
19293+ if (is_swapchain_pass) {
19294+ SOKOL_ASSERT(_sg.vk.swapchain.render_image);
19295+ VkImage present_image = _sg.vk.swapchain.resolve_image
19296+ ? (VkImage)_sg.vk.swapchain.resolve_image
19297+ : (VkImage)_sg.vk.swapchain.render_image;
19298+ _sg_vk_swapchain_endpass_barrier(cmd_buf, present_image, _SG_VK_ACCESS_COLOR_ATTACHMENT, true);
19299+ } else {
19300+ for (int i = 0; i < atts->num_color_views; i++) {
19301+ if (_sg.cur_pass.action.colors[i].store_action == SG_STOREACTION_STORE) {
19302+ SOKOL_ASSERT(atts->color_views[i]);
19303+ _sg_image_t* img = _sg_image_ref_ptr(&atts->color_views[i]->cmn.img.ref);
19304+ _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE);
19305+ }
19306+ if (atts->resolve_views[i]) {
19307+ _sg_image_t* img = _sg_image_ref_ptr(&atts->resolve_views[i]->cmn.img.ref);
19308+ _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE);
19309+ }
19310+ }
19311+ if (atts->ds_view) {
19312+ _sg_image_t* img = _sg_image_ref_ptr(&atts->ds_view->cmn.img.ref);
19313+ if (_sg.cur_pass.action.depth.store_action == SG_STOREACTION_STORE) {
19314+ _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE);
19315+ }
19316+ }
19317+ }
19318+ }
19319+}
19320+
19321+_SOKOL_PRIVATE int _sg_vk_mem_find_memory_type_index(uint32_t type_filter, VkMemoryPropertyFlags props) {
19322+ SOKOL_ASSERT(_sg.vk.phys_dev);
19323+ _SG_STRUCT(VkPhysicalDeviceMemoryProperties, mem_props);
19324+ vkGetPhysicalDeviceMemoryProperties(_sg.vk.phys_dev, &mem_props);
19325+ for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) {
19326+ if ((type_filter & (1 << i)) && ((mem_props.memoryTypes[i].propertyFlags & props) == props)) {
19327+ return (int)i;
19328+ }
19329+ }
19330+ return -1;
19331+}
19332+
19333+_SOKOL_PRIVATE VkDeviceMemory _sg_vk_mem_alloc_device_memory(_sg_vk_memtype_t mem_type, const VkMemoryRequirements* mem_reqs) {
19334+ SOKOL_ASSERT(_sg.vk.dev);
19335+ SOKOL_ASSERT(mem_reqs);
19336+
19337+ VkMemoryPropertyFlags mem_prop_flags = 0;
19338+ VkMemoryAllocateFlags mem_alloc_flags = 0;
19339+ switch (mem_type) {
19340+ case _SG_VK_MEMTYPE_GENERIC_BUFFER:
19341+ mem_prop_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
19342+ break;
19343+ case _SG_VK_MEMTYPE_STORAGE_BUFFER:
19344+ mem_prop_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
19345+ mem_alloc_flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
19346+ break;
19347+ case _SG_VK_MEMTYPE_IMAGE:
19348+ mem_prop_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
19349+ break;
19350+ case _SG_VK_MEMTYPE_STAGING_COPY:
19351+ mem_prop_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
19352+ break;
19353+ case _SG_VK_MEMTYPE_STAGING_STREAM:
19354+ mem_prop_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
19355+ break;
19356+ case _SG_VK_MEMTYPE_UNIFORMS:
19357+ mem_prop_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
19358+ mem_alloc_flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
19359+ break;
19360+ case _SG_VK_MEMTYPE_DESCRIPTORS:
19361+ mem_prop_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
19362+ mem_alloc_flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
19363+ break;
19364+ default:
19365+ SOKOL_UNREACHABLE;
19366+ break;
19367+ }
19368+
19369+ int mem_type_index = _sg_vk_mem_find_memory_type_index(mem_reqs->memoryTypeBits, mem_prop_flags);
19370+ if (-1 == mem_type_index) {
19371+ _SG_ERROR(VULKAN_ALLOC_DEVICE_MEMORY_NO_SUITABLE_MEMORY_TYPE);
19372+ return 0;
19373+ }
19374+ _SG_STRUCT(VkMemoryAllocateFlagsInfo, flags_info);
19375+ flags_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO;
19376+ flags_info.flags = mem_alloc_flags;
19377+ _SG_STRUCT(VkMemoryAllocateInfo, alloc_info);
19378+ alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
19379+ alloc_info.pNext = &flags_info;
19380+ alloc_info.allocationSize = mem_reqs->size;
19381+ alloc_info.memoryTypeIndex = (uint32_t) mem_type_index;
19382+ VkDeviceMemory vk_dev_mem = 0;
19383+ VkResult res = vkAllocateMemory(_sg.vk.dev, &alloc_info, 0, &vk_dev_mem);
19384+ _sg_stats_inc(vk.num_allocate_memory);
19385+ _sg_stats_add(vk.size_allocate_memory, (uint32_t)mem_reqs->size);
19386+ if (res != VK_SUCCESS) {
19387+ _SG_ERROR(VULKAN_ALLOCATE_MEMORY_FAILED);
19388+ return 0;
19389+ }
19390+ SOKOL_ASSERT(vk_dev_mem);
19391+ return vk_dev_mem;
19392+}
19393+
19394+_SOKOL_PRIVATE void _sg_vk_mem_free_device_memory(VkDeviceMemory vk_dev_mem) {
19395+ SOKOL_ASSERT(_sg.vk.dev);
19396+ SOKOL_ASSERT(vk_dev_mem);
19397+ vkFreeMemory(_sg.vk.dev, vk_dev_mem, 0);
19398+ _sg_stats_inc(vk.num_free_memory);
19399+}
19400+
19401+_SOKOL_PRIVATE bool _sg_vk_mem_alloc_buffer_device_memory(_sg_buffer_t* buf) {
19402+ SOKOL_ASSERT(_sg.vk.dev);
19403+ SOKOL_ASSERT(buf);
19404+ SOKOL_ASSERT(buf->vk.buf);
19405+ SOKOL_ASSERT(0 == buf->vk.mem);
19406+ _SG_STRUCT(VkMemoryRequirements, mem_reqs);
19407+ vkGetBufferMemoryRequirements(_sg.vk.dev, buf->vk.buf, &mem_reqs);
19408+ _sg_vk_memtype_t mem_type = buf->cmn.usage.storage_buffer
19409+ ? _SG_VK_MEMTYPE_STORAGE_BUFFER
19410+ : _SG_VK_MEMTYPE_GENERIC_BUFFER;
19411+ buf->vk.mem = _sg_vk_mem_alloc_device_memory(mem_type, &mem_reqs);
19412+ if (0 == buf->vk.mem) {
19413+ _SG_ERROR(VULKAN_ALLOC_BUFFER_DEVICE_MEMORY_FAILED);
19414+ return false;
19415+ }
19416+ return true;
19417+}
19418+
19419+_SOKOL_PRIVATE bool _sg_vk_mem_alloc_image_device_memory(_sg_image_t* img) {
19420+ SOKOL_ASSERT(_sg.vk.dev);
19421+ SOKOL_ASSERT(img);
19422+ SOKOL_ASSERT(img->vk.img);
19423+ SOKOL_ASSERT(0 == img->vk.mem);
19424+ _SG_STRUCT(VkMemoryRequirements, mem_reqs);
19425+ vkGetImageMemoryRequirements(_sg.vk.dev, img->vk.img, &mem_reqs);
19426+ img->vk.mem = _sg_vk_mem_alloc_device_memory(_SG_VK_MEMTYPE_IMAGE, &mem_reqs);
19427+ if (0 == img->vk.mem) {
19428+ _SG_ERROR(VULKAN_ALLOC_IMAGE_DEVICE_MEMORY_FAILED);
19429+ return false;
19430+ }
19431+ return true;
19432+}
19433+
19434+_SOKOL_PRIVATE void _sg_vk_create_delete_queues(void) {
19435+ const uint32_t num_items = (uint32_t)
19436+ (2 * _sg.desc.buffer_pool_size +
19437+ 2 * _sg.desc.image_pool_size +
19438+ 1 * _sg.desc.sampler_pool_size +
19439+ 5 * _sg.desc.shader_pool_size +
19440+ 2 * _sg.desc.pipeline_pool_size +
19441+ 1 * _sg.desc.view_pool_size +
19442+ 256);
19443+ for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
19444+ _sg_vk_delete_queue_t* queue = &_sg.vk.frame.slot[i].delete_queue;
19445+ SOKOL_ASSERT(0 == queue->items);
19446+ SOKOL_ASSERT(0 == queue->index);
19447+ queue->num = num_items;
19448+ const size_t pool_size = num_items * sizeof(_sg_vk_delete_queue_item_t);
19449+ queue->items = (_sg_vk_delete_queue_item_t*)_sg_malloc(pool_size);
19450+ }
19451+}
19452+
19453+_SOKOL_PRIVATE void _sg_vk_delete_queue_collect_items(_sg_vk_delete_queue_t* queue) {
19454+ SOKOL_ASSERT(queue && queue->items);
19455+ for (uint32_t i = 0; i < queue->index; i++) {
19456+ _sg_vk_delete_queue_item_t* item = &queue->items[i];
19457+ SOKOL_ASSERT(item->destructor && item->obj);
19458+ item->destructor(item->obj);
19459+ item->destructor = 0;
19460+ item->obj = 0;
19461+ }
19462+ _sg_stats_add(vk.num_delete_queue_collected, queue->index);
19463+ queue->index = 0;
19464+}
19465+
19466+_SOKOL_PRIVATE void _sg_vk_destroy_delete_queues(void) {
19467+ for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
19468+ _sg_vk_delete_queue_t* queue = &_sg.vk.frame.slot[i].delete_queue;
19469+ SOKOL_ASSERT(queue->items);
19470+ _sg_vk_delete_queue_collect_items(queue);
19471+ _sg_free(queue->items);
19472+ SOKOL_ASSERT(queue->index == 0);
19473+ queue->items = 0;
19474+ queue->num = 0;
19475+ }
19476+}
19477+
19478+_SOKOL_PRIVATE _sg_vk_delete_queue_t* _sg_vk_cur_delete_queue(void) {
19479+ return &_sg.vk.frame.slot[_sg.vk.frame_slot].delete_queue;
19480+}
19481+
19482+_SOKOL_PRIVATE void _sg_vk_delete_queue_collect(void) {
19483+ _sg_vk_delete_queue_t* queue = _sg_vk_cur_delete_queue();
19484+ _sg_vk_delete_queue_collect_items(queue);
19485+}
19486+
19487+_SOKOL_PRIVATE void _sg_vk_delete_queue_add(_sg_vk_delete_queue_destructor_t destructor, void* obj) {
19488+ SOKOL_ASSERT(destructor && obj);
19489+ _sg_vk_delete_queue_t* queue = _sg_vk_cur_delete_queue();
19490+ SOKOL_ASSERT(queue->items);
19491+ if (queue->index >= queue->num) {
19492+ _SG_PANIC(VULKAN_DELETE_QUEUE_EXHAUSTED);
19493+ }
19494+ queue->items[queue->index].destructor = destructor;
19495+ queue->items[queue->index].obj = obj;
19496+ queue->index += 1;
19497+ _sg_stats_inc(vk.num_delete_queue_added);
19498+}
19499+
19500+// double-buffer system for any non-blocking CPU => GPU data
19501+_SOKOL_PRIVATE void _sg_vk_shared_buffer_init(_sg_vk_shared_buffer_t* shbuf, uint32_t size, uint32_t align, _sg_vk_memtype_t mem_type, const char* label) {
19502+ SOKOL_ASSERT(_sg.vk.dev);
19503+ SOKOL_ASSERT(shbuf && (size > 0) && (align > 0));
19504+ SOKOL_ASSERT(0 == shbuf->size);
19505+ SOKOL_ASSERT(0 == shbuf->offset);
19506+ SOKOL_ASSERT(0 == shbuf->cur_buf);
19507+ SOKOL_ASSERT(false == shbuf->overflown);
19508+ VkResult res;
19509+ VkBufferUsageFlags vk_usage = 0;
19510+ bool want_device_address = false;
19511+ switch (mem_type) {
19512+ case _SG_VK_MEMTYPE_STAGING_STREAM:
19513+ vk_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
19514+ break;
19515+ case _SG_VK_MEMTYPE_UNIFORMS:
19516+ vk_usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
19517+ vk_usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
19518+ want_device_address = true;
19519+ break;
19520+ case _SG_VK_MEMTYPE_DESCRIPTORS:
19521+ vk_usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;
19522+ vk_usage |= VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT;
19523+ vk_usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
19524+ want_device_address = true;
19525+ break;
19526+ default:
19527+ SOKOL_UNREACHABLE;
19528+ break;
19529+ }
19530+
19531+ shbuf->size = _sg_roundup_u32(size, align);
19532+ shbuf->align = align;
19533+ for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
19534+ SOKOL_ASSERT(0 == shbuf->slots[i].buf);
19535+ SOKOL_ASSERT(0 == shbuf->slots[i].mem);
19536+ SOKOL_ASSERT(0 == shbuf->slots[i].mem_ptr);
19537+ _SG_STRUCT(VkBufferCreateInfo, buf_create_info);
19538+ buf_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
19539+ buf_create_info.size = shbuf->size;
19540+ buf_create_info.usage = vk_usage;
19541+ buf_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
19542+ res = vkCreateBuffer(_sg.vk.dev, &buf_create_info, 0, &shbuf->slots[i].buf);
19543+ if (res != VK_SUCCESS) {
19544+ _SG_PANIC(VULKAN_CREATE_SHARED_BUFFER_FAILED);
19545+ }
19546+ SOKOL_ASSERT(shbuf->slots[i].buf);
19547+ _sg_vk_set_object_label(VK_OBJECT_TYPE_BUFFER, (uint64_t)shbuf->slots[i].buf, label);
19548+
19549+ _SG_STRUCT(VkMemoryRequirements, mem_reqs);
19550+ vkGetBufferMemoryRequirements(_sg.vk.dev, shbuf->slots[i].buf, &mem_reqs);
19551+ shbuf->slots[i].mem = _sg_vk_mem_alloc_device_memory(mem_type, &mem_reqs);
19552+ if (0 == shbuf->slots[i].mem) {
19553+ _SG_PANIC(VULKAN_ALLOCATE_SHARED_BUFFER_MEMORY_FAILED);
19554+ }
19555+ res = vkBindBufferMemory(_sg.vk.dev, shbuf->slots[i].buf, shbuf->slots[i].mem, 0);
19556+ if (res != VK_SUCCESS) {
19557+ _SG_PANIC(VULKAN_BIND_SHARED_BUFFER_MEMORY_FAILED);
19558+ }
19559+ if (want_device_address) {
19560+ _SG_STRUCT(VkBufferDeviceAddressInfo, addr_info);
19561+ addr_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO;
19562+ addr_info.buffer = shbuf->slots[i].buf;
19563+ shbuf->slots[i].dev_addr = vkGetBufferDeviceAddress(_sg.vk.dev, &addr_info);
19564+ SOKOL_ASSERT(shbuf->slots[i].dev_addr);
19565+ }
19566+ res = vkMapMemory(_sg.vk.dev, shbuf->slots[i].mem, 0, VK_WHOLE_SIZE, 0, &shbuf->slots[i].mem_ptr);
19567+ if (res != VK_SUCCESS) {
19568+ _SG_PANIC(VULKAN_MAP_SHARED_BUFFER_MEMORY_FAILED);
19569+ }
19570+ SOKOL_ASSERT(shbuf->slots[i].mem_ptr);
19571+ }
19572+}
19573+
19574+_SOKOL_PRIVATE void _sg_vk_shared_buffer_discard(_sg_vk_shared_buffer_t* shbuf) {
19575+ SOKOL_ASSERT(_sg.vk.dev);
19576+ SOKOL_ASSERT(shbuf);
19577+ for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
19578+ SOKOL_ASSERT(shbuf->slots[i].buf);
19579+ SOKOL_ASSERT(shbuf->slots[i].mem);
19580+ SOKOL_ASSERT(shbuf->slots[i].mem_ptr);
19581+ vkUnmapMemory(_sg.vk.dev, shbuf->slots[i].mem);
19582+ shbuf->slots[i].mem_ptr = 0;
19583+ _sg_vk_mem_free_device_memory(shbuf->slots[i].mem);
19584+ shbuf->slots[i].mem = 0;
19585+ vkDestroyBuffer(_sg.vk.dev, shbuf->slots[i].buf, 0);
19586+ shbuf->slots[i].buf = 0;
19587+ shbuf->slots[i].dev_addr = 0;
19588+ }
19589+ shbuf->size = 0;
19590+ shbuf->offset = 0;
19591+ shbuf->cur_buf = 0;
19592+ shbuf->cur_dev_addr = 0;
19593+ shbuf->overflown = false;
19594+}
19595+
19596+_SOKOL_PRIVATE void _sg_vk_shared_buffer_after_acquire(_sg_vk_shared_buffer_t* shbuf) {
19597+ SOKOL_ASSERT(_sg.vk.dev);
19598+ SOKOL_ASSERT(0 == shbuf->cur_buf);
19599+ SOKOL_ASSERT(0 == shbuf->cur_mem_ptr);
19600+ SOKOL_ASSERT(0 == shbuf->cur_dev_addr);
19601+ const uint32_t frame_slot = _sg.vk.frame_slot;
19602+ shbuf->offset = 0;
19603+ shbuf->cur_buf = shbuf->slots[frame_slot].buf;
19604+ shbuf->cur_mem_ptr = shbuf->slots[frame_slot].mem_ptr;
19605+ shbuf->cur_dev_addr = shbuf->slots[frame_slot].dev_addr; // NOTE: may be 0
19606+ shbuf->overflown = false;
19607+ SOKOL_ASSERT(shbuf->cur_buf);
19608+ SOKOL_ASSERT(shbuf->cur_mem_ptr);
19609+}
19610+
19611+_SOKOL_PRIVATE void _sg_vk_shared_buffer_before_submit(_sg_vk_shared_buffer_t* shbuf) {
19612+ SOKOL_ASSERT(shbuf->cur_buf);
19613+ SOKOL_ASSERT(shbuf->cur_mem_ptr);
19614+ // NOTE: if the buffer wouldn't be cache-coherent, this would be the place to do a flush
19615+ shbuf->cur_buf = 0;
19616+ shbuf->cur_mem_ptr = 0;
19617+ shbuf->cur_dev_addr = 0;
19618+}
19619+
19620+_SOKOL_PRIVATE VkDeviceSize _sg_vk_shared_buffer_alloc(_sg_vk_shared_buffer_t* shbuf, uint32_t num_bytes) {
19621+ SOKOL_ASSERT(shbuf && (num_bytes > 0));
19622+ if (shbuf->overflown) {
19623+ return _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT;
19624+ }
19625+ if ((shbuf->offset + num_bytes) > shbuf->size) {
19626+ shbuf->overflown = true;
19627+ return _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT;
19628+ }
19629+ SOKOL_ASSERT((shbuf->offset & (shbuf->align - 1)) == 0);
19630+ VkDeviceSize offset = shbuf->offset;
19631+ shbuf->offset = _sg_roundup_u32(shbuf->offset + num_bytes, shbuf->align);
19632+ return offset;
19633+}
19634+
19635+_SOKOL_PRIVATE uint8_t* _sg_vk_shared_buffer_ptr(_sg_vk_shared_buffer_t* shbuf, VkDeviceSize offset) {
19636+ SOKOL_ASSERT(shbuf && shbuf->cur_mem_ptr);
19637+ SOKOL_ASSERT(!shbuf->overflown);
19638+ SOKOL_ASSERT(offset < shbuf->size);
19639+ return ((uint8_t*)shbuf->cur_mem_ptr) + offset;
19640+}
19641+
19642+_SOKOL_PRIVATE VkDeviceSize _sg_vk_shared_buffer_memcpy(_sg_vk_shared_buffer_t* shbuf, const void* src_ptr, uint32_t num_bytes) {
19643+ SOKOL_ASSERT(shbuf && src_ptr && (num_bytes > 0));
19644+ const VkDeviceSize offset = _sg_vk_shared_buffer_alloc(shbuf, num_bytes);
19645+ if (offset != _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT) {
19646+ memcpy(_sg_vk_shared_buffer_ptr(shbuf, offset), src_ptr, num_bytes);
19647+ }
19648+ return offset;
19649+}
19650+
19651+// staging system for blocking immutable and dynamic updates, can deal arbitrarily sized data
19652+_SOKOL_PRIVATE void _sg_vk_staging_copy_init(void) {
19653+ SOKOL_ASSERT(_sg.vk.dev);
19654+ VkResult res;
19655+
19656+ SOKOL_ASSERT(0 == _sg.vk.stage.copy.cmd_pool);
19657+ SOKOL_ASSERT(0 == _sg.vk.stage.copy.cmd_buf);
19658+ SOKOL_ASSERT(0 == _sg.vk.stage.copy.size);
19659+ SOKOL_ASSERT(0 == _sg.vk.stage.copy.buf);
19660+ SOKOL_ASSERT(0 == _sg.vk.stage.copy.mem);
19661+ SOKOL_ASSERT(_sg.desc.vulkan.copy_staging_buffer_size > 0);
19662+
19663+ _SG_STRUCT(VkCommandPoolCreateInfo, pool_create_info);
19664+ pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
19665+ pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
19666+ pool_create_info.queueFamilyIndex = _sg.vk.queue_family_index;
19667+ res = vkCreateCommandPool(_sg.vk.dev, &pool_create_info, 0, &_sg.vk.stage.copy.cmd_pool);
19668+ SOKOL_ASSERT((res == VK_SUCCESS && _sg.vk.stage.copy.cmd_pool));
19669+ _sg_vk_set_object_label(VK_OBJECT_TYPE_COMMAND_POOL, (uint64_t)_sg.vk.stage.copy.cmd_pool, "copy-staging cmd pool");
19670+
19671+ _SG_STRUCT(VkCommandBufferAllocateInfo, cmdbuf_alloc_info);
19672+ cmdbuf_alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
19673+ cmdbuf_alloc_info.commandPool = _sg.vk.stage.copy.cmd_pool;
19674+ cmdbuf_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
19675+ cmdbuf_alloc_info.commandBufferCount = 1;
19676+ res = vkAllocateCommandBuffers(_sg.vk.dev, &cmdbuf_alloc_info, &_sg.vk.stage.copy.cmd_buf);
19677+ SOKOL_ASSERT((res == VK_SUCCESS) && _sg.vk.stage.copy.cmd_buf);
19678+ _sg_vk_set_object_label(VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)_sg.vk.stage.copy.cmd_buf, "copy-staging cmd buffer");
19679+
19680+ _sg.vk.stage.copy.size = (uint32_t) _sg.desc.vulkan.copy_staging_buffer_size;
19681+ _SG_STRUCT(VkBufferCreateInfo, buf_create_info);
19682+ buf_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
19683+ buf_create_info.size = _sg.vk.stage.copy.size;
19684+ buf_create_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
19685+ buf_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
19686+ res = vkCreateBuffer(_sg.vk.dev, &buf_create_info, 0, &_sg.vk.stage.copy.buf);
19687+ if (res != VK_SUCCESS) {
19688+ _SG_PANIC(VULKAN_STAGING_CREATE_BUFFER_FAILED);
19689+ }
19690+ SOKOL_ASSERT(_sg.vk.stage.copy.buf);
19691+ _sg_vk_set_object_label(VK_OBJECT_TYPE_BUFFER, (uint64_t)_sg.vk.stage.copy.buf, "copy-staging staging buffer");
19692+
19693+ _SG_STRUCT(VkMemoryRequirements, mem_reqs);
19694+ vkGetBufferMemoryRequirements(_sg.vk.dev, _sg.vk.stage.copy.buf, &mem_reqs);
19695+ _sg.vk.stage.copy.mem = _sg_vk_mem_alloc_device_memory(_SG_VK_MEMTYPE_STAGING_COPY, &mem_reqs);
19696+ if (0 == _sg.vk.stage.copy.mem) {
19697+ _SG_PANIC(VULKAN_STAGING_ALLOCATE_MEMORY_FAILED);
19698+ }
19699+ res = vkBindBufferMemory(_sg.vk.dev, _sg.vk.stage.copy.buf, _sg.vk.stage.copy.mem, 0);
19700+ if (res != VK_SUCCESS) {
19701+ _SG_PANIC(VULKAN_STAGING_BIND_BUFFER_MEMORY_FAILED);
19702+ }
19703+}
19704+
19705+_SOKOL_PRIVATE void _sg_vk_staging_copy_discard(void) {
19706+ SOKOL_ASSERT(_sg.vk.dev);
19707+ SOKOL_ASSERT(_sg.vk.stage.copy.cmd_pool);
19708+ SOKOL_ASSERT(_sg.vk.stage.copy.cmd_buf);
19709+ SOKOL_ASSERT(_sg.vk.stage.copy.size);
19710+ SOKOL_ASSERT(_sg.vk.stage.copy.buf);
19711+ SOKOL_ASSERT(_sg.vk.stage.copy.mem);
19712+
19713+ _sg_vk_mem_free_device_memory(_sg.vk.stage.copy.mem);
19714+ _sg.vk.stage.copy.mem = 0;
19715+ vkDestroyBuffer(_sg.vk.dev, _sg.vk.stage.copy.buf, 0);
19716+ _sg.vk.stage.copy.buf = 0;
19717+ vkDestroyCommandPool(_sg.vk.dev, _sg.vk.stage.copy.cmd_pool, 0);
19718+ _sg.vk.stage.copy.cmd_pool = 0;
19719+ _sg.vk.stage.copy.cmd_buf = 0;
19720+ _sg.vk.stage.copy.size = 0;
19721+}
19722+
19723+_SOKOL_PRIVATE VkCommandBuffer _sg_vk_staging_copy_begin(void) {
19724+ VkCommandBuffer cmd_buf = _sg.vk.stage.copy.cmd_buf;
19725+ _SG_STRUCT(VkCommandBufferBeginInfo, cmdbuf_begin_info);
19726+ cmdbuf_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
19727+ cmdbuf_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
19728+ VkResult res = vkBeginCommandBuffer(cmd_buf, &cmdbuf_begin_info);
19729+ SOKOL_ASSERT(res == VK_SUCCESS); _SOKOL_UNUSED(res);
19730+ return cmd_buf;
19731+}
19732+
19733+_SOKOL_PRIVATE void _sg_vk_staging_copy_end(VkCommandBuffer cmd_buf, VkQueue queue) {
19734+ SOKOL_ASSERT(cmd_buf && queue);
19735+ VkResult res;
19736+ _SOKOL_UNUSED(res);
19737+ vkEndCommandBuffer(cmd_buf);
19738+ _SG_STRUCT(VkSubmitInfo, submit_info);
19739+ submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
19740+ submit_info.commandBufferCount = 1;
19741+ submit_info.pCommandBuffers = &cmd_buf;
19742+ res = vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
19743+ SOKOL_ASSERT(res == VK_SUCCESS);
19744+ res = vkQueueWaitIdle(queue);
19745+ SOKOL_ASSERT(res == VK_SUCCESS);
19746+ res = vkResetCommandBuffer(cmd_buf, 0);
19747+ SOKOL_ASSERT(res == VK_SUCCESS);
19748+}
19749+
19750+_SOKOL_PRIVATE void _sg_vk_staging_map_memcpy_unmap(VkDeviceMemory mem, const void* ptr, uint32_t num_bytes) {
19751+ SOKOL_ASSERT(_sg.vk.dev);
19752+ SOKOL_ASSERT(mem);
19753+ SOKOL_ASSERT(ptr);
19754+ SOKOL_ASSERT(num_bytes > 0);
19755+ void* dst_ptr = 0;
19756+ VkResult res = vkMapMemory(_sg.vk.dev, mem, 0, VK_WHOLE_SIZE, 0, &dst_ptr);
19757+ SOKOL_ASSERT((res == VK_SUCCESS) && dst_ptr); _SOKOL_UNUSED(res);
19758+ memcpy(dst_ptr, ptr, num_bytes);
19759+ vkUnmapMemory(_sg.vk.dev, mem);
19760+}
19761+
19762+_SOKOL_PRIVATE void _sg_vk_staging_copy_buffer_data(_sg_buffer_t* buf, const sg_range* src_data, size_t dst_offset, bool initial_wait) {
19763+ SOKOL_ASSERT(_sg.vk.dev);
19764+ SOKOL_ASSERT(_sg.vk.queue);
19765+ SOKOL_ASSERT(_sg.vk.stage.copy.mem);
19766+ SOKOL_ASSERT(_sg.vk.stage.copy.buf);
19767+ SOKOL_ASSERT(buf && buf->vk.buf);
19768+ SOKOL_ASSERT(src_data && src_data->ptr && (src_data->size > 0));
19769+ SOKOL_ASSERT((dst_offset + src_data->size) <= (size_t)buf->cmn.size);
19770+
19771+ // an inital wait is only needed for updating existing resources but not when populating a new resource
19772+ if (initial_wait) {
19773+ VkResult res = vkQueueWaitIdle(_sg.vk.queue);
19774+ SOKOL_ASSERT(res == VK_SUCCESS); _SOKOL_UNUSED(res);
19775+ }
19776+
19777+ VkDeviceMemory dst_mem = _sg.vk.stage.copy.mem;
19778+ VkBuffer src_buf = _sg.vk.stage.copy.buf;
19779+ VkBuffer dst_buf = buf->vk.buf;
19780+ const uint8_t* src_ptr = (const uint8_t*)src_data->ptr;
19781+ uint32_t dst_size = _sg.vk.stage.copy.size;
19782+ uint32_t bytes_remaining = (uint32_t)src_data->size;
19783+ _SG_STRUCT(VkBufferCopy, region);
19784+ region.dstOffset = dst_offset;
19785+ while (bytes_remaining > 0) {
19786+ uint64_t bytes_to_copy = bytes_remaining;
19787+ if (bytes_remaining > dst_size) {
19788+ bytes_to_copy = dst_size;
19789+ bytes_remaining -= dst_size;
19790+ } else {
19791+ bytes_to_copy = bytes_remaining;
19792+ bytes_remaining = 0;
19793+ }
19794+ region.size = bytes_to_copy;
19795+ _sg_vk_staging_map_memcpy_unmap(dst_mem, src_ptr, (uint32_t)bytes_to_copy);
19796+ VkCommandBuffer cmd_buf = _sg_vk_staging_copy_begin();
19797+ vkCmdCopyBuffer(cmd_buf, src_buf, dst_buf, 1, ®ion);
19798+ _sg_stats_inc(vk.num_cmd_copy_buffer);
19799+ _sg_vk_staging_copy_end(cmd_buf, _sg.vk.queue);
19800+ src_ptr += bytes_to_copy;
19801+ region.dstOffset += bytes_to_copy;
19802+ }
19803+ buf->vk.cur_access = _SG_VK_ACCESS_VERTEXBUFFER | _SG_VK_ACCESS_INDEXBUFFER | _SG_VK_ACCESS_STORAGEBUFFER_RO;
19804+}
19805+
19806+_SOKOL_PRIVATE void _sg_vk_init_vk_image_staging_structs(const _sg_image_t* img, VkBuffer vk_buf, VkBufferImageCopy2* region, VkCopyBufferToImageInfo2* copy_info) {
19807+ SOKOL_ASSERT(img && region && copy_info);
19808+
19809+ region->sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2;
19810+ if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) {
19811+ region->imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
19812+ if (_sg_is_depth_stencil_format(img->cmn.pixel_format)) {
19813+ region->imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
19814+ }
19815+ } else {
19816+ region->imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
19817+ }
19818+ region->imageSubresource.layerCount = 1;
19819+ region->imageExtent.depth = 1;
19820+
19821+ copy_info->sType = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2;
19822+ copy_info->srcBuffer = vk_buf;
19823+ copy_info->dstImage = img->vk.img;
19824+ copy_info->dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
19825+ copy_info->regionCount = 1;
19826+ copy_info->pRegions = region;
19827+}
19828+
19829+_SOKOL_PRIVATE void _sg_vk_staging_copy_image_data(_sg_image_t* img, const sg_image_data* src_data, bool initial_wait) {
19830+ SOKOL_ASSERT(_sg.vk.dev);
19831+ SOKOL_ASSERT(_sg.vk.queue);
19832+ SOKOL_ASSERT(_sg.vk.stage.copy.mem);
19833+ SOKOL_ASSERT(_sg.vk.stage.copy.buf);
19834+ SOKOL_ASSERT(img && img->vk.img);
19835+ const uint32_t block_dim = (uint32_t)_sg_block_dim(img->cmn.pixel_format);
19836+
19837+ // an inital wait is only needed for updating existing resources but not when populating a new resource
19838+ if (initial_wait) {
19839+ VkResult res = vkQueueWaitIdle(_sg.vk.queue);
19840+ SOKOL_ASSERT(res == VK_SUCCESS); _SOKOL_UNUSED(res);
19841+ }
19842+
19843+ VkDeviceMemory mem = _sg.vk.stage.copy.mem;
19844+ _SG_STRUCT(VkBufferImageCopy2, region);
19845+ _SG_STRUCT(VkCopyBufferToImageInfo2, copy_info);
19846+ _sg_vk_init_vk_image_staging_structs(img, _sg.vk.stage.copy.buf, ®ion, ©_info);
19847+ for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) {
19848+ const uint8_t* src_ptr = (uint8_t*)src_data->mip_levels[mip_index].ptr;
19849+ int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index);
19850+ int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index);
19851+ int mip_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices;
19852+ const uint32_t row_pitch = (uint32_t) _sg_row_pitch(img->cmn.pixel_format, mip_width, 1);
19853+ const uint32_t num_rows = (uint32_t) _sg_num_rows(img->cmn.pixel_format, mip_height);
19854+ region.imageSubresource.mipLevel = (uint32_t)mip_index;
19855+ region.imageExtent.width = (uint32_t)mip_width;
19856+
19857+ const uint32_t max_rows = _sg.vk.stage.copy.size / row_pitch;
19858+ for (int slice_index = 0; slice_index < mip_slices; slice_index++) {
19859+ if (img->cmn.type == SG_IMAGETYPE_3D) {
19860+ region.imageOffset.z = slice_index;
19861+ } else {
19862+ region.imageSubresource.baseArrayLayer = (uint32_t)slice_index;
19863+ }
19864+ uint32_t rows_remaining = num_rows;
19865+ uint32_t cur_row = 0;
19866+ while (rows_remaining > 0) {
19867+ uint32_t rows_to_copy = rows_remaining;
19868+ if (rows_remaining > max_rows) {
19869+ rows_to_copy = max_rows;
19870+ rows_remaining -= max_rows;
19871+ } else {
19872+ rows_to_copy = rows_remaining;
19873+ rows_remaining = 0;
19874+ }
19875+ const uint32_t bytes_to_copy = rows_to_copy * row_pitch;
19876+ SOKOL_ASSERT(bytes_to_copy <= _sg.vk.stage.copy.size);
19877+ _sg_vk_staging_map_memcpy_unmap(mem, src_ptr, bytes_to_copy);
19878+ src_ptr += bytes_to_copy;
19879+ VkCommandBuffer cmd_buf = _sg_vk_staging_copy_begin();
19880+ _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_STAGING);
19881+ region.imageOffset.y = (int32_t)(cur_row * block_dim);
19882+ region.imageExtent.height = _sg_min((uint32_t)mip_height, rows_to_copy * block_dim);
19883+ vkCmdCopyBufferToImage2(cmd_buf, ©_info);
19884+ _sg_stats_inc(vk.num_cmd_copy_buffer_to_image);
19885+ _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE);
19886+
19887+ _sg_vk_staging_copy_end(cmd_buf, _sg.vk.queue);
19888+ cur_row += rows_to_copy;
19889+ }
19890+ }
19891+ }
19892+}
19893+
19894+// staging system for non-blocking streaming updates with a max per-frame data limit
19895+_SOKOL_PRIVATE void _sg_vk_staging_stream_init(void) {
19896+ SOKOL_ASSERT(_sg.desc.vulkan.stream_staging_buffer_size > 0);
19897+ _sg_vk_shared_buffer_init(&_sg.vk.stage.stream,
19898+ (uint32_t)_sg.desc.vulkan.stream_staging_buffer_size,
19899+ 16, // NOTE: arbitrary alignment (FIXME?)
19900+ _SG_VK_MEMTYPE_STAGING_STREAM,
19901+ "shared-stream-buffer");
19902+}
19903+
19904+_SOKOL_PRIVATE void _sg_vk_staging_stream_discard(void) {
19905+ _sg_vk_shared_buffer_discard(&_sg.vk.stage.stream);
19906+}
19907+
19908+_SOKOL_PRIVATE void _sg_vk_staging_stream_after_acquire(void) {
19909+ _sg_vk_shared_buffer_after_acquire(&_sg.vk.stage.stream);
19910+}
19911+
19912+_SOKOL_PRIVATE void _sg_vk_staging_stream_before_submit(void) {
19913+ _sg_vk_shared_buffer_before_submit(&_sg.vk.stage.stream);
19914+}
19915+
19916+_SOKOL_PRIVATE void _sg_vk_staging_stream_buffer_data(_sg_buffer_t* buf, const sg_range* src_data, size_t dst_offset) {
19917+ SOKOL_ASSERT(_sg.vk.dev);
19918+ SOKOL_ASSERT(_sg.vk.frame.stream_cmd_buf);
19919+ SOKOL_ASSERT(_sg.vk.stage.stream.cur_buf);
19920+ SOKOL_ASSERT(buf && buf->vk.buf);
19921+ SOKOL_ASSERT(src_data && src_data->ptr && (src_data->size > 0));
19922+ SOKOL_ASSERT((src_data->size + dst_offset) <= (size_t)buf->cmn.size);
19923+
19924+ const uint32_t src_offset = (uint32_t)_sg_vk_shared_buffer_memcpy(&_sg.vk.stage.stream, src_data->ptr, (uint32_t)src_data->size);
19925+ if (src_offset == _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT) {
19926+ _SG_ERROR(VULKAN_STAGING_STREAM_BUFFER_OVERFLOW);
19927+ return;
19928+ }
19929+ VkCommandBuffer cmd_buf = _sg.vk.frame.stream_cmd_buf;
19930+ VkBuffer vk_src_buf = _sg.vk.stage.stream.cur_buf;
19931+ VkBuffer vk_dst_buf = buf->vk.buf;
19932+ _SG_STRUCT(VkBufferCopy, region);
19933+ region.srcOffset = src_offset;
19934+ region.dstOffset = dst_offset;
19935+ region.size = src_data->size;
19936+ _sg_vk_buffer_barrier(cmd_buf, buf, _SG_VK_ACCESS_STAGING);
19937+ vkCmdCopyBuffer(cmd_buf, vk_src_buf, vk_dst_buf, 1, ®ion);
19938+ _sg_stats_inc(vk.num_cmd_copy_buffer);
19939+ // FIXME: not great to issue a barrier right here,
19940+ // rethink buffer barrier strategy? => a single memory barrier
19941+ // at the end of the stream command buffer should be sufficient?
19942+ _sg_vk_buffer_barrier(cmd_buf, buf, _SG_VK_ACCESS_VERTEXBUFFER|_SG_VK_ACCESS_INDEXBUFFER|_SG_VK_ACCESS_STORAGEBUFFER_RO);
19943+}
19944+
19945+_SOKOL_PRIVATE void _sg_vk_staging_stream_image_data(_sg_image_t* img, const sg_image_data* src_data) {
19946+ SOKOL_ASSERT(_sg.vk.dev);
19947+ SOKOL_ASSERT(_sg.vk.frame.stream_cmd_buf);
19948+ SOKOL_ASSERT(img && img->vk.img);
19949+ SOKOL_ASSERT(src_data);
19950+ VkCommandBuffer cmd_buf = _sg.vk.frame.stream_cmd_buf;
19951+ _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_STAGING);
19952+ _SG_STRUCT(VkBufferImageCopy2, region);
19953+ _SG_STRUCT(VkCopyBufferToImageInfo2, copy_info);
19954+ _sg_vk_init_vk_image_staging_structs(img, _sg.vk.stage.stream.cur_buf, ®ion, ©_info);
19955+ for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) {
19956+ const sg_range* src_mip = &src_data->mip_levels[mip_index];
19957+ SOKOL_ASSERT(src_mip->ptr);
19958+ SOKOL_ASSERT(src_mip->size > 0);
19959+ const uint32_t src_offset = (uint32_t)_sg_vk_shared_buffer_memcpy(&_sg.vk.stage.stream, src_mip->ptr, (uint32_t)src_mip->size);
19960+ if (src_offset == _SG_VK_SHARED_BUFFER_OVERFLOW_RESULT) {
19961+ _SG_ERROR(VULKAN_STAGING_STREAM_BUFFER_OVERFLOW);
19962+ _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE);
19963+ return;
19964+ }
19965+ region.bufferOffset = src_offset;
19966+ int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index);
19967+ int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index);
19968+ int mip_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices;
19969+ region.imageExtent.width = (uint32_t)mip_width;
19970+ region.imageExtent.height = (uint32_t)mip_height;
19971+ region.imageSubresource.mipLevel = (uint32_t)mip_index;
19972+ if (img->cmn.type == SG_IMAGETYPE_3D) {
19973+ region.imageExtent.depth = (uint32_t)mip_slices;
19974+ region.imageSubresource.layerCount = 1;
19975+ } else {
19976+ region.imageExtent.depth = 1;
19977+ region.imageSubresource.layerCount = (uint32_t)mip_slices;
19978+ }
19979+ vkCmdCopyBufferToImage2(cmd_buf, ©_info);
19980+ _sg_stats_inc(vk.num_cmd_copy_buffer_to_image);
19981+ }
19982+ _sg_vk_image_barrier(cmd_buf, img, _SG_VK_ACCESS_TEXTURE);
19983+}
19984+
19985+// uniform data system
19986+_SOKOL_PRIVATE void _sg_vk_uniform_init(void) {
19987+ SOKOL_ASSERT(_sg.desc.uniform_buffer_size > 0);
19988+ SOKOL_ASSERT(0 == _sg.vk.uniforms.dset_cache);
19989+
19990+ _sg_vk_shared_buffer_init(&_sg.vk.uniforms.dbuf,
19991+ (uint32_t)_sg.desc.uniform_buffer_size,
19992+ (uint32_t)_sg.vk.dev_props.properties.limits.minUniformBufferOffsetAlignment,
19993+ _SG_VK_MEMTYPE_UNIFORMS,
19994+ "shared-uniform-buffer");
19995+
19996+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
19997+ _sg.vk.uniforms.addr_info[i].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT;
19998+ _sg.vk.uniforms.get_info[i].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT;
19999+ _sg.vk.uniforms.get_info[i].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
20000+ _sg.vk.uniforms.get_info[i].data.pUniformBuffer = &_sg.vk.uniforms.addr_info[i];
20001+ }
20002+
20003+ // NOTE: we assume here that the max alignment for uniform buffer
20004+ // descriptors in the descriptor buffer is the same as the assumed max
20005+ // descriptor size (e.g. 256 bytes)
20006+ _sg.vk.uniforms.dset_cache_size = SG_MAX_UNIFORMBLOCK_BINDSLOTS * _SG_VK_MAX_DESCRIPTOR_DATA_SIZE;
20007+ _sg.vk.uniforms.dset_cache = (uint8_t*)_sg_malloc_clear(_sg.vk.uniforms.dset_cache_size);
20008+}
20009+
20010+_SOKOL_PRIVATE void _sg_vk_uniform_discard(void) {
20011+ SOKOL_ASSERT(_sg.vk.uniforms.dset_cache);
20012+ _sg_free(_sg.vk.uniforms.dset_cache); _sg.vk.uniforms.dset_cache = 0;
20013+ _sg_vk_shared_buffer_discard(&_sg.vk.uniforms.dbuf);
20014+}
20015+
20016+// called from _sg_vk_acquire_frame_command_buffer()
20017+_SOKOL_PRIVATE void _sg_vk_uniform_after_acquire(void) {
20018+ _sg_vk_shared_buffer_after_acquire(&_sg.vk.uniforms.dbuf);
20019+ // reset uniform tracking data
20020+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
20021+ _sg.vk.uniforms.addr_info[i].address = 0;
20022+ _sg.vk.uniforms.addr_info[i].range = 0;
20023+ }
20024+}
20025+
20026+// called from _sg_vk_submit_frame_command_buffer()
20027+_SOKOL_PRIVATE void _sg_vk_uniform_before_submit(void) {
20028+ _sg_vk_shared_buffer_before_submit(&_sg.vk.uniforms.dbuf);
20029+}
20030+
20031+// called form _sg_vk_apply_uniforms, returns offset of data snippet into uniform buffer
20032+_SOKOL_PRIVATE uint32_t _sg_vk_uniform_copy(const sg_range* data) {
20033+ SOKOL_ASSERT(data && data->ptr && (data->size > 0));
20034+ return (uint32_t)_sg_vk_shared_buffer_memcpy(&_sg.vk.uniforms.dbuf, data->ptr, (uint32_t)data->size);
20035+}
20036+
20037+// resource binding system
20038+_SOKOL_PRIVATE void _sg_vk_bind_init(void) {
20039+ SOKOL_ASSERT(_sg.desc.vulkan.descriptor_buffer_size > 0);
20040+ _sg_vk_shared_buffer_init(&_sg.vk.bind,
20041+ (uint32_t)_sg.desc.vulkan.descriptor_buffer_size,
20042+ (uint32_t)_sg.vk.descriptor_buffer_props.descriptorBufferOffsetAlignment,
20043+ _SG_VK_MEMTYPE_DESCRIPTORS,
20044+ "shared-descriptor-buffer");
20045+}
20046+
20047+_SOKOL_PRIVATE void _sg_vk_bind_discard(void) {
20048+ _sg_vk_shared_buffer_discard(&_sg.vk.bind);
20049+}
20050+
20051+// called from _sg_vk_acquire_frame_command_buffer()
20052+_SOKOL_PRIVATE void _sg_vk_bind_after_acquire(void) {
20053+ _sg_vk_shared_buffer_after_acquire(&_sg.vk.bind);
20054+
20055+ // bind the current frame's descriptor buffer
20056+ SOKOL_ASSERT(_sg.vk.frame.cmd_buf);
20057+ SOKOL_ASSERT(_sg.vk.bind.cur_buf);
20058+ SOKOL_ASSERT(_sg.vk.bind.cur_dev_addr);
20059+ _SG_STRUCT(VkDescriptorBufferBindingInfoEXT, bind_info);
20060+ bind_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT;
20061+ bind_info.address = _sg.vk.bind.cur_dev_addr;
20062+ bind_info.usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT |
20063+ VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT;
20064+ _sg.vk.ext.cmd_bind_descriptor_buffers(_sg.vk.frame.cmd_buf, 1, &bind_info);
20065+}
20066+
20067+// called from _sg_vk_submit_frame_command_buffer()
20068+_SOKOL_PRIVATE void _sg_vk_bind_before_submit(void) {
20069+ _sg_vk_shared_buffer_before_submit(&_sg.vk.bind);
20070+}
20071+
20072+_SOKOL_PRIVATE bool _sg_vk_bind_view_smp_descriptor_set(VkCommandBuffer cmd_buf, const _sg_bindings_ptrs_t* bnd, VkPipelineBindPoint vk_bind_point) {
20073+ SOKOL_ASSERT(_sg.vk.dev);
20074+ SOKOL_ASSERT(cmd_buf);
20075+ SOKOL_ASSERT(bnd && bnd->pip);
20076+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd->pip->cmn.shader);
20077+
20078+ // get next pointer in descriptor buffer
20079+ const VkDeviceSize dset_size = shd->vk.view_smp_dset_size;
20080+ if (dset_size == 0) {
20081+ // nothing to bind
20082+ return true;
20083+ }
20084+ const VkDeviceSize dbuf_offset = _sg_vk_shared_buffer_alloc(&_sg.vk.bind, (uint32_t)dset_size);
20085+ if (_sg.vk.bind.overflown) {
20086+ _SG_ERROR(VULKAN_DESCRIPTOR_BUFFER_OVERFLOW);
20087+ return false;
20088+ }
20089+ _sg_stats_add(vk.size_descriptor_buffer_writes, (uint32_t)dset_size);
20090+ uint8_t* dbuf_ptr = _sg_vk_shared_buffer_ptr(&_sg.vk.bind, dbuf_offset);
20091+
20092+ // copy pre-recorded descriptor data into descriptor buffer
20093+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
20094+ if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) {
20095+ continue;
20096+ }
20097+ const _sg_view_t* view = bnd->views[i];
20098+ SOKOL_ASSERT(view && (view->vk.descriptor_size > 0));
20099+ const void* src_ptr = view->vk.descriptor_data;
20100+ size_t size = view->vk.descriptor_size;
20101+ void* dst_ptr = dbuf_ptr + shd->vk.view_dset_offsets[i];
20102+ memcpy(dst_ptr, src_ptr, size);
20103+ }
20104+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
20105+ if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) {
20106+ continue;
20107+ }
20108+ const _sg_sampler_t* smp = bnd->smps[i];
20109+ SOKOL_ASSERT(smp && (smp->vk.descriptor_size > 0));
20110+ const void* src_ptr = smp->vk.descriptor_data;
20111+ size_t size = smp->vk.descriptor_size;
20112+ void* dst_ptr = dbuf_ptr + shd->vk.smp_dset_offsets[i];
20113+ memcpy(dst_ptr, src_ptr, size);
20114+ }
20115+
20116+ // record the new descriptor buffer offset
20117+ const uint32_t dbuf_index = 0;
20118+ SOKOL_ASSERT(shd->vk.pip_layout);
20119+ _sg.vk.ext.cmd_set_descriptor_buffer_offsets(
20120+ cmd_buf,
20121+ vk_bind_point,
20122+ shd->vk.pip_layout,
20123+ _SG_VK_VIEW_SMP_DESCRIPTORSET_INDEX, // firstSet
20124+ 1, // setCount
20125+ &dbuf_index,
20126+ &dbuf_offset);
20127+ _sg_stats_inc(vk.num_cmd_set_descriptor_buffer_offsets);
20128+ return true;
20129+}
20130+
20131+_SOKOL_PRIVATE bool _sg_vk_bind_uniform_descriptor_set(VkCommandBuffer cmd_buf) {
20132+ SOKOL_ASSERT(cmd_buf);
20133+ SOKOL_ASSERT(_sg.vk.uniforms.dirty);
20134+ _sg.vk.uniforms.dirty = false;
20135+ const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip);
20136+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
20137+
20138+ // get next pointer in descriptor buffer
20139+ const VkDeviceSize dbuf_offset = _sg_vk_shared_buffer_alloc(&_sg.vk.bind, (uint32_t)shd->vk.ub_dset_size);
20140+ if (_sg.vk.bind.overflown) {
20141+ _SG_ERROR(VULKAN_DESCRIPTOR_BUFFER_OVERFLOW);
20142+ return false;
20143+ }
20144+ _sg_stats_add(vk.size_descriptor_buffer_writes, (uint32_t)shd->vk.ub_dset_size);
20145+ uint8_t* dbuf_ptr = _sg_vk_shared_buffer_ptr(&_sg.vk.bind, dbuf_offset);
20146+
20147+ // update descriptor buffer
20148+ SOKOL_ASSERT(shd->vk.ub_dset_size <= _sg.vk.uniforms.dset_cache_size);
20149+ memcpy(dbuf_ptr, _sg.vk.uniforms.dset_cache, shd->vk.ub_dset_size);
20150+
20151+ // record the descriptor buffer offset
20152+ const VkPipelineBindPoint vk_bind_point = _sg.cur_pass.is_compute
20153+ ? VK_PIPELINE_BIND_POINT_COMPUTE
20154+ : VK_PIPELINE_BIND_POINT_GRAPHICS;
20155+ const uint32_t dbuf_index = 0;
20156+ SOKOL_ASSERT(shd->vk.pip_layout);
20157+ _sg.vk.ext.cmd_set_descriptor_buffer_offsets(
20158+ cmd_buf,
20159+ vk_bind_point,
20160+ shd->vk.pip_layout,
20161+ _SG_VK_UB_DESCRIPTORSET_INDEX, // firstIndex
20162+ 1, // setCount
20163+ &dbuf_index,
20164+ &dbuf_offset);
20165+ _sg_stats_inc(vk.num_cmd_set_descriptor_buffer_offsets);
20166+ return true;
20167+}
20168+
20169+_SOKOL_PRIVATE void _sg_vk_memory_destructor(void* obj) {
20170+ SOKOL_ASSERT(_sg.vk.dev && obj);
20171+ _sg_vk_mem_free_device_memory((VkDeviceMemory)obj);
20172+}
20173+
20174+_SOKOL_PRIVATE void _sg_vk_buffer_destructor(void* obj) {
20175+ SOKOL_ASSERT(_sg.vk.dev && obj);
20176+ vkDestroyBuffer(_sg.vk.dev, (VkBuffer)obj, 0);
20177+}
20178+
20179+_SOKOL_PRIVATE void _sg_vk_image_destructor(void* obj) {
20180+ SOKOL_ASSERT(_sg.vk.dev && obj);
20181+ vkDestroyImage(_sg.vk.dev, (VkImage)obj, 0);
20182+}
20183+
20184+_SOKOL_PRIVATE void _sg_vk_image_view_destructor(void* obj) {
20185+ SOKOL_ASSERT(_sg.vk.dev && obj);
20186+ vkDestroyImageView(_sg.vk.dev, (VkImageView)obj, 0);
20187+}
20188+
20189+_SOKOL_PRIVATE void _sg_vk_sampler_destructor(void* obj) {
20190+ SOKOL_ASSERT(_sg.vk.dev && obj);
20191+ vkDestroySampler(_sg.vk.dev, (VkSampler)obj, 0);
20192+}
20193+
20194+_SOKOL_PRIVATE void _sg_vk_shader_module_destructor(void* obj) {
20195+ SOKOL_ASSERT(_sg.vk.dev && obj);
20196+ vkDestroyShaderModule(_sg.vk.dev, (VkShaderModule)obj, 0);
20197+}
20198+
20199+_SOKOL_PRIVATE void _sg_vk_pipelinelayout_destructor(void* obj) {
20200+ SOKOL_ASSERT(_sg.vk.dev && obj);
20201+ vkDestroyPipelineLayout(_sg.vk.dev, (VkPipelineLayout)obj, 0);
20202+}
20203+
20204+_SOKOL_PRIVATE void _sg_vk_descriptorsetlayout_destructor(void* obj) {
20205+ SOKOL_ASSERT(_sg.vk.dev && obj);
20206+ vkDestroyDescriptorSetLayout(_sg.vk.dev, (VkDescriptorSetLayout)obj, 0);
20207+}
20208+
20209+_SOKOL_PRIVATE void _sg_vk_pipeline_destructor(void* obj) {
20210+ SOKOL_ASSERT(_sg.vk.dev && obj);
20211+ vkDestroyPipeline(_sg.vk.dev, (VkPipeline)obj, 0);
20212+}
20213+
20214+_SOKOL_PRIVATE VkBufferUsageFlags _sg_vk_buffer_usage(const sg_buffer_usage* usg) {
20215+ VkBufferUsageFlags res = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
20216+ if (usg->vertex_buffer) {
20217+ res |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
20218+ }
20219+ if (usg->index_buffer) {
20220+ res |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
20221+ }
20222+ if (usg->storage_buffer) {
20223+ res |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
20224+ }
20225+ return res;
20226+}
20227+
20228+_SOKOL_PRIVATE VkVertexInputRate _sg_vk_vertex_input_rate(sg_vertex_step s) {
20229+ return (s == SG_VERTEXSTEP_PER_VERTEX) ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
20230+}
20231+
20232+_SOKOL_PRIVATE VkFormat _sg_vk_vertex_format(sg_vertex_format f) {
20233+ switch (f) {
20234+ case SG_VERTEXFORMAT_FLOAT: return VK_FORMAT_R32_SFLOAT;
20235+ case SG_VERTEXFORMAT_FLOAT2: return VK_FORMAT_R32G32_SFLOAT;
20236+ case SG_VERTEXFORMAT_FLOAT3: return VK_FORMAT_R32G32B32_SFLOAT;
20237+ case SG_VERTEXFORMAT_FLOAT4: return VK_FORMAT_R32G32B32A32_SFLOAT;
20238+ case SG_VERTEXFORMAT_INT: return VK_FORMAT_R32_SINT;
20239+ case SG_VERTEXFORMAT_INT2: return VK_FORMAT_R32G32_SINT;
20240+ case SG_VERTEXFORMAT_INT3: return VK_FORMAT_R32G32B32_SINT;
20241+ case SG_VERTEXFORMAT_INT4: return VK_FORMAT_R32G32B32A32_SINT;
20242+ case SG_VERTEXFORMAT_UINT: return VK_FORMAT_R32_UINT;
20243+ case SG_VERTEXFORMAT_UINT2: return VK_FORMAT_R32G32_UINT;
20244+ case SG_VERTEXFORMAT_UINT3: return VK_FORMAT_R32G32B32_UINT;
20245+ case SG_VERTEXFORMAT_UINT4: return VK_FORMAT_R32G32B32A32_UINT;
20246+ case SG_VERTEXFORMAT_BYTE4: return VK_FORMAT_R8G8B8A8_SINT;
20247+ case SG_VERTEXFORMAT_BYTE4N: return VK_FORMAT_R8G8B8A8_SNORM;
20248+ case SG_VERTEXFORMAT_UBYTE4: return VK_FORMAT_R8G8B8A8_UINT;
20249+ case SG_VERTEXFORMAT_UBYTE4N: return VK_FORMAT_R8G8B8A8_UNORM;
20250+ case SG_VERTEXFORMAT_SHORT2: return VK_FORMAT_R16G16_SINT;
20251+ case SG_VERTEXFORMAT_SHORT2N: return VK_FORMAT_R16G16_SNORM;
20252+ case SG_VERTEXFORMAT_USHORT2: return VK_FORMAT_R16G16_UINT;
20253+ case SG_VERTEXFORMAT_USHORT2N: return VK_FORMAT_R16G16_UNORM;
20254+ case SG_VERTEXFORMAT_SHORT4: return VK_FORMAT_R16G16B16A16_SINT;
20255+ case SG_VERTEXFORMAT_SHORT4N: return VK_FORMAT_R16G16B16A16_SNORM;
20256+ case SG_VERTEXFORMAT_USHORT4: return VK_FORMAT_R16G16B16A16_UINT;
20257+ case SG_VERTEXFORMAT_USHORT4N: return VK_FORMAT_R16G16B16A16_UNORM;
20258+ case SG_VERTEXFORMAT_INT10_N2: return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
20259+ case SG_VERTEXFORMAT_UINT10_N2: return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
20260+ case SG_VERTEXFORMAT_HALF2: return VK_FORMAT_R16G16_SFLOAT;
20261+ case SG_VERTEXFORMAT_HALF4: return VK_FORMAT_R16G16B16A16_SFLOAT;
20262+ default:
20263+ SOKOL_UNREACHABLE;
20264+ return VK_FORMAT_UNDEFINED;
20265+ }
20266+}
20267+
20268+_SOKOL_PRIVATE VkImageCreateFlags _sg_vk_image_create_flags(sg_image_type t) {
20269+ switch (t) {
20270+ case SG_IMAGETYPE_2D: return 0;
20271+ case SG_IMAGETYPE_CUBE: return VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
20272+ // FIXME: VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT needed for render to slice?
20273+ case SG_IMAGETYPE_3D: return 0;
20274+ case SG_IMAGETYPE_ARRAY: return 0;
20275+ default:
20276+ SOKOL_UNREACHABLE;
20277+ return 0;
20278+ }
20279+}
20280+
20281+_SOKOL_PRIVATE VkImageType _sg_vk_image_type(sg_image_type t) {
20282+ return (SG_IMAGETYPE_3D == t) ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
20283+}
20284+
20285+_SOKOL_PRIVATE VkImageUsageFlags _sg_vk_image_usage(const sg_image_usage* usg) {
20286+ VkImageUsageFlags res = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
20287+ res |= VK_IMAGE_USAGE_SAMPLED_BIT;
20288+ if (usg->storage_image) {
20289+ res |= VK_IMAGE_USAGE_STORAGE_BIT;
20290+ }
20291+ if (usg->color_attachment || usg->resolve_attachment) {
20292+ res |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
20293+ }
20294+ if (usg->depth_stencil_attachment) {
20295+ res |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
20296+ }
20297+ return res;
20298+}
20299+
20300+_SOKOL_PRIVATE VkFormat _sg_vk_format(sg_pixel_format fmt) {
20301+ switch (fmt) {
20302+ case SG_PIXELFORMAT_NONE: return VK_FORMAT_UNDEFINED;
20303+ case SG_PIXELFORMAT_R8: return VK_FORMAT_R8_UNORM;
20304+ case SG_PIXELFORMAT_R8SN: return VK_FORMAT_R8_SNORM;
20305+ case SG_PIXELFORMAT_R8UI: return VK_FORMAT_R8_UINT;
20306+ case SG_PIXELFORMAT_R8SI: return VK_FORMAT_R8_SINT;
20307+ case SG_PIXELFORMAT_R16: return VK_FORMAT_R16_UNORM;
20308+ case SG_PIXELFORMAT_R16SN: return VK_FORMAT_R16_SNORM;
20309+ case SG_PIXELFORMAT_R16UI: return VK_FORMAT_R16_UINT;
20310+ case SG_PIXELFORMAT_R16SI: return VK_FORMAT_R16_SINT;
20311+ case SG_PIXELFORMAT_R16F: return VK_FORMAT_R16_SFLOAT;
20312+ case SG_PIXELFORMAT_RG8: return VK_FORMAT_R8G8_UNORM;
20313+ case SG_PIXELFORMAT_RG8SN: return VK_FORMAT_R8G8_SNORM;
20314+ case SG_PIXELFORMAT_RG8UI: return VK_FORMAT_R8G8_UINT;
20315+ case SG_PIXELFORMAT_RG8SI: return VK_FORMAT_R8G8_SINT;
20316+ case SG_PIXELFORMAT_R32UI: return VK_FORMAT_R32_UINT;
20317+ case SG_PIXELFORMAT_R32SI: return VK_FORMAT_R32_SINT;
20318+ case SG_PIXELFORMAT_R32F: return VK_FORMAT_R32_SFLOAT;
20319+ case SG_PIXELFORMAT_RG16: return VK_FORMAT_R16G16_UNORM;
20320+ case SG_PIXELFORMAT_RG16SN: return VK_FORMAT_R16G16_SNORM;
20321+ case SG_PIXELFORMAT_RG16UI: return VK_FORMAT_R16G16_UINT;
20322+ case SG_PIXELFORMAT_RG16SI: return VK_FORMAT_R16G16_SINT;
20323+ case SG_PIXELFORMAT_RG16F: return VK_FORMAT_R16G16_SFLOAT;
20324+ case SG_PIXELFORMAT_RGBA8: return VK_FORMAT_R8G8B8A8_UNORM;
20325+ case SG_PIXELFORMAT_SRGB8A8: return VK_FORMAT_R8G8B8A8_SRGB;
20326+ case SG_PIXELFORMAT_RGBA8SN: return VK_FORMAT_R8G8B8A8_SNORM;
20327+ case SG_PIXELFORMAT_RGBA8UI: return VK_FORMAT_R8G8B8A8_UINT;
20328+ case SG_PIXELFORMAT_RGBA8SI: return VK_FORMAT_R8G8B8A8_SINT;
20329+ case SG_PIXELFORMAT_BGRA8: return VK_FORMAT_B8G8R8A8_UNORM;
20330+ case SG_PIXELFORMAT_SBGR8A8: return VK_FORMAT_B8G8R8A8_SRGB;
20331+ case SG_PIXELFORMAT_RGB10A2: return VK_FORMAT_A2R10G10B10_UNORM_PACK32;
20332+ case SG_PIXELFORMAT_RG11B10F: return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
20333+ case SG_PIXELFORMAT_RGB9E5: return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32;
20334+ case SG_PIXELFORMAT_RG32UI: return VK_FORMAT_R32G32_UINT;
20335+ case SG_PIXELFORMAT_RG32SI: return VK_FORMAT_R32G32_SINT;
20336+ case SG_PIXELFORMAT_RG32F: return VK_FORMAT_R32G32_SFLOAT;
20337+ case SG_PIXELFORMAT_RGBA16: return VK_FORMAT_R16G16B16A16_UNORM;
20338+ case SG_PIXELFORMAT_RGBA16SN: return VK_FORMAT_R16G16B16A16_SNORM;
20339+ case SG_PIXELFORMAT_RGBA16UI: return VK_FORMAT_R16G16B16A16_UINT;
20340+ case SG_PIXELFORMAT_RGBA16SI: return VK_FORMAT_R16G16B16A16_SINT;
20341+ case SG_PIXELFORMAT_RGBA16F: return VK_FORMAT_R16G16B16A16_SFLOAT;
20342+ case SG_PIXELFORMAT_RGBA32UI: return VK_FORMAT_R32G32B32A32_UINT;
20343+ case SG_PIXELFORMAT_RGBA32SI: return VK_FORMAT_R32G32B32A32_SINT;
20344+ case SG_PIXELFORMAT_RGBA32F: return VK_FORMAT_R32G32B32A32_SFLOAT;
20345+ case SG_PIXELFORMAT_DEPTH: return VK_FORMAT_D32_SFLOAT;
20346+ case SG_PIXELFORMAT_DEPTH_STENCIL: return VK_FORMAT_D32_SFLOAT_S8_UINT;
20347+ case SG_PIXELFORMAT_BC1_RGBA: return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
20348+ case SG_PIXELFORMAT_BC2_RGBA: return VK_FORMAT_BC2_UNORM_BLOCK;
20349+ case SG_PIXELFORMAT_BC3_RGBA: return VK_FORMAT_BC3_UNORM_BLOCK;
20350+ case SG_PIXELFORMAT_BC3_SRGBA: return VK_FORMAT_BC3_SRGB_BLOCK;
20351+ case SG_PIXELFORMAT_BC4_R: return VK_FORMAT_BC4_UNORM_BLOCK;
20352+ case SG_PIXELFORMAT_BC4_RSN: return VK_FORMAT_BC4_SNORM_BLOCK;
20353+ case SG_PIXELFORMAT_BC5_RG: return VK_FORMAT_BC5_UNORM_BLOCK;
20354+ case SG_PIXELFORMAT_BC5_RGSN: return VK_FORMAT_BC5_SNORM_BLOCK;
20355+ case SG_PIXELFORMAT_BC6H_RGBF: return VK_FORMAT_BC6H_SFLOAT_BLOCK;
20356+ case SG_PIXELFORMAT_BC6H_RGBUF: return VK_FORMAT_BC6H_UFLOAT_BLOCK;
20357+ case SG_PIXELFORMAT_BC7_RGBA: return VK_FORMAT_BC7_UNORM_BLOCK;
20358+ case SG_PIXELFORMAT_BC7_SRGBA: return VK_FORMAT_BC7_SRGB_BLOCK;
20359+ case SG_PIXELFORMAT_ETC2_RGB8: return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
20360+ case SG_PIXELFORMAT_ETC2_RGB8A1: return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
20361+ case SG_PIXELFORMAT_ETC2_RGBA8: return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
20362+ case SG_PIXELFORMAT_ETC2_SRGB8: return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK;
20363+ case SG_PIXELFORMAT_ETC2_SRGB8A8: return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK;
20364+ case SG_PIXELFORMAT_EAC_R11: return VK_FORMAT_EAC_R11_UNORM_BLOCK;
20365+ case SG_PIXELFORMAT_EAC_R11SN: return VK_FORMAT_EAC_R11_SNORM_BLOCK;
20366+ case SG_PIXELFORMAT_EAC_RG11: return VK_FORMAT_EAC_R11G11_UNORM_BLOCK;
20367+ case SG_PIXELFORMAT_EAC_RG11SN: return VK_FORMAT_EAC_R11G11_SNORM_BLOCK;
20368+ case SG_PIXELFORMAT_ASTC_4x4_RGBA: return VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
20369+ case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return VK_FORMAT_ASTC_4x4_SRGB_BLOCK;
20370+ default: return VK_FORMAT_UNDEFINED;
20371+ };
20372+}
20373+
20374+_SOKOL_PRIVATE VkPrimitiveTopology _sg_vk_primitive_topology(sg_primitive_type t) {
20375+ switch (t) {
20376+ case SG_PRIMITIVETYPE_POINTS: return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
20377+ case SG_PRIMITIVETYPE_LINES: return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
20378+ case SG_PRIMITIVETYPE_LINE_STRIP: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
20379+ case SG_PRIMITIVETYPE_TRIANGLES: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
20380+ case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
20381+ default:
20382+ SOKOL_UNREACHABLE;
20383+ return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
20384+ }
20385+}
20386+
20387+_SOKOL_PRIVATE VkCullModeFlags _sg_vk_cullmode(sg_cull_mode cm) {
20388+ switch (cm) {
20389+ case SG_CULLMODE_NONE: return VK_CULL_MODE_NONE;
20390+ case SG_CULLMODE_FRONT: return VK_CULL_MODE_FRONT_BIT;
20391+ case SG_CULLMODE_BACK: return VK_CULL_MODE_BACK_BIT;
20392+ default:
20393+ SOKOL_UNREACHABLE;
20394+ return VK_CULL_MODE_NONE;
20395+ }
20396+}
20397+
20398+_SOKOL_PRIVATE VkFrontFace _sg_vk_frontface(sg_face_winding fw) {
20399+ return (fw == SG_FACEWINDING_CCW) ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE;
20400+}
20401+
20402+_SOKOL_PRIVATE VkCompareOp _sg_vk_compare_op(sg_compare_func f) {
20403+ switch (f) {
20404+ case SG_COMPAREFUNC_NEVER: return VK_COMPARE_OP_NEVER;
20405+ case SG_COMPAREFUNC_LESS: return VK_COMPARE_OP_LESS;
20406+ case SG_COMPAREFUNC_EQUAL: return VK_COMPARE_OP_EQUAL;
20407+ case SG_COMPAREFUNC_LESS_EQUAL: return VK_COMPARE_OP_LESS_OR_EQUAL;
20408+ case SG_COMPAREFUNC_GREATER: return VK_COMPARE_OP_GREATER;
20409+ case SG_COMPAREFUNC_NOT_EQUAL: return VK_COMPARE_OP_NOT_EQUAL;
20410+ case SG_COMPAREFUNC_GREATER_EQUAL: return VK_COMPARE_OP_GREATER_OR_EQUAL;
20411+ case SG_COMPAREFUNC_ALWAYS: return VK_COMPARE_OP_ALWAYS;
20412+ default:
20413+ SOKOL_UNREACHABLE;
20414+ return VK_COMPARE_OP_ALWAYS;
20415+ }
20416+}
20417+
20418+_SOKOL_PRIVATE VkStencilOp _sg_vk_stencil_op(sg_stencil_op op) {
20419+ switch (op) {
20420+ case SG_STENCILOP_KEEP: return VK_STENCIL_OP_KEEP;
20421+ case SG_STENCILOP_ZERO: return VK_STENCIL_OP_ZERO;
20422+ case SG_STENCILOP_REPLACE: return VK_STENCIL_OP_REPLACE;
20423+ case SG_STENCILOP_INCR_CLAMP: return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
20424+ case SG_STENCILOP_DECR_CLAMP: return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
20425+ case SG_STENCILOP_INVERT: return VK_STENCIL_OP_INVERT;
20426+ case SG_STENCILOP_INCR_WRAP: return VK_STENCIL_OP_INCREMENT_AND_WRAP;
20427+ case SG_STENCILOP_DECR_WRAP: return VK_STENCIL_OP_DECREMENT_AND_WRAP;
20428+ default:
20429+ SOKOL_UNREACHABLE;
20430+ return VK_STENCIL_OP_KEEP;
20431+ }
20432+}
20433+
20434+_SOKOL_PRIVATE VkBlendOp _sg_vk_blend_op(sg_blend_op op) {
20435+ switch (op) {
20436+ case SG_BLENDOP_ADD: return VK_BLEND_OP_ADD;
20437+ case SG_BLENDOP_SUBTRACT: return VK_BLEND_OP_SUBTRACT;
20438+ case SG_BLENDOP_REVERSE_SUBTRACT: return VK_BLEND_OP_REVERSE_SUBTRACT;
20439+ case SG_BLENDOP_MIN: return VK_BLEND_OP_MIN;
20440+ case SG_BLENDOP_MAX: return VK_BLEND_OP_MAX;
20441+ default:
20442+ SOKOL_UNREACHABLE;
20443+ return VK_BLEND_OP_ADD;
20444+ }
20445+}
20446+
20447+_SOKOL_PRIVATE VkBlendFactor _sg_vk_blend_factor(sg_blend_factor f) {
20448+ switch (f) {
20449+ case SG_BLENDFACTOR_ZERO: return VK_BLEND_FACTOR_ZERO;
20450+ case SG_BLENDFACTOR_ONE: return VK_BLEND_FACTOR_ONE;
20451+ case SG_BLENDFACTOR_SRC_COLOR: return VK_BLEND_FACTOR_SRC_COLOR;
20452+ case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
20453+ case SG_BLENDFACTOR_SRC_ALPHA: return VK_BLEND_FACTOR_SRC_ALPHA;
20454+ case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
20455+ case SG_BLENDFACTOR_DST_COLOR: return VK_BLEND_FACTOR_DST_COLOR;
20456+ case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
20457+ case SG_BLENDFACTOR_DST_ALPHA: return VK_BLEND_FACTOR_DST_ALPHA;
20458+ case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
20459+ case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
20460+ case SG_BLENDFACTOR_BLEND_COLOR: return VK_BLEND_FACTOR_CONSTANT_COLOR;
20461+ case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
20462+ case SG_BLENDFACTOR_BLEND_ALPHA: return VK_BLEND_FACTOR_CONSTANT_ALPHA;
20463+ case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
20464+ case SG_BLENDFACTOR_SRC1_COLOR: return VK_BLEND_FACTOR_SRC1_COLOR ;
20465+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
20466+ case SG_BLENDFACTOR_SRC1_ALPHA: return VK_BLEND_FACTOR_SRC1_ALPHA;
20467+ case SG_BLENDFACTOR_ONE_MINUS_SRC1_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
20468+ default:
20469+ SOKOL_UNREACHABLE;
20470+ return VK_BLEND_FACTOR_ONE;
20471+ }
20472+}
20473+
20474+_SOKOL_PRIVATE VkColorComponentFlags _sg_vk_color_write_mask(sg_color_mask m) {
20475+ int res = 0;
20476+ if (0 != (m & SG_COLORMASK_R)) {
20477+ res |= (int)VK_COLOR_COMPONENT_R_BIT;
20478+ }
20479+ if (0 != (m & SG_COLORMASK_G)) {
20480+ res |= (int)VK_COLOR_COMPONENT_G_BIT;
20481+ }
20482+ if (0 != (m & SG_COLORMASK_B)) {
20483+ res |= (int)VK_COLOR_COMPONENT_B_BIT;
20484+ }
20485+ if (0 != (m & SG_COLORMASK_A)) {
20486+ res |= (int)VK_COLOR_COMPONENT_A_BIT;
20487+ }
20488+ return (VkColorComponentFlags)res;
20489+}
20490+
20491+_SOKOL_PRIVATE VkShaderStageFlags _sg_vk_shader_stage(sg_shader_stage s) {
20492+ switch (s) {
20493+ case SG_SHADERSTAGE_VERTEX: return VK_SHADER_STAGE_VERTEX_BIT;
20494+ case SG_SHADERSTAGE_FRAGMENT: return VK_SHADER_STAGE_FRAGMENT_BIT;
20495+ case SG_SHADERSTAGE_COMPUTE: return VK_SHADER_STAGE_COMPUTE_BIT;
20496+ default: SOKOL_UNREACHABLE; return 0;
20497+ }
20498+}
20499+
20500+_SOKOL_PRIVATE VkAttachmentLoadOp _sg_vk_load_op(sg_load_action a) {
20501+ switch (a) {
20502+ case SG_LOADACTION_CLEAR:
20503+ return VK_ATTACHMENT_LOAD_OP_CLEAR;
20504+ case SG_LOADACTION_DONTCARE:
20505+ return VK_ATTACHMENT_LOAD_OP_DONT_CARE;
20506+ default:
20507+ return VK_ATTACHMENT_LOAD_OP_LOAD;
20508+ }
20509+}
20510+
20511+_SOKOL_PRIVATE VkAttachmentStoreOp _sg_vk_store_op(sg_store_action a) {
20512+ switch (a) {
20513+ case SG_STOREACTION_STORE:
20514+ return VK_ATTACHMENT_STORE_OP_STORE;
20515+ default:
20516+ return VK_ATTACHMENT_STORE_OP_DONT_CARE;
20517+ }
20518+}
20519+
20520+_SOKOL_PRIVATE VkIndexType _sg_vk_index_type(sg_index_type t) {
20521+ return (t == SG_INDEXTYPE_UINT16) ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32;
20522+}
20523+
20524+_SOKOL_PRIVATE VkImageViewType _sg_vk_texture_image_view_type(sg_image_type t) {
20525+ switch (t) {
20526+ case SG_IMAGETYPE_2D: return VK_IMAGE_VIEW_TYPE_2D;
20527+ case SG_IMAGETYPE_CUBE: return VK_IMAGE_VIEW_TYPE_CUBE;
20528+ case SG_IMAGETYPE_3D: return VK_IMAGE_VIEW_TYPE_3D;
20529+ case SG_IMAGETYPE_ARRAY: return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
20530+ default: SOKOL_UNREACHABLE; return VK_IMAGE_VIEW_TYPE_2D;
20531+ }
20532+}
20533+
20534+_SOKOL_PRIVATE VkImageViewType _sg_vk_attachment_image_view_type(sg_image_type t) {
20535+ switch (t) {
20536+ case SG_IMAGETYPE_2D: return VK_IMAGE_VIEW_TYPE_2D;
20537+ case SG_IMAGETYPE_CUBE: return VK_IMAGE_VIEW_TYPE_2D_ARRAY; // not a bug
20538+ case SG_IMAGETYPE_3D: return VK_IMAGE_VIEW_TYPE_2D; // not a bug
20539+ case SG_IMAGETYPE_ARRAY: return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
20540+ default: SOKOL_UNREACHABLE; return VK_IMAGE_VIEW_TYPE_2D;
20541+ }
20542+}
20543+
20544+_SOKOL_PRIVATE VkFilter _sg_vk_sampler_minmag_filter(sg_filter f) {
20545+ switch (f) {
20546+ case SG_FILTER_NEAREST: return VK_FILTER_NEAREST;
20547+ case SG_FILTER_LINEAR: return VK_FILTER_LINEAR;
20548+ default: SOKOL_UNREACHABLE; return VK_FILTER_NEAREST;
20549+ }
20550+}
20551+
20552+_SOKOL_PRIVATE VkSamplerMipmapMode _sg_vk_sampler_mipmap_mode(sg_filter f) {
20553+ switch (f) {
20554+ case SG_FILTER_NEAREST: return VK_SAMPLER_MIPMAP_MODE_NEAREST;
20555+ case SG_FILTER_LINEAR: return VK_SAMPLER_MIPMAP_MODE_LINEAR;
20556+ default: SOKOL_UNREACHABLE; return VK_SAMPLER_MIPMAP_MODE_NEAREST;
20557+ }
20558+}
20559+
20560+_SOKOL_PRIVATE VkSamplerAddressMode _sg_vk_sampler_address_mode(sg_wrap w) {
20561+ switch (w) {
20562+ case SG_WRAP_REPEAT: return VK_SAMPLER_ADDRESS_MODE_REPEAT;
20563+ case SG_WRAP_CLAMP_TO_EDGE: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
20564+ case SG_WRAP_CLAMP_TO_BORDER: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
20565+ case SG_WRAP_MIRRORED_REPEAT: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
20566+ default: SOKOL_UNREACHABLE; return VK_SAMPLER_ADDRESS_MODE_REPEAT;
20567+ }
20568+}
20569+
20570+_SOKOL_PRIVATE VkBorderColor _sg_vk_sampler_border_color(sg_border_color c) {
20571+ switch (c) {
20572+ case SG_BORDERCOLOR_TRANSPARENT_BLACK: return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
20573+ case SG_BORDERCOLOR_OPAQUE_BLACK: return VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
20574+ case SG_BORDERCOLOR_OPAQUE_WHITE: return VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
20575+ default: SOKOL_UNREACHABLE; return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
20576+ }
20577+}
20578+
20579+_SOKOL_PRIVATE void _sg_vk_load_ext_funcs(void) {
20580+ SOKOL_ASSERT(_sg.vk.dev);
20581+ #if defined(SOKOL_DEBUG)
20582+ _sg.vk.ext.set_debug_utils_object_name_ext = (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(_sg.vk.instance, "vkSetDebugUtilsObjectNameEXT");
20583+ if (0 == _sg.vk.ext.set_debug_utils_object_name_ext) {
20584+ _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING);
20585+ }
20586+ #endif
20587+ _sg.vk.ext.get_descriptor_set_layout_size = (PFN_vkGetDescriptorSetLayoutSizeEXT)vkGetDeviceProcAddr(_sg.vk.dev, "vkGetDescriptorSetLayoutSizeEXT");
20588+ if (0 == _sg.vk.ext.get_descriptor_set_layout_size) {
20589+ _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING);
20590+ }
20591+ _sg.vk.ext.get_descriptor_set_layout_binding_offset = (PFN_vkGetDescriptorSetLayoutBindingOffsetEXT)vkGetDeviceProcAddr(_sg.vk.dev, "vkGetDescriptorSetLayoutBindingOffsetEXT");
20592+ if (0 == _sg.vk.ext.get_descriptor_set_layout_binding_offset) {
20593+ _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING);
20594+ }
20595+ _sg.vk.ext.get_descriptor = (PFN_vkGetDescriptorEXT)vkGetDeviceProcAddr(_sg.vk.dev, "vkGetDescriptorEXT");
20596+ if (0 == _sg.vk.ext.get_descriptor) {
20597+ _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING);
20598+ }
20599+ _sg.vk.ext.cmd_bind_descriptor_buffers = (PFN_vkCmdBindDescriptorBuffersEXT)vkGetDeviceProcAddr(_sg.vk.dev, "vkCmdBindDescriptorBuffersEXT");
20600+ if (0 == _sg.vk.ext.cmd_bind_descriptor_buffers) {
20601+ _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING);
20602+ }
20603+ _sg.vk.ext.cmd_set_descriptor_buffer_offsets = (PFN_vkCmdSetDescriptorBufferOffsetsEXT)vkGetDeviceProcAddr(_sg.vk.dev, "vkCmdSetDescriptorBufferOffsetsEXT");
20604+ if (0 == _sg.vk.ext.cmd_set_descriptor_buffer_offsets) {
20605+ _SG_PANIC(VULKAN_REQUIRED_EXTENSION_FUNCTION_MISSING);
20606+ }
20607+}
20608+
20609+_SOKOL_PRIVATE void _sg_vk_init_caps(void) {
20610+ _sg.backend = SG_BACKEND_VULKAN;
20611+ _sg.features.origin_top_left = true;
20612+ _sg.features.image_clamp_to_border = false; // FIXME?
20613+ _sg.features.mrt_independent_blend_state = true;
20614+ _sg.features.mrt_independent_write_mask = true;
20615+ _sg.features.compute = true;
20616+ _sg.features.msaa_texture_bindings = true;
20617+ _sg.features.draw_base_vertex = true;
20618+ _sg.features.draw_base_instance = true;
20619+ _sg.features.dual_source_blending = true;
20620+ _sg.features.vertexformat_int10_n2 = true;
20621+
20622+ SOKOL_ASSERT(_sg.vk.phys_dev);
20623+ _sg.vk.descriptor_buffer_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT;
20624+ _sg.vk.dev_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
20625+ _sg.vk.dev_props.pNext = &_sg.vk.descriptor_buffer_props;
20626+ vkGetPhysicalDeviceProperties2(_sg.vk.phys_dev, &_sg.vk.dev_props);
20627+ _sg.vk.dev_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
20628+ vkGetPhysicalDeviceFeatures2(_sg.vk.phys_dev, &_sg.vk.dev_features);
20629+
20630+ const VkPhysicalDeviceLimits* l = &_sg.vk.dev_props.properties.limits;
20631+ _sg.limits.max_image_size_2d = (int)l->maxImageDimension2D;
20632+ _sg.limits.max_image_size_cube = (int)l->maxImageDimensionCube;
20633+ _sg.limits.max_image_size_3d = (int)l->maxImageDimension3D;
20634+ _sg.limits.max_image_size_array = _sg.limits.max_image_size_2d;
20635+ _sg.limits.max_image_array_layers = (int)l->maxImageArrayLayers;
20636+ _sg.limits.max_vertex_attrs = _sg_min((int)l->maxVertexInputAttributes, SG_MAX_VERTEX_ATTRIBUTES);
20637+ _sg.limits.max_color_attachments = _sg_min((int)l->maxFragmentOutputAttachments, SG_MAX_COLOR_ATTACHMENTS);
20638+ _sg.limits.max_texture_bindings_per_stage = _sg_min((int)l->maxPerStageDescriptorSampledImages, SG_MAX_VIEW_BINDSLOTS);
20639+ _sg.limits.max_storage_buffer_bindings_per_stage = _sg_min((int)l->maxPerStageDescriptorStorageBuffers, SG_MAX_VIEW_BINDSLOTS);
20640+ _sg.limits.max_storage_image_bindings_per_stage = _sg_min((int)l->maxPerStageDescriptorStorageImages, SG_MAX_VIEW_BINDSLOTS);
20641+ _sg.limits.vk_min_uniform_buffer_offset_alignment = (int)l->minUniformBufferOffsetAlignment;
20642+
20643+ _SG_STRUCT(VkPhysicalDeviceImageFormatInfo2, fmt_info);
20644+ fmt_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
20645+ fmt_info.type = VK_IMAGE_TYPE_2D;
20646+ fmt_info.tiling = VK_IMAGE_TILING_OPTIMAL;
20647+ _SG_STRUCT(VkImageFormatProperties2, props2);
20648+ props2.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
20649+ for (int fmt = (SG_PIXELFORMAT_NONE+1); fmt < _SG_PIXELFORMAT_NUM; fmt++) {
20650+ _SG_STRUCT(VkFormatProperties, props);
20651+ VkFormat vkfmt = _sg_vk_format((sg_pixel_format)fmt);
20652+ vkGetPhysicalDeviceFormatProperties(_sg.vk.phys_dev, vkfmt, &props);
20653+ const VkFormatFeatureFlags f = props.optimalTilingFeatures;
20654+ _sg_pixelformat_info_t* info = &_sg.formats[fmt];
20655+ info->sample = 0 != (f & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
20656+ info->filter = 0 != (f & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT);
20657+ info->render = 0 != (f & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
20658+ info->blend = 0 != (f & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT);
20659+ info->depth = 0 != (f & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
20660+ info->read = info->write = 0 != (f & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT);
20661+ if (info->depth) {
20662+ info->render = true;
20663+ }
20664+ if (info->render || info->depth) {
20665+ // query msaa support
20666+ fmt_info.format = vkfmt;
20667+ fmt_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
20668+ if (info->depth) {
20669+ fmt_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
20670+ } else {
20671+ fmt_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
20672+ }
20673+ VkResult res = vkGetPhysicalDeviceImageFormatProperties2(_sg.vk.phys_dev, &fmt_info, &props2);
20674+ if (res == VK_SUCCESS) {
20675+ info->msaa = props2.imageFormatProperties.sampleCounts > VK_SAMPLE_COUNT_1_BIT;
20676+ }
20677+ }
20678+ }
20679+}
20680+
20681+_SOKOL_PRIVATE void _sg_vk_create_fences(void) {
20682+ SOKOL_ASSERT(_sg.vk.dev);
20683+ _SG_STRUCT(VkFenceCreateInfo, create_info);
20684+ create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
20685+ create_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
20686+ for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
20687+ SOKOL_ASSERT(0 == _sg.vk.frame.slot[i].fence);
20688+ VkResult res = vkCreateFence(_sg.vk.dev, &create_info, 0, &_sg.vk.frame.slot[i].fence);
20689+ SOKOL_ASSERT((res == VK_SUCCESS) && _sg.vk.frame.slot[i].fence); _SOKOL_UNUSED(res);
20690+ }
20691+}
20692+
20693+_SOKOL_PRIVATE void _sg_vk_destroy_fences(void) {
20694+ SOKOL_ASSERT(_sg.vk.dev);
20695+ for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
20696+ SOKOL_ASSERT(_sg.vk.frame.slot[i].fence);
20697+ vkDestroyFence(_sg.vk.dev, _sg.vk.frame.slot[i].fence, 0);
20698+ _sg.vk.frame.slot[i].fence = 0;
20699+ }
20700+}
20701+
20702+_SOKOL_PRIVATE void _sg_vk_create_frame_command_pool_and_buffers(void) {
20703+ SOKOL_ASSERT(_sg.vk.dev);
20704+ SOKOL_ASSERT(0 == _sg.vk.frame.cmd_pool);
20705+ _SG_STRUCT(VkCommandPoolCreateInfo, pool_create_info);
20706+ pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
20707+ // FIXME: transient bit when the cmd buffers are reset each frame?
20708+ pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
20709+ pool_create_info.queueFamilyIndex = _sg.vk.queue_family_index;
20710+ VkResult res = vkCreateCommandPool(_sg.vk.dev, &pool_create_info, 0, &_sg.vk.frame.cmd_pool);
20711+ SOKOL_ASSERT((res == VK_SUCCESS) && _sg.vk.frame.cmd_pool); _SOKOL_UNUSED(res);
20712+
20713+ for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
20714+ _SG_STRUCT(VkCommandBufferAllocateInfo, cmdbuf_alloc_info);
20715+ cmdbuf_alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
20716+ cmdbuf_alloc_info.commandPool = _sg.vk.frame.cmd_pool;
20717+ cmdbuf_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
20718+ cmdbuf_alloc_info.commandBufferCount = 1;
20719+ res = vkAllocateCommandBuffers(_sg.vk.dev, &cmdbuf_alloc_info, &_sg.vk.frame.slot[i].command_buffer);
20720+ SOKOL_ASSERT((res == VK_SUCCESS) && _sg.vk.frame.slot[i].command_buffer);
20721+ res = vkAllocateCommandBuffers(_sg.vk.dev, &cmdbuf_alloc_info, &_sg.vk.frame.slot[i].stream_command_buffer);
20722+ SOKOL_ASSERT((res == VK_SUCCESS) && _sg.vk.frame.slot[i].stream_command_buffer);
20723+ }
20724+}
20725+
20726+_SOKOL_PRIVATE void _sg_vk_destroy_frame_command_pool(void) {
20727+ SOKOL_ASSERT(_sg.vk.dev);
20728+ SOKOL_ASSERT(_sg.vk.frame.cmd_pool);
20729+ SOKOL_ASSERT(0 == _sg.vk.frame.cmd_buf);
20730+ SOKOL_ASSERT(0 == _sg.vk.frame.stream_cmd_buf);
20731+ // NOTE: command buffers owned by the pool will be automatically destroyed
20732+ vkDestroyCommandPool(_sg.vk.dev, _sg.vk.frame.cmd_pool, 0);
20733+ _sg.vk.frame.cmd_pool = 0;
20734+ for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
20735+ SOKOL_ASSERT(_sg.vk.frame.slot[i].command_buffer);
20736+ _sg.vk.frame.slot[i].command_buffer = 0;
20737+ _sg.vk.frame.slot[i].stream_command_buffer = 0;
20738+ }
20739+}
20740+
20741+_SOKOL_PRIVATE void _sg_vk_acquire_frame_command_buffers(void) {
20742+ SOKOL_ASSERT(_sg.vk.dev);
20743+ VkResult res;
20744+ if (0 == _sg.vk.frame.cmd_buf) {
20745+ SOKOL_ASSERT(0 == _sg.vk.frame.stream_cmd_buf);
20746+ _sg.vk.frame_slot = (_sg.vk.frame_slot + 1) % SG_NUM_INFLIGHT_FRAMES;
20747+ // block until oldest inflight-frame has finished
20748+ do {
20749+ res = vkWaitForFences(_sg.vk.dev,
20750+ 1,
20751+ &_sg.vk.frame.slot[_sg.vk.frame_slot].fence,
20752+ VK_TRUE,
20753+ UINT64_MAX);
20754+ } while (res == VK_TIMEOUT);
20755+ if (res != VK_SUCCESS) {
20756+ _SG_WARN(VULKAN_WAIT_FOR_FENCE_FAILED);
20757+ _sg.cur_pass.valid = false;
20758+ return;
20759+ }
20760+ res = vkResetFences(_sg.vk.dev, 1, &_sg.vk.frame.slot[_sg.vk.frame_slot].fence);
20761+ SOKOL_ASSERT(res == VK_SUCCESS); _SOKOL_UNUSED(res);
20762+
20763+ _sg_vk_delete_queue_collect();
20764+
20765+ _sg.vk.frame.cmd_buf = _sg.vk.frame.slot[_sg.vk.frame_slot].command_buffer;
20766+ res = vkResetCommandBuffer(_sg.vk.frame.cmd_buf, 0);
20767+ SOKOL_ASSERT(res == VK_SUCCESS);
20768+ _sg.vk.frame.stream_cmd_buf = _sg.vk.frame.slot[_sg.vk.frame_slot].stream_command_buffer;
20769+ res = vkResetCommandBuffer(_sg.vk.frame.stream_cmd_buf, 0);
20770+ SOKOL_ASSERT(res == VK_SUCCESS);
20771+
20772+ _SG_STRUCT(VkCommandBufferBeginInfo, cmdbuf_begin_info);
20773+ cmdbuf_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
20774+ cmdbuf_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
20775+ res = vkBeginCommandBuffer(_sg.vk.frame.cmd_buf, &cmdbuf_begin_info);
20776+ SOKOL_ASSERT(res == VK_SUCCESS);
20777+ res = vkBeginCommandBuffer(_sg.vk.frame.stream_cmd_buf, &cmdbuf_begin_info);
20778+ SOKOL_ASSERT(res == VK_SUCCESS);
20779+
20780+ _sg_vk_uniform_after_acquire();
20781+ _sg_vk_bind_after_acquire();
20782+ _sg_vk_staging_stream_after_acquire();
20783+ }
20784+ SOKOL_ASSERT(_sg.vk.frame.cmd_buf);
20785+}
20786+
20787+_SOKOL_PRIVATE void _sg_vk_submit_frame_command_buffers(void) {
20788+ SOKOL_ASSERT(_sg.vk.frame.cmd_buf);
20789+ SOKOL_ASSERT(_sg.vk.frame.stream_cmd_buf);
20790+ VkResult res;
20791+ _SOKOL_UNUSED(res);
20792+
20793+ _sg_vk_staging_stream_before_submit();
20794+ _sg_vk_bind_before_submit();
20795+ _sg_vk_uniform_before_submit();
20796+
20797+ res = vkEndCommandBuffer(_sg.vk.frame.stream_cmd_buf);
20798+ SOKOL_ASSERT(res == VK_SUCCESS);
20799+ res = vkEndCommandBuffer(_sg.vk.frame.cmd_buf);
20800+ SOKOL_ASSERT(res == VK_SUCCESS);
20801+
20802+ _SG_STRUCT(VkSubmitInfo, submit_infos[2]);
20803+ // streaming-update command buffer
20804+ submit_infos[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
20805+ submit_infos[0].commandBufferCount = 1;
20806+ submit_infos[0].pCommandBuffers = &_sg.vk.frame.stream_cmd_buf;
20807+ // render command buffer
20808+ const VkPipelineStageFlags present_wait_dst_stage_mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
20809+ submit_infos[1].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
20810+ // NOTE: the render_finished_sem and present_complete_sem are only
20811+ // provided in swapchain passes, but rendering without a swapchain
20812+ // pass should be a valid scenario (for instance for 'headless rendering')
20813+ //
20814+ // see: https://github.com/floooh/sokol/issues/1459
20815+ if (_sg.vk.present_complete_sem && _sg.vk.render_finished_sem) {
20816+ submit_infos[1].waitSemaphoreCount = 1;
20817+ submit_infos[1].pWaitSemaphores = &_sg.vk.present_complete_sem;
20818+ submit_infos[1].pWaitDstStageMask = &present_wait_dst_stage_mask;
20819+ submit_infos[1].signalSemaphoreCount = 1;
20820+ submit_infos[1].pSignalSemaphores = &_sg.vk.render_finished_sem;
20821+ }
20822+ submit_infos[1].commandBufferCount = 1;
20823+ submit_infos[1].pCommandBuffers = &_sg.vk.frame.cmd_buf;
20824+ res = vkQueueSubmit(_sg.vk.queue, 2, submit_infos, _sg.vk.frame.slot[_sg.vk.frame_slot].fence);
20825+ SOKOL_ASSERT(res == VK_SUCCESS);
20826+
20827+ _sg.vk.frame.cmd_buf = 0;
20828+ _sg.vk.frame.stream_cmd_buf = 0;
20829+
20830+ // NOTE: it's valid to register resource objects for destruction in the
20831+ // delete queue past this point (between _sg_vk_submit_frame_command_buffer()
20832+ // and the next _sg_vk_acquire_frame_command_buffer()) since resources which are
20833+ // destroyed in this 'gap' can at most have been used by the command
20834+ // buffer that was just submitted
20835+}
20836+
20837+_SOKOL_PRIVATE void _sg_vk_setup_backend(const sg_desc* desc) {
20838+ SOKOL_ASSERT(desc);
20839+ SOKOL_ASSERT(desc->environment.vulkan.instance);
20840+ SOKOL_ASSERT(desc->environment.vulkan.physical_device);
20841+ SOKOL_ASSERT(desc->environment.vulkan.device);
20842+ SOKOL_ASSERT(desc->environment.vulkan.queue);
20843+ SOKOL_ASSERT(desc->uniform_buffer_size > 0);
20844+ _sg.vk.valid = true;
20845+ _sg.vk.instance = (VkInstance) desc->environment.vulkan.instance;
20846+ _sg.vk.phys_dev = (VkPhysicalDevice) desc->environment.vulkan.physical_device;
20847+ _sg.vk.dev = (VkDevice) desc->environment.vulkan.device;
20848+ _sg.vk.queue = (VkQueue) desc->environment.vulkan.queue;
20849+ _sg.vk.queue_family_index = desc->environment.vulkan.queue_family_index;
20850+
20851+ _sg_track_init(&_sg.vk.track.buffers, _sg.pools.buffer_pool.size);
20852+ _sg_track_init(&_sg.vk.track.images, _sg.pools.image_pool.size);
20853+ _sg_vk_load_ext_funcs();
20854+ _sg_vk_init_caps();
20855+ _sg_vk_create_fences();
20856+ _sg_vk_create_frame_command_pool_and_buffers();
20857+ _sg_vk_staging_copy_init();
20858+ _sg_vk_staging_stream_init();
20859+ _sg_vk_uniform_init();
20860+ _sg_vk_bind_init();
20861+ _sg_vk_create_delete_queues();
20862+}
20863+
20864+_SOKOL_PRIVATE void _sg_vk_discard_backend(void) {
20865+ SOKOL_ASSERT(_sg.vk.valid);
20866+ SOKOL_ASSERT(_sg.vk.dev);
20867+ vkDeviceWaitIdle(_sg.vk.dev);
20868+ _sg_vk_destroy_delete_queues();
20869+ _sg_vk_bind_discard();
20870+ _sg_vk_uniform_discard();
20871+ _sg_vk_staging_stream_discard();
20872+ _sg_vk_staging_copy_discard();
20873+ _sg_vk_destroy_frame_command_pool();
20874+ _sg_vk_destroy_fences();
20875+ _sg_track_discard(&_sg.vk.track.images);
20876+ _sg_track_discard(&_sg.vk.track.buffers);
20877+ _sg.vk.valid = false;
20878+}
20879+
20880+_SOKOL_PRIVATE void _sg_vk_reset_state_cache(void) {
20881+ // nothing to do here
20882+}
20883+
20884+_SOKOL_PRIVATE sg_resource_state _sg_vk_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) {
20885+ SOKOL_ASSERT(_sg.vk.dev);
20886+ SOKOL_ASSERT(buf && desc);
20887+ SOKOL_ASSERT(buf->cmn.size > 0);
20888+ SOKOL_ASSERT(0 == buf->vk.buf);
20889+ SOKOL_ASSERT(0 == buf->vk.mem);
20890+ SOKOL_ASSERT(0 == buf->vk.dev_addr);
20891+ VkResult res;
20892+ // FIXME: inject external buffer
20893+
20894+ buf->vk.cur_access = _SG_VK_ACCESS_NONE;
20895+
20896+ _SG_STRUCT(VkBufferCreateInfo, create_info);
20897+ create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
20898+ create_info.size = (VkDeviceSize)buf->cmn.size;
20899+ create_info.usage = _sg_vk_buffer_usage(&buf->cmn.usage);
20900+ create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
20901+ res = vkCreateBuffer(_sg.vk.dev, &create_info, 0, &buf->vk.buf);
20902+ if (res != VK_SUCCESS) {
20903+ _SG_ERROR(VULKAN_CREATE_BUFFER_FAILED);
20904+ return SG_RESOURCESTATE_FAILED;
20905+ }
20906+ SOKOL_ASSERT(buf->vk.buf);
20907+ _sg_vk_set_object_label(VK_OBJECT_TYPE_BUFFER, (uint64_t)buf->vk.buf, desc->label);
20908+
20909+ if (!_sg_vk_mem_alloc_buffer_device_memory(buf)) {
20910+ return SG_RESOURCESTATE_FAILED;
20911+ }
20912+ SOKOL_ASSERT(buf->vk.mem);
20913+ res = vkBindBufferMemory(_sg.vk.dev, buf->vk.buf, buf->vk.mem, 0);
20914+ if (res != VK_SUCCESS) {
20915+ _SG_ERROR(VULKAN_BIND_BUFFER_MEMORY_FAILED);
20916+ return SG_RESOURCESTATE_FAILED;
20917+ }
20918+ if (buf->cmn.usage.storage_buffer) {
20919+ _SG_STRUCT(VkBufferDeviceAddressInfo, addr_info);
20920+ addr_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO;
20921+ addr_info.buffer = buf->vk.buf;
20922+ buf->vk.dev_addr = vkGetBufferDeviceAddress(_sg.vk.dev, &addr_info);
20923+ SOKOL_ASSERT(buf->vk.dev_addr);
20924+ }
20925+ if (buf->cmn.usage.immutable && desc->data.ptr) {
20926+ _sg_vk_staging_copy_buffer_data(buf, &desc->data, 0, false);
20927+ }
20928+ return SG_RESOURCESTATE_VALID;
20929+}
20930+
20931+_SOKOL_PRIVATE void _sg_vk_discard_buffer(_sg_buffer_t* buf) {
20932+ SOKOL_ASSERT(buf);
20933+ _sg_track_remove(&_sg.vk.track.buffers, buf->slot.id);
20934+ if (buf->vk.buf) {
20935+ _sg_vk_delete_queue_add(_sg_vk_buffer_destructor, (void*)buf->vk.buf);
20936+ buf->vk.buf = 0;
20937+ }
20938+ if (buf->vk.mem) {
20939+ _sg_vk_delete_queue_add(_sg_vk_memory_destructor, (void*)buf->vk.mem);
20940+ buf->vk.mem = 0;
20941+ }
20942+}
20943+
20944+_SOKOL_PRIVATE sg_resource_state _sg_vk_create_image(_sg_image_t* img, const sg_image_desc* desc) {
20945+ SOKOL_ASSERT(img && desc);
20946+ VkResult res;
20947+ // FIXME: injected images
20948+
20949+ img->vk.cur_access = _SG_VK_ACCESS_NONE;
20950+
20951+ _SG_STRUCT(VkImageCreateInfo, create_info);
20952+ create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
20953+ create_info.flags = _sg_vk_image_create_flags(img->cmn.type);
20954+ create_info.imageType = _sg_vk_image_type(img->cmn.type);
20955+ create_info.format = _sg_vk_format(desc->pixel_format);
20956+ create_info.extent.width = (uint32_t)img->cmn.width;
20957+ create_info.extent.height = (uint32_t)img->cmn.height;
20958+ if (desc->type == SG_IMAGETYPE_3D) {
20959+ create_info.extent.depth = (uint32_t)img->cmn.num_slices;
20960+ create_info.arrayLayers = 1;
20961+ } else {
20962+ create_info.extent.depth = 1;
20963+ create_info.arrayLayers = (uint32_t)img->cmn.num_slices;
20964+ }
20965+ create_info.mipLevels = (uint32_t)img->cmn.num_mipmaps;
20966+ create_info.samples = (VkSampleCountFlagBits)desc->sample_count;
20967+ create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
20968+ create_info.usage = _sg_vk_image_usage(&img->cmn.usage);
20969+ create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
20970+ create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
20971+ res = vkCreateImage(_sg.vk.dev, &create_info, 0, &img->vk.img);
20972+ if (res != VK_SUCCESS) {
20973+ _SG_ERROR(VULKAN_CREATE_IMAGE_FAILED);
20974+ return SG_RESOURCESTATE_FAILED;
20975+ }
20976+ SOKOL_ASSERT(img->vk.img);
20977+ _sg_vk_set_object_label(VK_OBJECT_TYPE_IMAGE, (uint64_t)img->vk.img, desc->label);
20978+
20979+ if (!_sg_vk_mem_alloc_image_device_memory(img)) {
20980+ return SG_RESOURCESTATE_FAILED;
20981+ }
20982+ SOKOL_ASSERT(img->vk.mem);
20983+ res = vkBindImageMemory(_sg.vk.dev, img->vk.img, img->vk.mem, 0);
20984+ if (res != VK_SUCCESS) {
20985+ _SG_ERROR(VULKAN_BIND_IMAGE_MEMORY_FAILED);
20986+ return SG_RESOURCESTATE_FAILED;
20987+ }
20988+ if (img->cmn.usage.immutable && desc->data.mip_levels[0].ptr) {
20989+ _sg_vk_staging_copy_image_data(img, &desc->data, false);
20990+ }
20991+ return SG_RESOURCESTATE_VALID;
20992+}
20993+
20994+_SOKOL_PRIVATE void _sg_vk_discard_image(_sg_image_t* img) {
20995+ SOKOL_ASSERT(img);
20996+ _sg_track_remove(&_sg.vk.track.images, img->slot.id);
20997+ if (img->vk.img) {
20998+ _sg_vk_delete_queue_add(_sg_vk_image_destructor, (void*)img->vk.img);
20999+ img->vk.img = 0;
21000+ }
21001+ if (img->vk.mem) {
21002+ _sg_vk_delete_queue_add(_sg_vk_memory_destructor, (void*)img->vk.mem);
21003+ img->vk.mem = 0;
21004+ }
21005+}
21006+
21007+_SOKOL_PRIVATE sg_resource_state _sg_vk_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) {
21008+ SOKOL_ASSERT(smp && desc);
21009+ SOKOL_ASSERT(_sg.vk.dev);
21010+ SOKOL_ASSERT(0 == smp->vk.smp);
21011+ // FIXME: injection
21012+
21013+ // create sampler object
21014+ _SG_STRUCT(VkSamplerCreateInfo, create_info);
21015+ create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
21016+ create_info.magFilter = _sg_vk_sampler_minmag_filter(desc->mag_filter);
21017+ create_info.minFilter = _sg_vk_sampler_minmag_filter(desc->min_filter);
21018+ create_info.mipmapMode = _sg_vk_sampler_mipmap_mode(desc->mipmap_filter);
21019+ create_info.addressModeU = _sg_vk_sampler_address_mode(desc->wrap_u);
21020+ create_info.addressModeV = _sg_vk_sampler_address_mode(desc->wrap_v);
21021+ create_info.addressModeW = _sg_vk_sampler_address_mode(desc->wrap_w);
21022+ create_info.mipLodBias = 0.0f;
21023+ if (desc->max_anisotropy > 1) {
21024+ create_info.anisotropyEnable = VK_TRUE;
21025+ create_info.maxAnisotropy = (float)desc->max_anisotropy;
21026+ }
21027+ if (desc->compare != SG_COMPAREFUNC_NEVER) {
21028+ create_info.compareEnable = VK_TRUE;
21029+ create_info.compareOp = _sg_vk_compare_op(desc->compare);
21030+ }
21031+ create_info.minLod = desc->min_lod;
21032+ create_info.maxLod = desc->max_lod;
21033+ create_info.borderColor = _sg_vk_sampler_border_color(desc->border_color);
21034+ VkResult res = vkCreateSampler(_sg.vk.dev, &create_info, 0, &smp->vk.smp);
21035+ if (res != VK_SUCCESS) {
21036+ _SG_ERROR(VULKAN_CREATE_SAMPLER_FAILED);
21037+ return SG_RESOURCESTATE_FAILED;
21038+ }
21039+ SOKOL_ASSERT(smp->vk.smp);
21040+ _sg_vk_set_object_label(VK_OBJECT_TYPE_SAMPLER, (uint64_t)smp->vk.smp, desc->label);
21041+
21042+ // record sampler descriptor data
21043+ smp->vk.descriptor_size = _sg.vk.descriptor_buffer_props.samplerDescriptorSize;
21044+ if (_SG_VK_MAX_DESCRIPTOR_DATA_SIZE < smp->vk.descriptor_size) {
21045+ _SG_ERROR(VULKAN_SAMPLER_MAX_DESCRIPTOR_SIZE);
21046+ return SG_RESOURCESTATE_FAILED;
21047+ }
21048+ _SG_STRUCT(VkDescriptorGetInfoEXT, get_info);
21049+ get_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT;
21050+ get_info.type = VK_DESCRIPTOR_TYPE_SAMPLER;
21051+ get_info.data.pSampler = &smp->vk.smp;
21052+ _sg.vk.ext.get_descriptor(_sg.vk.dev, &get_info, smp->vk.descriptor_size, &smp->vk.descriptor_data);
21053+
21054+ return SG_RESOURCESTATE_VALID;
21055+}
21056+
21057+_SOKOL_PRIVATE void _sg_vk_discard_sampler(_sg_sampler_t* smp) {
21058+ SOKOL_ASSERT(smp);
21059+ if (smp->vk.smp) {
21060+ _sg_vk_delete_queue_add(_sg_vk_sampler_destructor, (void*)smp->vk.smp);
21061+ smp->vk.smp = 0;
21062+ }
21063+}
21064+
21065+_SOKOL_PRIVATE _sg_vk_shader_func_t _sg_vk_create_shader_func(const sg_shader_function* func, const char* label) {
21066+ SOKOL_ASSERT(_sg.vk.dev);
21067+ SOKOL_ASSERT(func);
21068+ SOKOL_ASSERT(func->bytecode.ptr && (func->bytecode.size > 0));
21069+ SOKOL_ASSERT(func->entry);
21070+
21071+ _SG_STRUCT(_sg_vk_shader_func_t, vk_func);
21072+ _sg_strcpy(&vk_func.entry, func->entry);
21073+
21074+ _SG_STRUCT(VkShaderModuleCreateInfo, create_info);
21075+ create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
21076+ create_info.codeSize = func->bytecode.size;
21077+ create_info.pCode = (uint32_t*)func->bytecode.ptr;
21078+ VkResult res = vkCreateShaderModule(_sg.vk.dev, &create_info, 0, &vk_func.module);
21079+ if (VK_SUCCESS != res) {
21080+ _SG_ERROR(VULKAN_CREATE_SHADER_MODULE_FAILED);
21081+ } else {
21082+ SOKOL_ASSERT(vk_func.module);
21083+ _sg_vk_set_object_label(VK_OBJECT_TYPE_SHADER_MODULE, (uint64_t)vk_func.module, label);
21084+ }
21085+ return vk_func;
21086+}
21087+
21088+_SOKOL_PRIVATE void _sg_vk_discard_shader_func(_sg_vk_shader_func_t* func) {
21089+ SOKOL_ASSERT(_sg.vk.dev);
21090+ SOKOL_ASSERT(func);
21091+ if (func->module) {
21092+ _sg_vk_delete_queue_add(_sg_vk_shader_module_destructor, (void*)func->module);
21093+ func->module = 0;
21094+ }
21095+}
21096+
21097+_SOKOL_PRIVATE bool _sg_vk_ensure_spirv_bindslot_ranges(const sg_shader_desc* desc) {
21098+ SOKOL_ASSERT(desc);
21099+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
21100+ const sg_shader_uniform_block* ub = &desc->uniform_blocks[i];
21101+ if (ub->stage != SG_SHADERSTAGE_NONE) {
21102+ if (ub->spirv_set0_binding_n >= _SG_VK_MAX_UB_DESCRIPTORSET_SLOTS) {
21103+ _SG_ERROR(VULKAN_UNIFORMBLOCK_SPIRV_SET0_BINDING_OUT_OF_RANGE);
21104+ return false;
21105+ }
21106+ }
21107+ }
21108+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
21109+ const sg_shader_view* view = &desc->views[i];
21110+ if (view->texture.stage != SG_SHADERSTAGE_NONE) {
21111+ if (view->texture.spirv_set1_binding_n >= _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_SLOTS) {
21112+ _SG_ERROR(VULKAN_TEXTURE_SPIRV_SET1_BINDING_OUT_OF_RANGE);
21113+ return false;
21114+ }
21115+ }
21116+ if (view->storage_buffer.stage != SG_SHADERSTAGE_NONE) {
21117+ if (view->storage_buffer.spirv_set1_binding_n >= _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_SLOTS) {
21118+ _SG_ERROR(VULKAN_STORAGEBUFFER_SPIRV_SET1_BINDING_OUT_OF_RANGE);
21119+ return false;
21120+ }
21121+ }
21122+ if (view->storage_image.stage != SG_SHADERSTAGE_NONE) {
21123+ if (view->storage_image.spirv_set1_binding_n >= _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_SLOTS) {
21124+ _SG_ERROR(VULKAN_STORAGEIMAGE_SPIRV_SET1_BINDING_OUT_OF_RANGE);
21125+ return false;
21126+ }
21127+ }
21128+ }
21129+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
21130+ const sg_shader_sampler* smp = &desc->samplers[i];
21131+ if (smp->stage != SG_SHADERSTAGE_NONE) {
21132+ if (smp->spirv_set1_binding_n >= _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_SLOTS) {
21133+ _SG_ERROR(VULKAN_SAMPLER_SPIRV_SET1_BINDING_OUT_OF_RANGE);
21134+ return false;
21135+ }
21136+ }
21137+ }
21138+ return true;
21139+}
21140+
21141+_SOKOL_PRIVATE sg_resource_state _sg_vk_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) {
21142+ SOKOL_ASSERT(shd && desc);
21143+ SOKOL_ASSERT(_sg.vk.dev);
21144+ SOKOL_ASSERT(shd->vk.vertex_func.module == 0);
21145+ SOKOL_ASSERT(shd->vk.fragment_func.module == 0);
21146+ SOKOL_ASSERT(shd->vk.compute_func.module == 0);
21147+ SOKOL_ASSERT(shd->vk.ub_dsl == 0);
21148+ SOKOL_ASSERT(shd->vk.view_smp_dsl == 0);
21149+
21150+ if (!_sg_vk_ensure_spirv_bindslot_ranges(desc)) {
21151+ return SG_RESOURCESTATE_FAILED;
21152+ }
21153+
21154+ // build shader modules
21155+ bool shd_valid = true;
21156+ if (desc->vertex_func.bytecode.ptr) {
21157+ shd->vk.vertex_func = _sg_vk_create_shader_func(&desc->vertex_func, desc->label);
21158+ shd_valid &= shd->vk.vertex_func.module != 0;
21159+ }
21160+ if (desc->fragment_func.bytecode.ptr) {
21161+ shd->vk.fragment_func = _sg_vk_create_shader_func(&desc->fragment_func, desc->label);
21162+ shd_valid &= shd->vk.fragment_func.module != 0;
21163+ }
21164+ if (desc->compute_func.bytecode.ptr) {
21165+ shd->vk.compute_func = _sg_vk_create_shader_func(&desc->compute_func, desc->label);
21166+ shd_valid &= shd->vk.compute_func.module != 0;
21167+ }
21168+ if (!shd_valid) {
21169+ _sg_vk_discard_shader_func(&shd->vk.vertex_func);
21170+ _sg_vk_discard_shader_func(&shd->vk.fragment_func);
21171+ _sg_vk_discard_shader_func(&shd->vk.compute_func);
21172+ return SG_RESOURCESTATE_FAILED;
21173+ }
21174+
21175+ // descriptor set layouts and pipeline layout
21176+ VkResult res;
21177+ _SG_STRUCT(VkDescriptorSetLayoutBinding, dsl_entries[_SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_ENTRIES]);
21178+ _SG_STRUCT(VkDescriptorSetLayoutCreateInfo, dsl_create_info);
21179+ uint32_t dsl_index = 0;
21180+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
21181+ if (shd->cmn.uniform_blocks[i].stage == SG_SHADERSTAGE_NONE) {
21182+ continue;
21183+ }
21184+ shd->vk.ub_set0_bnd_n[i] = desc->uniform_blocks[i].spirv_set0_binding_n;
21185+ VkDescriptorSetLayoutBinding* dsl_entry = &dsl_entries[dsl_index];
21186+ dsl_entry->binding = shd->vk.ub_set0_bnd_n[i];
21187+ dsl_entry->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
21188+ dsl_entry->descriptorCount = 1;
21189+ dsl_entry->stageFlags = _sg_vk_shader_stage(shd->cmn.uniform_blocks[i].stage);
21190+ dsl_index += 1;
21191+ }
21192+ dsl_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
21193+ dsl_create_info.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
21194+ dsl_create_info.bindingCount = dsl_index;
21195+ dsl_create_info.pBindings = dsl_entries;
21196+ res = vkCreateDescriptorSetLayout(_sg.vk.dev, &dsl_create_info, 0, &shd->vk.ub_dsl);
21197+ if (res != VK_SUCCESS) {
21198+ _SG_ERROR(VULKAN_CREATE_DESCRIPTOR_SET_LAYOUT_FAILED);
21199+ return SG_RESOURCESTATE_FAILED;
21200+ }
21201+
21202+ // store uniform descriptor set size and descriptor offsets
21203+ _sg.vk.ext.get_descriptor_set_layout_size(_sg.vk.dev, shd->vk.ub_dsl, &shd->vk.ub_dset_size);
21204+ if (shd->vk.ub_dset_size > _sg.vk.uniforms.dset_cache_size) {
21205+ _SG_ERROR(VULKAN_SHADER_UNIFORM_DESCRIPTOR_SET_SIZE_VS_CACHE_SIZE);
21206+ return SG_RESOURCESTATE_FAILED;
21207+ }
21208+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
21209+ if (shd->cmn.uniform_blocks[i].stage == SG_SHADERSTAGE_NONE) {
21210+ continue;
21211+ }
21212+ const uint8_t vk_bnd = shd->vk.ub_set0_bnd_n[i];
21213+ VkDeviceSize dset_offset = 0;
21214+ _sg.vk.ext.get_descriptor_set_layout_binding_offset(_sg.vk.dev, shd->vk.ub_dsl, vk_bnd, &dset_offset);
21215+ shd->vk.ub_dset_offsets[i] = (uint16_t)dset_offset;
21216+ }
21217+
21218+ _sg_clear(dsl_entries, sizeof(dsl_entries));
21219+ _sg_clear(&dsl_create_info, sizeof(dsl_create_info));
21220+ dsl_index = 0;
21221+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
21222+ if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) {
21223+ continue;
21224+ }
21225+ SOKOL_ASSERT(dsl_index < _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_ENTRIES);
21226+ VkDescriptorSetLayoutBinding* dsl_entry = &dsl_entries[dsl_index];
21227+ dsl_entry->stageFlags = _sg_vk_shader_stage(shd->cmn.views[i].stage);
21228+ if (shd->cmn.views[i].view_type == SG_VIEWTYPE_TEXTURE) {
21229+ shd->vk.view_set1_bnd_n[i] = desc->views[i].texture.spirv_set1_binding_n;
21230+ dsl_entry->descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
21231+ } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEBUFFER) {
21232+ shd->vk.view_set1_bnd_n[i] = desc->views[i].storage_buffer.spirv_set1_binding_n;
21233+ dsl_entry->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
21234+ } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEIMAGE) {
21235+ shd->vk.view_set1_bnd_n[i] = desc->views[i].storage_image.spirv_set1_binding_n;
21236+ dsl_entry->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
21237+ } else {
21238+ SOKOL_UNREACHABLE;
21239+ }
21240+ dsl_entry->binding = shd->vk.view_set1_bnd_n[i];
21241+ dsl_entry->descriptorCount = 1;
21242+ dsl_index += 1;
21243+ }
21244+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
21245+ if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) {
21246+ continue;
21247+ }
21248+ shd->vk.smp_set1_bnd_n[i] = desc->samplers[i].spirv_set1_binding_n;
21249+ SOKOL_ASSERT(dsl_index < _SG_VK_MAX_VIEW_SMP_DESCRIPTORSET_ENTRIES);
21250+ VkDescriptorSetLayoutBinding* dsl_entry = &dsl_entries[dsl_index];
21251+ dsl_entry->binding = shd->vk.smp_set1_bnd_n[i];
21252+ dsl_entry->descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
21253+ dsl_entry->descriptorCount = 1;
21254+ dsl_entry->stageFlags = _sg_vk_shader_stage(shd->cmn.samplers[i].stage);
21255+ dsl_index += 1;
21256+ }
21257+ dsl_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
21258+ dsl_create_info.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
21259+ dsl_create_info.bindingCount = dsl_index;
21260+ dsl_create_info.pBindings = dsl_entries;
21261+ res = vkCreateDescriptorSetLayout(_sg.vk.dev, &dsl_create_info, 0, &shd->vk.view_smp_dsl);
21262+ if (res != VK_SUCCESS) {
21263+ _SG_ERROR(VULKAN_CREATE_DESCRIPTOR_SET_LAYOUT_FAILED);
21264+ return SG_RESOURCESTATE_FAILED;
21265+ }
21266+
21267+ // store view/smp descriptor set size and descriptor offsets
21268+ _sg.vk.ext.get_descriptor_set_layout_size(_sg.vk.dev, shd->vk.view_smp_dsl, &shd->vk.view_smp_dset_size);
21269+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
21270+ if (shd->cmn.views[i].stage == SG_SHADERSTAGE_NONE) {
21271+ continue;
21272+ }
21273+ const uint8_t vk_bnd = shd->vk.view_set1_bnd_n[i];
21274+ VkDeviceSize dset_offset = 0;
21275+ _sg.vk.ext.get_descriptor_set_layout_binding_offset(_sg.vk.dev, shd->vk.view_smp_dsl, vk_bnd, &dset_offset);
21276+ shd->vk.view_dset_offsets[i] = (uint16_t)dset_offset;
21277+ }
21278+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
21279+ if (shd->cmn.samplers[i].stage == SG_SHADERSTAGE_NONE) {
21280+ continue;
21281+ }
21282+ const uint8_t vk_bnd = shd->vk.smp_set1_bnd_n[i];
21283+ VkDeviceSize dset_offset = 0;
21284+ _sg.vk.ext.get_descriptor_set_layout_binding_offset(_sg.vk.dev, shd->vk.view_smp_dsl, vk_bnd, &dset_offset);
21285+ shd->vk.smp_dset_offsets[i] = (uint16_t)dset_offset;
21286+ }
21287+
21288+ VkDescriptorSetLayout set_layouts[_SG_VK_NUM_DESCRIPTORSETS] = {
21289+ shd->vk.ub_dsl,
21290+ shd->vk.view_smp_dsl,
21291+ };
21292+ _SG_STRUCT(VkPipelineLayoutCreateInfo, pl_create_info);
21293+ pl_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
21294+ pl_create_info.setLayoutCount = _SG_VK_NUM_DESCRIPTORSETS;
21295+ pl_create_info.pSetLayouts = set_layouts;
21296+ res = vkCreatePipelineLayout(_sg.vk.dev, &pl_create_info, 0, &shd->vk.pip_layout);
21297+ if (res != VK_SUCCESS) {
21298+ _SG_ERROR(VULKAN_CREATE_PIPELINE_LAYOUT_FAILED);
21299+ return SG_RESOURCESTATE_FAILED;
21300+ }
21301+ return SG_RESOURCESTATE_VALID;
21302+}
21303+
21304+_SOKOL_PRIVATE void _sg_vk_discard_shader(_sg_shader_t* shd) {
21305+ SOKOL_ASSERT(shd);
21306+ SOKOL_ASSERT(_sg.vk.dev);
21307+ _sg_vk_discard_shader_func(&shd->vk.vertex_func);
21308+ _sg_vk_discard_shader_func(&shd->vk.fragment_func);
21309+ _sg_vk_discard_shader_func(&shd->vk.compute_func);
21310+ if (shd->vk.pip_layout) {
21311+ _sg_vk_delete_queue_add(_sg_vk_pipelinelayout_destructor, (void*)shd->vk.pip_layout);
21312+ shd->vk.pip_layout = 0;
21313+ }
21314+ if (shd->vk.ub_dsl) {
21315+ _sg_vk_delete_queue_add(_sg_vk_descriptorsetlayout_destructor, (void*)shd->vk.ub_dsl);
21316+ shd->vk.ub_dsl = 0;
21317+ }
21318+ if (shd->vk.view_smp_dsl) {
21319+ _sg_vk_delete_queue_add(_sg_vk_descriptorsetlayout_destructor, (void*)shd->vk.view_smp_dsl);
21320+ shd->vk.view_smp_dsl = 0;
21321+ }
21322+}
21323+
21324+_SOKOL_PRIVATE sg_resource_state _sg_vk_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) {
21325+ SOKOL_ASSERT(pip && desc);
21326+ SOKOL_ASSERT(_sg.vk.dev);
21327+ VkResult res;
21328+
21329+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
21330+ SOKOL_ASSERT(shd->vk.pip_layout);
21331+ if (pip->cmn.is_compute) {
21332+ SOKOL_ASSERT(shd->vk.compute_func.module);
21333+ _SG_STRUCT(VkComputePipelineCreateInfo, pip_create_info);
21334+ pip_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
21335+ pip_create_info.flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
21336+ pip_create_info.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
21337+ pip_create_info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
21338+ pip_create_info.stage.module = shd->vk.compute_func.module;
21339+ pip_create_info.stage.pName = shd->vk.compute_func.entry.buf;
21340+ pip_create_info.layout = shd->vk.pip_layout;
21341+ res = vkCreateComputePipelines(_sg.vk.dev, VK_NULL_HANDLE, 1, &pip_create_info, 0, &pip->vk.pip);
21342+ if (res != VK_SUCCESS) {
21343+ _SG_ERROR(VULKAN_CREATE_COMPUTE_PIPELINE_FAILED);
21344+ return SG_RESOURCESTATE_FAILED;
21345+ }
21346+ } else {
21347+ uint32_t num_stages = 0;
21348+ _SG_STRUCT(VkPipelineShaderStageCreateInfo, stages[2]);
21349+ if (shd->vk.vertex_func.module) {
21350+ stages[num_stages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
21351+ stages[num_stages].stage = VK_SHADER_STAGE_VERTEX_BIT;
21352+ stages[num_stages].module = shd->vk.vertex_func.module;
21353+ stages[num_stages].pName = shd->vk.vertex_func.entry.buf;
21354+ num_stages += 1;
21355+ }
21356+ if (shd->vk.fragment_func.module) {
21357+ stages[num_stages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
21358+ stages[num_stages].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
21359+ stages[num_stages].module = shd->vk.fragment_func.module;
21360+ stages[num_stages].pName = shd->vk.fragment_func.entry.buf;
21361+ num_stages += 1;
21362+ }
21363+
21364+ uint32_t num_vtx_bnds = 0;
21365+ _SG_STRUCT(VkVertexInputBindingDescription, vtx_bnds[SG_MAX_VERTEXBUFFER_BINDSLOTS]);
21366+ for (uint32_t vbl_idx = 0; vbl_idx < SG_MAX_VERTEXBUFFER_BINDSLOTS; vbl_idx++, num_vtx_bnds++) {
21367+ const sg_vertex_buffer_layout_state* vbl_state = &desc->layout.buffers[vbl_idx];
21368+ if (0 == vbl_state->stride) {
21369+ break;
21370+ }
21371+ vtx_bnds[vbl_idx].binding = vbl_idx;
21372+ vtx_bnds[vbl_idx].stride = (uint32_t)vbl_state->stride;
21373+ vtx_bnds[vbl_idx].inputRate = _sg_vk_vertex_input_rate(vbl_state->step_func);
21374+ }
21375+
21376+ uint32_t num_vtx_attrs = 0;
21377+ _SG_STRUCT(VkVertexInputAttributeDescription, vtx_attrs[SG_MAX_VERTEX_ATTRIBUTES]);
21378+ for (uint32_t va_idx = 0; va_idx < SG_MAX_VERTEX_ATTRIBUTES; va_idx++, num_vtx_attrs++) {
21379+ const sg_vertex_attr_state* va_state = &desc->layout.attrs[va_idx];
21380+ if (SG_VERTEXFORMAT_INVALID == va_state->format) {
21381+ break;
21382+ }
21383+ const uint32_t vbl_idx = (uint32_t)va_state->buffer_index;
21384+ SOKOL_ASSERT(vbl_idx < SG_MAX_VERTEXBUFFER_BINDSLOTS);
21385+ SOKOL_ASSERT(pip->cmn.vertex_buffer_layout_active[vbl_idx]);
21386+ vtx_attrs[va_idx].location = va_idx;
21387+ vtx_attrs[va_idx].binding = vbl_idx;
21388+ vtx_attrs[va_idx].format = _sg_vk_vertex_format(va_state->format);
21389+ vtx_attrs[va_idx].offset = (uint32_t)va_state->offset;
21390+ }
21391+
21392+ _SG_STRUCT(VkPipelineVertexInputStateCreateInfo, vi_state);
21393+ vi_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
21394+ vi_state.vertexBindingDescriptionCount = num_vtx_bnds;
21395+ vi_state.pVertexBindingDescriptions = vtx_bnds;
21396+ vi_state.vertexAttributeDescriptionCount = num_vtx_attrs;
21397+ vi_state.pVertexAttributeDescriptions = vtx_attrs;
21398+
21399+ _SG_STRUCT(VkPipelineInputAssemblyStateCreateInfo, ia_state);
21400+ ia_state.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
21401+ ia_state.topology = _sg_vk_primitive_topology(desc->primitive_type);
21402+ ia_state.primitiveRestartEnable = VK_FALSE; // FIXME: needs 'primitiveTopologyRestart feature enabled'
21403+
21404+ _SG_STRUCT(VkPipelineViewportStateCreateInfo, vp_state);
21405+ vp_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
21406+ vp_state.viewportCount = 1;
21407+ vp_state.scissorCount = 1;
21408+
21409+ _SG_STRUCT(VkPipelineRasterizationStateCreateInfo, rs_state);
21410+ rs_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
21411+ rs_state.depthClampEnable = false;
21412+ rs_state.rasterizerDiscardEnable = false;
21413+ rs_state.polygonMode = VK_POLYGON_MODE_FILL;
21414+ rs_state.cullMode = _sg_vk_cullmode(desc->cull_mode);
21415+ rs_state.frontFace = _sg_vk_frontface(desc->face_winding);
21416+ rs_state.depthBiasEnable = ((int32_t)desc->depth.bias) != 0;
21417+ rs_state.depthBiasConstantFactor = desc->depth.bias;
21418+ rs_state.depthBiasClamp = desc->depth.bias_clamp;
21419+ rs_state.depthBiasSlopeFactor = desc->depth.bias_slope_scale;
21420+ rs_state.lineWidth = 1.0f;
21421+
21422+ _SG_STRUCT(VkPipelineMultisampleStateCreateInfo, ms_state);
21423+ ms_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
21424+ ms_state.rasterizationSamples = (VkSampleCountFlagBits)desc->sample_count;
21425+ ms_state.alphaToCoverageEnable = desc->alpha_to_coverage_enabled;
21426+
21427+ _SG_STRUCT(VkPipelineDepthStencilStateCreateInfo, ds_state);
21428+ ds_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
21429+ ds_state.depthTestEnable = desc->depth.compare != SG_COMPAREFUNC_ALWAYS;
21430+ ds_state.depthWriteEnable = desc->depth.write_enabled;
21431+ ds_state.depthCompareOp = _sg_vk_compare_op(desc->depth.compare);
21432+ ds_state.depthBoundsTestEnable = false;
21433+ ds_state.stencilTestEnable = desc->stencil.enabled;
21434+ ds_state.front.failOp = _sg_vk_stencil_op(desc->stencil.front.fail_op);
21435+ ds_state.front.passOp = _sg_vk_stencil_op(desc->stencil.front.pass_op);
21436+ ds_state.front.depthFailOp = _sg_vk_stencil_op(desc->stencil.front.depth_fail_op);
21437+ ds_state.front.compareOp = _sg_vk_compare_op(desc->stencil.front.compare);
21438+ ds_state.front.compareMask = desc->stencil.read_mask;
21439+ ds_state.front.writeMask = desc->stencil.write_mask;
21440+ ds_state.front.reference = desc->stencil.ref;
21441+ ds_state.back.failOp = _sg_vk_stencil_op(desc->stencil.back.fail_op);
21442+ ds_state.back.passOp = _sg_vk_stencil_op(desc->stencil.back.pass_op);
21443+ ds_state.back.depthFailOp = _sg_vk_stencil_op(desc->stencil.back.depth_fail_op);
21444+ ds_state.back.compareOp = _sg_vk_compare_op(desc->stencil.back.compare);
21445+ ds_state.back.compareMask = desc->stencil.read_mask;
21446+ ds_state.back.writeMask = desc->stencil.write_mask;
21447+ ds_state.back.reference = desc->stencil.ref;
21448+
21449+ _SG_STRUCT(VkPipelineColorBlendAttachmentState, att_states[SG_MAX_COLOR_ATTACHMENTS]);
21450+ SOKOL_ASSERT(desc->color_count < SG_MAX_COLOR_ATTACHMENTS);
21451+ for (int i = 0; i < desc->color_count; i++) {
21452+ att_states[i].blendEnable = desc->colors[i].blend.enabled;
21453+ att_states[i].srcColorBlendFactor = _sg_vk_blend_factor(desc->colors[i].blend.src_factor_rgb);
21454+ att_states[i].dstColorBlendFactor = _sg_vk_blend_factor(desc->colors[i].blend.dst_factor_rgb);
21455+ att_states[i].colorBlendOp = _sg_vk_blend_op(desc->colors[i].blend.op_rgb);
21456+ att_states[i].srcAlphaBlendFactor = _sg_vk_blend_factor(desc->colors[i].blend.src_factor_alpha);
21457+ att_states[i].dstAlphaBlendFactor = _sg_vk_blend_factor(desc->colors[i].blend.dst_factor_alpha);
21458+ att_states[i].alphaBlendOp = _sg_vk_blend_op(desc->colors[i].blend.op_alpha);
21459+ att_states[i].colorWriteMask = _sg_vk_color_write_mask(desc->colors[i].write_mask);
21460+ }
21461+
21462+ _SG_STRUCT(VkPipelineColorBlendStateCreateInfo, cb_state);
21463+ cb_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
21464+ cb_state.logicOpEnable = false;
21465+ cb_state.attachmentCount = (uint32_t)desc->color_count;
21466+ cb_state.pAttachments = att_states;
21467+ cb_state.blendConstants[0] = desc->blend_color.r;
21468+ cb_state.blendConstants[1] = desc->blend_color.g;
21469+ cb_state.blendConstants[2] = desc->blend_color.b;
21470+ cb_state.blendConstants[3] = desc->blend_color.a;
21471+
21472+ _SG_STRUCT(VkFormat, color_formats[SG_MAX_COLOR_ATTACHMENTS]);
21473+ SOKOL_ASSERT(desc->color_count <= SG_MAX_COLOR_ATTACHMENTS);
21474+ for (int i = 0; i < desc->color_count; i++) {
21475+ color_formats[i] = _sg_vk_format(desc->colors[i].pixel_format);
21476+ }
21477+ _SG_STRUCT(VkPipelineRenderingCreateInfo, rnd_state);
21478+ rnd_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
21479+ rnd_state.colorAttachmentCount = (uint32_t)desc->color_count;
21480+ rnd_state.pColorAttachmentFormats = color_formats;
21481+ rnd_state.depthAttachmentFormat = _sg_vk_format(desc->depth.pixel_format);
21482+ if (_sg_is_depth_stencil_format(desc->depth.pixel_format)) {
21483+ rnd_state.stencilAttachmentFormat = _sg_vk_format(desc->depth.pixel_format);
21484+ } else {
21485+ rnd_state.stencilAttachmentFormat = VK_FORMAT_UNDEFINED;
21486+ }
21487+ VkDynamicState dyn_states[2] = {
21488+ VK_DYNAMIC_STATE_VIEWPORT,
21489+ VK_DYNAMIC_STATE_SCISSOR,
21490+ };
21491+ _SG_STRUCT(VkPipelineDynamicStateCreateInfo, dyn_state);
21492+ dyn_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
21493+ dyn_state.dynamicStateCount = 2;
21494+ dyn_state.pDynamicStates = dyn_states;
21495+
21496+ _SG_STRUCT(VkGraphicsPipelineCreateInfo, pip_create_info);
21497+ pip_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
21498+ pip_create_info.pNext = &rnd_state;
21499+ pip_create_info.flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
21500+ pip_create_info.stageCount = num_stages;
21501+ pip_create_info.pStages = stages;
21502+ pip_create_info.pVertexInputState = &vi_state;
21503+ pip_create_info.pInputAssemblyState = &ia_state;
21504+ pip_create_info.pViewportState = &vp_state;
21505+ pip_create_info.pRasterizationState = &rs_state;
21506+ pip_create_info.pMultisampleState = &ms_state;
21507+ pip_create_info.pDepthStencilState = &ds_state;
21508+ pip_create_info.pColorBlendState = &cb_state;
21509+ pip_create_info.pDynamicState = &dyn_state;
21510+ pip_create_info.layout = shd->vk.pip_layout;
21511+
21512+ res = vkCreateGraphicsPipelines(_sg.vk.dev, VK_NULL_HANDLE, 1, &pip_create_info, 0, &pip->vk.pip);
21513+ if (res != VK_SUCCESS) {
21514+ _SG_ERROR(VULKAN_CREATE_GRAPHICS_PIPELINE_FAILED);
21515+ return SG_RESOURCESTATE_FAILED;
21516+ }
21517+ }
21518+ SOKOL_ASSERT(pip->vk.pip);
21519+ _sg_vk_set_object_label(VK_OBJECT_TYPE_PIPELINE, (uint64_t)pip->vk.pip, desc->label);
21520+ return SG_RESOURCESTATE_VALID;
21521+}
21522+
21523+_SOKOL_PRIVATE void _sg_vk_discard_pipeline(_sg_pipeline_t* pip) {
21524+ SOKOL_ASSERT(pip);
21525+ if (pip->vk.pip) {
21526+ _sg_vk_delete_queue_add(_sg_vk_pipeline_destructor, (void*)pip->vk.pip);
21527+ pip->vk.pip = 0;
21528+ }
21529+}
21530+
21531+_SOKOL_PRIVATE sg_resource_state _sg_vk_create_view(_sg_view_t* view, const sg_view_desc* desc) {
21532+ SOKOL_ASSERT(view && desc);
21533+ SOKOL_ASSERT(_sg.vk.dev);
21534+ SOKOL_ASSERT(0 == view->vk.img_view);
21535+ VkResult res;
21536+ _SG_STRUCT(VkDescriptorGetInfoEXT, get_info);
21537+ get_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT;
21538+ if (view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) {
21539+ // record descriptor data for storage buffer
21540+ view->vk.descriptor_size = _sg.vk.descriptor_buffer_props.storageBufferDescriptorSize;
21541+ if (_SG_VK_MAX_DESCRIPTOR_DATA_SIZE < view->vk.descriptor_size) {
21542+ _SG_ERROR(VULKAN_VIEW_MAX_DESCRIPTOR_SIZE);
21543+ return SG_RESOURCESTATE_FAILED;
21544+ }
21545+ const _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref);
21546+ SOKOL_ASSERT(buf->vk.dev_addr);
21547+ _SG_STRUCT(VkDescriptorAddressInfoEXT, addr_info);
21548+ addr_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT;
21549+ addr_info.address = buf->vk.dev_addr + (VkDeviceSize)view->cmn.buf.offset;
21550+ addr_info.range = (VkDeviceSize)(buf->cmn.size - view->cmn.buf.offset);
21551+ get_info.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
21552+ get_info.data.pStorageBuffer = &addr_info;
21553+ _sg.vk.ext.get_descriptor(_sg.vk.dev, &get_info, view->vk.descriptor_size, &view->vk.descriptor_data);
21554+ } else {
21555+ // create image view object
21556+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
21557+ SOKOL_ASSERT(img->vk.img);
21558+ SOKOL_ASSERT(view->cmn.img.mip_level_count >= 1);
21559+ SOKOL_ASSERT(view->cmn.img.slice_count >= 1);
21560+ _SG_STRUCT(VkImageViewCreateInfo, create_info);
21561+ create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
21562+ create_info.image = img->vk.img;
21563+ if (view->cmn.type == SG_VIEWTYPE_TEXTURE) {
21564+ create_info.viewType = _sg_vk_texture_image_view_type(img->cmn.type);
21565+ } else {
21566+ create_info.viewType = _sg_vk_attachment_image_view_type(img->cmn.type);
21567+ }
21568+ create_info.format = _sg_vk_format(img->cmn.pixel_format);
21569+ if (view->cmn.type == SG_VIEWTYPE_DEPTHSTENCILATTACHMENT) {
21570+ create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
21571+ if (_sg_is_depth_stencil_format(img->cmn.pixel_format)) {
21572+ create_info.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
21573+ }
21574+ } else if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) {
21575+ create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
21576+ } else {
21577+ create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
21578+ }
21579+ create_info.subresourceRange.baseMipLevel = (uint32_t)view->cmn.img.mip_level;
21580+ create_info.subresourceRange.levelCount = (uint32_t)view->cmn.img.mip_level_count;
21581+ create_info.subresourceRange.baseArrayLayer = (uint32_t)view->cmn.img.slice;
21582+ create_info.subresourceRange.layerCount = (uint32_t)view->cmn.img.slice_count;
21583+ res = vkCreateImageView(_sg.vk.dev, &create_info, 0, &view->vk.img_view);
21584+ if (res != VK_SUCCESS) {
21585+ _SG_ERROR(VULKAN_CREATE_IMAGE_VIEW_FAILED);
21586+ return SG_RESOURCESTATE_FAILED;
21587+ }
21588+ SOKOL_ASSERT(view->vk.img_view);
21589+ _sg_vk_set_object_label(VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)view->vk.img_view, desc->label);
21590+
21591+ // record descriptor data for storage images and textures
21592+ if ((view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) || (view->cmn.type == SG_VIEWTYPE_TEXTURE)) {
21593+ _SG_STRUCT(VkDescriptorImageInfo, img_info);
21594+ img_info.imageView = view->vk.img_view;
21595+ if (view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE) {
21596+ view->vk.descriptor_size = _sg.vk.descriptor_buffer_props.storageImageDescriptorSize;
21597+ img_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
21598+ get_info.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
21599+ get_info.data.pStorageImage = &img_info;
21600+ } else {
21601+ view->vk.descriptor_size = _sg.vk.descriptor_buffer_props.sampledImageDescriptorSize;
21602+ img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
21603+ get_info.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
21604+ get_info.data.pSampledImage = &img_info;
21605+ }
21606+ if (_SG_VK_MAX_DESCRIPTOR_DATA_SIZE < view->vk.descriptor_size) {
21607+ _SG_ERROR(VULKAN_VIEW_MAX_DESCRIPTOR_SIZE);
21608+ return SG_RESOURCESTATE_FAILED;
21609+ }
21610+ _sg.vk.ext.get_descriptor(_sg.vk.dev, &get_info, view->vk.descriptor_size, &view->vk.descriptor_data);
21611+ }
21612+ }
21613+ return SG_RESOURCESTATE_VALID;
21614+}
21615+
21616+_SOKOL_PRIVATE void _sg_vk_discard_view(_sg_view_t* view) {
21617+ SOKOL_ASSERT(view);
21618+ if (view->vk.img_view) {
21619+ _sg_vk_delete_queue_add(_sg_vk_image_view_destructor, (void*)view->vk.img_view);
21620+ view->vk.img_view = 0;
21621+ }
21622+}
21623+
21624+_SOKOL_PRIVATE void _sg_vk_apply_viewport(int x, int y, int w, int h, bool origin_top_left) {
21625+ SOKOL_ASSERT(_sg.vk.frame.cmd_buf);
21626+ _SG_STRUCT(VkViewport, vp);
21627+ vp.x = (float) x;
21628+ vp.width = (float) w;
21629+ vp.height = (float) -h;
21630+ vp.maxDepth = 1.0f;
21631+ if (origin_top_left) {
21632+ vp.y = (float)(y + h);
21633+ } else {
21634+ vp.y = (float)(_sg.cur_pass.dim.height - y);
21635+ }
21636+ vkCmdSetViewport(_sg.vk.frame.cmd_buf, 0, 1, &vp);
21637+}
21638+
21639+_SOKOL_PRIVATE void _sg_vk_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) {
21640+ SOKOL_ASSERT(_sg.vk.frame.cmd_buf);
21641+ const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.dim.width, _sg.cur_pass.dim.height);
21642+ _SG_STRUCT(VkRect2D, rect);
21643+ rect.offset.x = clip.x;
21644+ rect.offset.y = (origin_top_left ? clip.y : (_sg.cur_pass.dim.height - (clip.y + clip.h)));
21645+ rect.extent.width = (uint32_t) clip.w;
21646+ rect.extent.height = (uint32_t) clip.h;
21647+ vkCmdSetScissor(_sg.vk.frame.cmd_buf, 0, 1, &rect);
21648+}
21649+
21650+_SOKOL_PRIVATE void _sg_vk_init_color_attachment_info(VkRenderingAttachmentInfo* info, const sg_color_attachment_action* action, VkImageView color_view, VkImageView resolve_view) {
21651+ info->sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
21652+ info->imageView = color_view;
21653+ info->imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
21654+ if (resolve_view) {
21655+ info->resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT;
21656+ info->resolveImageView = resolve_view;
21657+ info->resolveImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
21658+ } else {
21659+ info->resolveMode = VK_RESOLVE_MODE_NONE;
21660+ info->resolveImageView = 0;
21661+ info->resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
21662+ }
21663+ info->loadOp = _sg_vk_load_op(action->load_action);
21664+ info->storeOp = _sg_vk_store_op(action->store_action);
21665+ info->clearValue.color.float32[0] = action->clear_value.r;
21666+ info->clearValue.color.float32[1] = action->clear_value.g;
21667+ info->clearValue.color.float32[2] = action->clear_value.b;
21668+ info->clearValue.color.float32[3] = action->clear_value.a;
21669+}
21670+
21671+_SOKOL_PRIVATE void _sg_vk_init_depth_attachment_info(VkRenderingAttachmentInfo* info, const sg_depth_attachment_action* action, VkImageView ds_view) {
21672+ info->sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
21673+ info->imageView = ds_view;
21674+ info->imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
21675+ info->resolveMode = VK_RESOLVE_MODE_NONE;
21676+ info->loadOp = _sg_vk_load_op(action->load_action);
21677+ info->storeOp = _sg_vk_store_op(action->store_action);
21678+ info->clearValue.depthStencil.depth = action->clear_value;
21679+}
21680+
21681+_SOKOL_PRIVATE void _sg_vk_init_stencil_attachment_info(VkRenderingAttachmentInfo* info, const sg_stencil_attachment_action* action, VkImageView ds_view) {
21682+ info->sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
21683+ info->imageView = ds_view;
21684+ info->imageLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL;
21685+ info->resolveMode = VK_RESOLVE_MODE_NONE;
21686+ info->loadOp = _sg_vk_load_op(action->load_action);
21687+ info->storeOp = _sg_vk_store_op(action->store_action);
21688+ info->clearValue.depthStencil.stencil = action->clear_value;
21689+}
21690+
21691+_SOKOL_PRIVATE void _sg_vk_begin_compute_pass(VkCommandBuffer cmd_buf, const sg_pass* pass) {
21692+ // FIXME: nothing to do here?
21693+ _SOKOL_UNUSED(cmd_buf && pass);
21694+}
21695+
21696+_SOKOL_PRIVATE void _sg_vk_begin_render_pass(VkCommandBuffer cmd_buf, const sg_pass* pass, const _sg_attachments_ptrs_t* atts) {
21697+ const sg_pass_action* action = &pass->action;
21698+ const bool is_swapchain_pass = atts->empty;
21699+
21700+ _SG_STRUCT(VkRenderingAttachmentInfo, color_att_infos[SG_MAX_COLOR_ATTACHMENTS]);
21701+ _SG_STRUCT(VkRenderingAttachmentInfo, depth_att_info);
21702+ _SG_STRUCT(VkRenderingAttachmentInfo, stencil_att_info);
21703+ _SG_STRUCT(VkRenderingInfo, render_info);
21704+ render_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
21705+ render_info.renderArea.extent.width = (uint32_t)_sg.cur_pass.dim.width;
21706+ render_info.renderArea.extent.height = (uint32_t)_sg.cur_pass.dim.height;
21707+ render_info.layerCount = 1;
21708+
21709+ if (is_swapchain_pass) {
21710+ _sg.vk.swapchain = pass->swapchain.vulkan;
21711+ SOKOL_ASSERT(_sg.vk.swapchain.render_view);
21712+ if (pass->swapchain.sample_count > 1) {
21713+ SOKOL_ASSERT(_sg.vk.swapchain.resolve_view);
21714+ }
21715+ SOKOL_ASSERT(_sg.vk.swapchain.present_complete_semaphore);
21716+ SOKOL_ASSERT(_sg.vk.swapchain.render_finished_semaphore);
21717+ // FIXME: need to support multiple present_complete_semaphores
21718+ SOKOL_ASSERT(0 == _sg.vk.present_complete_sem);
21719+ _sg.vk.present_complete_sem = (VkSemaphore)_sg.vk.swapchain.present_complete_semaphore;
21720+ if (0 == _sg.vk.render_finished_sem) {
21721+ _sg.vk.render_finished_sem = (VkSemaphore)_sg.vk.swapchain.render_finished_semaphore;
21722+ } else {
21723+ SOKOL_ASSERT(_sg.vk.render_finished_sem == _sg.vk.swapchain.render_finished_semaphore);
21724+ }
21725+ VkImageView vk_color_view = (VkImageView)_sg.vk.swapchain.render_view;
21726+ VkImageView vk_resolve_view = (VkImageView)_sg.vk.swapchain.resolve_view;
21727+ _sg_vk_init_color_attachment_info(&color_att_infos[0], &action->colors[0], vk_color_view, vk_resolve_view);
21728+ render_info.colorAttachmentCount = 1;
21729+ render_info.pColorAttachments = color_att_infos;
21730+ if (_sg.vk.swapchain.depth_stencil_view) {
21731+ VkImageView vk_ds_view = (VkImageView)_sg.vk.swapchain.depth_stencil_view;
21732+ const bool has_stencil = _sg_is_depth_stencil_format(pass->swapchain.depth_format);
21733+ _sg_vk_init_depth_attachment_info(&depth_att_info, &action->depth, vk_ds_view);
21734+ render_info.pDepthAttachment = &depth_att_info;
21735+ if (has_stencil) {
21736+ _sg_vk_init_stencil_attachment_info(&stencil_att_info, &action->stencil, vk_ds_view);
21737+ render_info.pStencilAttachment = &stencil_att_info;
21738+ }
21739+ }
21740+ } else {
21741+ SOKOL_ASSERT(atts->num_color_views <= SG_MAX_COLOR_ATTACHMENTS);
21742+ for (int i = 0; i < atts->num_color_views; i++) {
21743+ SOKOL_ASSERT(atts->color_views[i]);
21744+ const _sg_view_t* color_view = atts->color_views[i];
21745+ VkImageView vk_color_view = color_view->vk.img_view;
21746+ const _sg_view_t* resolve_view = atts->resolve_views[i];
21747+ VkImageView vk_resolve_view = 0;
21748+ if (resolve_view) {
21749+ vk_resolve_view = resolve_view->vk.img_view;
21750+ }
21751+ _sg_vk_init_color_attachment_info(&color_att_infos[i], &action->colors[i], vk_color_view, vk_resolve_view);
21752+ }
21753+ if (atts->num_color_views > 0) {
21754+ render_info.colorAttachmentCount = (uint32_t)atts->num_color_views;
21755+ render_info.pColorAttachments = color_att_infos;
21756+ }
21757+ if (atts->ds_view) {
21758+ const _sg_view_t* ds_view = atts->ds_view;
21759+ const _sg_image_t* ds_image = _sg_image_ref_ptr(&ds_view->cmn.img.ref);
21760+ const bool has_stencil = _sg_is_depth_stencil_format(ds_image->cmn.pixel_format);
21761+ VkImageView vk_ds_view = ds_view->vk.img_view;
21762+ _sg_vk_init_depth_attachment_info(&depth_att_info, &action->depth, vk_ds_view);
21763+ render_info.pDepthAttachment = &depth_att_info;
21764+ if (has_stencil) {
21765+ _sg_vk_init_stencil_attachment_info(&stencil_att_info, &action->stencil, vk_ds_view);
21766+ render_info.pStencilAttachment = &stencil_att_info;
21767+ }
21768+ }
21769+ }
21770+ vkCmdBeginRendering(cmd_buf, &render_info);
21771+
21772+ _SG_STRUCT(VkViewport, vp);
21773+ vp.y = (float)_sg.cur_pass.dim.height;
21774+ vp.width = (float)_sg.cur_pass.dim.width;
21775+ vp.height = (float)-_sg.cur_pass.dim.height;
21776+ vp.maxDepth = 1.0f;
21777+ vkCmdSetViewport(_sg.vk.frame.cmd_buf, 0, 1, &vp);
21778+
21779+ _SG_STRUCT(VkRect2D, rect);
21780+ rect.extent.width = (uint32_t)_sg.cur_pass.dim.width;
21781+ rect.extent.height = (uint32_t)_sg.cur_pass.dim.height;
21782+ vkCmdSetScissor(_sg.vk.frame.cmd_buf, 0, 1, &rect);
21783+}
21784+
21785+_SOKOL_PRIVATE void _sg_vk_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) {
21786+ SOKOL_ASSERT(pass && atts);
21787+ _sg_vk_acquire_frame_command_buffers();
21788+ SOKOL_ASSERT(_sg.vk.frame.cmd_buf);
21789+ _sg_vk_barrier_on_begin_pass(_sg.vk.frame.cmd_buf, pass, atts, _sg.cur_pass.is_compute);
21790+ if (_sg.cur_pass.is_compute) {
21791+ _sg_vk_begin_compute_pass(_sg.vk.frame.cmd_buf, pass);
21792+ } else {
21793+ _sg_vk_begin_render_pass(_sg.vk.frame.cmd_buf, pass, atts);
21794+ }
21795+}
21796+
21797+_SOKOL_PRIVATE void _sg_vk_end_pass(const _sg_attachments_ptrs_t* atts) {
21798+ SOKOL_ASSERT(atts);
21799+ SOKOL_ASSERT(_sg.vk.frame.cmd_buf);
21800+ if (!_sg.cur_pass.is_compute) {
21801+ vkCmdEndRendering(_sg.vk.frame.cmd_buf);
21802+ }
21803+ _sg_vk_barrier_on_end_pass(_sg.vk.frame.cmd_buf, atts, _sg.cur_pass.is_compute);
21804+ _sg_clear(&_sg.vk.swapchain, sizeof(_sg.vk.swapchain));
21805+}
21806+
21807+_SOKOL_PRIVATE void _sg_vk_commit(void) {
21808+ SOKOL_ASSERT(_sg.vk.queue);
21809+ if (_sg.vk.frame.cmd_buf) {
21810+ _sg_vk_submit_frame_command_buffers();
21811+ }
21812+ _sg.vk.present_complete_sem = 0;
21813+ _sg.vk.render_finished_sem = 0;
21814+}
21815+
21816+_SOKOL_PRIVATE void _sg_vk_apply_pipeline(_sg_pipeline_t* pip) {
21817+ SOKOL_ASSERT(pip);
21818+ SOKOL_ASSERT(pip->vk.pip);
21819+ SOKOL_ASSERT(_sg.vk.frame.cmd_buf);
21820+ _sg.vk.uniforms.dirty = false;
21821+ VkPipelineBindPoint bindpoint = pip->cmn.is_compute
21822+ ? VK_PIPELINE_BIND_POINT_COMPUTE
21823+ : VK_PIPELINE_BIND_POINT_GRAPHICS;
21824+ vkCmdBindPipeline(_sg.vk.frame.cmd_buf, bindpoint, pip->vk.pip);
21825+}
21826+
21827+_SOKOL_PRIVATE bool _sg_vk_apply_bindings(_sg_bindings_ptrs_t* bnd) {
21828+ SOKOL_ASSERT(bnd && bnd->pip);
21829+ SOKOL_ASSERT(_sg.vk.dev);
21830+ SOKOL_ASSERT(_sg.vk.frame.cmd_buf);
21831+ VkCommandBuffer cmd_buf = _sg.vk.frame.cmd_buf;
21832+
21833+ // track or insert pipeline barriers
21834+ _sg_vk_barrier_on_apply_bindings(cmd_buf, bnd, _sg.cur_pass.is_compute);
21835+
21836+ if (!_sg.cur_pass.is_compute) {
21837+ // bind vertex buffers
21838+ // FIXME: could do this in a single call if buffer bindings are guaranteed
21839+ // to be continuous (currently that's not checked anywhere), or alternative
21840+ // via nullDescriptor robustness feature (which apparently may have performance downsides)
21841+ for (uint32_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) {
21842+ if (bnd->vbs[i]) {
21843+ VkBuffer vk_buf = bnd->vbs[i]->vk.buf;
21844+ VkDeviceSize vk_offset = (VkDeviceSize)bnd->vb_offsets[i];
21845+ vkCmdBindVertexBuffers(cmd_buf, i, 1, &vk_buf, &vk_offset);
21846+ }
21847+ }
21848+ if (bnd->ib) {
21849+ VkBuffer vk_buf = bnd->ib->vk.buf;
21850+ VkDeviceSize vk_offset = (VkDeviceSize)bnd->ib_offset;
21851+ VkIndexType vk_index_type = _sg_vk_index_type(bnd->pip->cmn.index_type);
21852+ vkCmdBindIndexBuffer(cmd_buf, vk_buf, vk_offset, vk_index_type);
21853+ }
21854+ }
21855+
21856+ // bind views and samplers
21857+ const VkPipelineBindPoint pip_bind_point = _sg.cur_pass.is_compute
21858+ ? VK_PIPELINE_BIND_POINT_COMPUTE
21859+ : VK_PIPELINE_BIND_POINT_GRAPHICS;
21860+ return _sg_vk_bind_view_smp_descriptor_set(cmd_buf, bnd, pip_bind_point);
21861+}
21862+
21863+_SOKOL_PRIVATE void _sg_vk_apply_uniforms(int ub_slot, const sg_range* data) {
21864+ SOKOL_ASSERT(_sg.vk.uniforms.dbuf.cur_dev_addr);
21865+ SOKOL_ASSERT(data && data->ptr && (data->size > 0));
21866+ SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS));
21867+ const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip);
21868+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
21869+ SOKOL_ASSERT(data->size == shd->cmn.uniform_blocks[ub_slot].size);
21870+
21871+ // copy data into uniform buffer and keep track of uniform bind infos
21872+ const VkDeviceSize ubuf_offset = _sg_vk_uniform_copy(data);
21873+ if (_sg.vk.uniforms.dbuf.overflown) {
21874+ _SG_ERROR(VULKAN_UNIFORM_BUFFER_OVERFLOW);
21875+ _sg.next_draw_valid = false;
21876+ return;
21877+ }
21878+ _sg.vk.uniforms.addr_info[ub_slot].range = data->size;
21879+ _sg.vk.uniforms.addr_info[ub_slot].address = _sg.vk.uniforms.dbuf.cur_dev_addr + ubuf_offset;
21880+
21881+ // copy uniform buffer descriptor data into intermediate sysmem buffer
21882+ // NOTE: letting vkGetDescriptorEXT write directly into the descriptor
21883+ // buffer has catastrophic performance on some Vulkan drivers, notably
21884+ // Intel's Windows driver
21885+ const size_t dsize = _sg.vk.descriptor_buffer_props.uniformBufferDescriptorSize;
21886+ SOKOL_ASSERT((shd->vk.ub_dset_offsets[ub_slot] + dsize) <= _sg.vk.uniforms.dset_cache_size);
21887+ uint8_t* dst_ptr = _sg.vk.uniforms.dset_cache + shd->vk.ub_dset_offsets[ub_slot];
21888+ _sg.vk.ext.get_descriptor(_sg.vk.dev, &_sg.vk.uniforms.get_info[ub_slot], dsize, dst_ptr);
21889+
21890+ // set uniforms dirty, applying the descriptor buffer offset is happens in draw/dispatch
21891+ _sg.vk.uniforms.dirty = true;
21892+}
21893+
21894+_SOKOL_PRIVATE void _sg_vk_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) {
21895+ SOKOL_ASSERT(_sg.vk.frame.cmd_buf);
21896+ VkCommandBuffer cmd_buf = _sg.vk.frame.cmd_buf;
21897+ if (_sg.vk.uniforms.dirty) {
21898+ if (!_sg_vk_bind_uniform_descriptor_set(cmd_buf)) {
21899+ return;
21900+ }
21901+ }
21902+ if (_sg.use_indexed_draw) {
21903+ vkCmdDrawIndexed(cmd_buf,
21904+ (uint32_t)num_elements,
21905+ (uint32_t)num_instances,
21906+ (uint32_t)base_element,
21907+ base_vertex,
21908+ (uint32_t)base_instance);
21909+ } else {
21910+ vkCmdDraw(cmd_buf,
21911+ (uint32_t)num_elements,
21912+ (uint32_t)num_instances,
21913+ (uint32_t)base_element,
21914+ (uint32_t)base_instance);
21915+ }
21916+}
21917+
21918+_SOKOL_PRIVATE void _sg_vk_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) {
21919+ SOKOL_ASSERT(_sg.vk.frame.cmd_buf);
21920+ VkCommandBuffer cmd_buf = _sg.vk.frame.cmd_buf;
21921+ if (_sg.vk.uniforms.dirty) {
21922+ if (!_sg_vk_bind_uniform_descriptor_set(cmd_buf)) {
21923+ return;
21924+ }
21925+ }
21926+ vkCmdDispatch(cmd_buf, (uint32_t)num_groups_x, (uint32_t)num_groups_y, (uint32_t)num_groups_z);
21927+}
21928+
21929+_SOKOL_PRIVATE void _sg_vk_update_buffer(_sg_buffer_t* buf, const sg_range* data) {
21930+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
21931+ if (buf->cmn.usage.stream_update) {
21932+ _sg_vk_acquire_frame_command_buffers();
21933+ _sg_vk_staging_stream_buffer_data(buf, data, 0);
21934+ } else {
21935+ _sg_vk_staging_copy_buffer_data(buf, data, 0, true);
21936+ }
21937+}
21938+
21939+_SOKOL_PRIVATE void _sg_vk_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) {
21940+ SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0));
21941+ _SOKOL_UNUSED(new_frame);
21942+ if (buf->cmn.usage.stream_update) {
21943+ _sg_vk_acquire_frame_command_buffers();
21944+ _sg_vk_staging_stream_buffer_data(buf, data, (size_t)buf->cmn.append_pos);
21945+ } else {
21946+ _sg_vk_staging_copy_buffer_data(buf, data, (size_t)buf->cmn.append_pos, true);
21947+ }
21948+}
21949+
21950+_SOKOL_PRIVATE void _sg_vk_update_image(_sg_image_t* img, const sg_image_data* data) {
21951+ SOKOL_ASSERT(img && data);
21952+ if (img->cmn.usage.stream_update) {
21953+ _sg_vk_acquire_frame_command_buffers();
21954+ _sg_vk_staging_stream_image_data(img, data);
21955+ } else {
21956+ _sg_vk_staging_copy_image_data(img, data, true);
21957+ }
21958+}
21959+
21960+#endif
21961+
21962+// ██████ ███████ ███ ██ ███████ ██████ ██ ██████ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████
21963+// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██
21964+// ██ ███ █████ ██ ██ ██ █████ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██
21965+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
21966+// ██████ ███████ ██ ████ ███████ ██ ██ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████
21967+//
21968+// >>generic backend
21969+static inline void _sg_setup_backend(const sg_desc* desc) {
21970+ #if defined(_SOKOL_ANY_GL)
21971+ _sg_gl_setup_backend(desc);
21972+ #elif defined(SOKOL_METAL)
21973+ _sg_mtl_setup_backend(desc);
21974+ #elif defined(SOKOL_D3D11)
21975+ _sg_d3d11_setup_backend(desc);
21976+ #elif defined(SOKOL_WGPU)
21977+ _sg_wgpu_setup_backend(desc);
21978+ #elif defined(SOKOL_VULKAN)
21979+ _sg_vk_setup_backend(desc);
21980+ #elif defined(SOKOL_DUMMY_BACKEND)
21981+ _sg_dummy_setup_backend(desc);
21982+ #else
21983+ #error("INVALID BACKEND");
21984+ #endif
21985+}
21986+
21987+static inline void _sg_discard_backend(void) {
21988+ #if defined(_SOKOL_ANY_GL)
21989+ _sg_gl_discard_backend();
21990+ #elif defined(SOKOL_METAL)
21991+ _sg_mtl_discard_backend();
21992+ #elif defined(SOKOL_D3D11)
21993+ _sg_d3d11_discard_backend();
21994+ #elif defined(SOKOL_WGPU)
21995+ _sg_wgpu_discard_backend();
21996+ #elif defined(SOKOL_VULKAN)
21997+ _sg_vk_discard_backend();
21998+ #elif defined(SOKOL_DUMMY_BACKEND)
21999+ _sg_dummy_discard_backend();
22000+ #else
22001+ #error("INVALID BACKEND");
22002+ #endif
22003+}
22004+
22005+static inline void _sg_reset_state_cache(void) {
22006+ #if defined(_SOKOL_ANY_GL)
22007+ _sg_gl_reset_state_cache();
22008+ #elif defined(SOKOL_METAL)
22009+ _sg_mtl_reset_state_cache();
22010+ #elif defined(SOKOL_D3D11)
22011+ _sg_d3d11_reset_state_cache();
22012+ #elif defined(SOKOL_WGPU)
22013+ _sg_wgpu_reset_state_cache();
22014+ #elif defined(SOKOL_VULKAN)
22015+ _sg_vk_reset_state_cache();
22016+ #elif defined(SOKOL_DUMMY_BACKEND)
22017+ _sg_dummy_reset_state_cache();
22018+ #else
22019+ #error("INVALID BACKEND");
22020+ #endif
22021+}
22022+
22023+static inline sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) {
22024+ #if defined(_SOKOL_ANY_GL)
22025+ return _sg_gl_create_buffer(buf, desc);
22026+ #elif defined(SOKOL_METAL)
22027+ return _sg_mtl_create_buffer(buf, desc);
22028+ #elif defined(SOKOL_D3D11)
22029+ return _sg_d3d11_create_buffer(buf, desc);
22030+ #elif defined(SOKOL_WGPU)
22031+ return _sg_wgpu_create_buffer(buf, desc);
22032+ #elif defined(SOKOL_VULKAN)
22033+ return _sg_vk_create_buffer(buf, desc);
22034+ #elif defined(SOKOL_DUMMY_BACKEND)
22035+ return _sg_dummy_create_buffer(buf, desc);
22036+ #else
22037+ #error("INVALID BACKEND");
22038+ #endif
22039+}
22040+
22041+static inline void _sg_discard_buffer(_sg_buffer_t* buf) {
22042+ #if defined(_SOKOL_ANY_GL)
22043+ _sg_gl_discard_buffer(buf);
22044+ #elif defined(SOKOL_METAL)
22045+ _sg_mtl_discard_buffer(buf);
22046+ #elif defined(SOKOL_D3D11)
22047+ _sg_d3d11_discard_buffer(buf);
22048+ #elif defined(SOKOL_WGPU)
22049+ _sg_wgpu_discard_buffer(buf);
22050+ #elif defined(SOKOL_VULKAN)
22051+ _sg_vk_discard_buffer(buf);
22052+ #elif defined(SOKOL_DUMMY_BACKEND)
22053+ _sg_dummy_discard_buffer(buf);
22054+ #else
22055+ #error("INVALID BACKEND");
22056+ #endif
22057+}
22058+
22059+static inline sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) {
22060+ #if defined(_SOKOL_ANY_GL)
22061+ return _sg_gl_create_image(img, desc);
22062+ #elif defined(SOKOL_METAL)
22063+ return _sg_mtl_create_image(img, desc);
22064+ #elif defined(SOKOL_D3D11)
22065+ return _sg_d3d11_create_image(img, desc);
22066+ #elif defined(SOKOL_WGPU)
22067+ return _sg_wgpu_create_image(img, desc);
22068+ #elif defined(SOKOL_VULKAN)
22069+ return _sg_vk_create_image(img, desc);
22070+ #elif defined(SOKOL_DUMMY_BACKEND)
22071+ return _sg_dummy_create_image(img, desc);
22072+ #else
22073+ #error("INVALID BACKEND");
22074+ #endif
22075+}
22076+
22077+static inline void _sg_discard_image(_sg_image_t* img) {
22078+ #if defined(_SOKOL_ANY_GL)
22079+ _sg_gl_discard_image(img);
22080+ #elif defined(SOKOL_METAL)
22081+ _sg_mtl_discard_image(img);
22082+ #elif defined(SOKOL_D3D11)
22083+ _sg_d3d11_discard_image(img);
22084+ #elif defined(SOKOL_WGPU)
22085+ _sg_wgpu_discard_image(img);
22086+ #elif defined(SOKOL_VULKAN)
22087+ _sg_vk_discard_image(img);
22088+ #elif defined(SOKOL_DUMMY_BACKEND)
22089+ _sg_dummy_discard_image(img);
22090+ #else
22091+ #error("INVALID BACKEND");
22092+ #endif
22093+}
22094+
22095+static inline sg_resource_state _sg_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) {
22096+ #if defined(_SOKOL_ANY_GL)
22097+ return _sg_gl_create_sampler(smp, desc);
22098+ #elif defined(SOKOL_METAL)
22099+ return _sg_mtl_create_sampler(smp, desc);
22100+ #elif defined(SOKOL_D3D11)
22101+ return _sg_d3d11_create_sampler(smp, desc);
22102+ #elif defined(SOKOL_WGPU)
22103+ return _sg_wgpu_create_sampler(smp, desc);
22104+ #elif defined(SOKOL_VULKAN)
22105+ return _sg_vk_create_sampler(smp, desc);
22106+ #elif defined(SOKOL_DUMMY_BACKEND)
22107+ return _sg_dummy_create_sampler(smp, desc);
22108+ #else
22109+ #error("INVALID BACKEND");
22110+ #endif
22111+}
22112+
22113+static inline void _sg_discard_sampler(_sg_sampler_t* smp) {
22114+ #if defined(_SOKOL_ANY_GL)
22115+ _sg_gl_discard_sampler(smp);
22116+ #elif defined(SOKOL_METAL)
22117+ _sg_mtl_discard_sampler(smp);
22118+ #elif defined(SOKOL_D3D11)
22119+ _sg_d3d11_discard_sampler(smp);
22120+ #elif defined(SOKOL_WGPU)
22121+ _sg_wgpu_discard_sampler(smp);
22122+ #elif defined(SOKOL_VULKAN)
22123+ _sg_vk_discard_sampler(smp);
22124+ #elif defined(SOKOL_DUMMY_BACKEND)
22125+ _sg_dummy_discard_sampler(smp);
22126+ #else
22127+ #error("INVALID BACKEND");
22128+ #endif
22129+}
22130+
22131+static inline sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) {
22132+ #if defined(_SOKOL_ANY_GL)
22133+ return _sg_gl_create_shader(shd, desc);
22134+ #elif defined(SOKOL_METAL)
22135+ return _sg_mtl_create_shader(shd, desc);
22136+ #elif defined(SOKOL_D3D11)
22137+ return _sg_d3d11_create_shader(shd, desc);
22138+ #elif defined(SOKOL_WGPU)
22139+ return _sg_wgpu_create_shader(shd, desc);
22140+ #elif defined(SOKOL_VULKAN)
22141+ return _sg_vk_create_shader(shd, desc);
22142+ #elif defined(SOKOL_DUMMY_BACKEND)
22143+ return _sg_dummy_create_shader(shd, desc);
22144+ #else
22145+ #error("INVALID BACKEND");
22146+ #endif
22147+}
22148+
22149+static inline void _sg_discard_shader(_sg_shader_t* shd) {
22150+ #if defined(_SOKOL_ANY_GL)
22151+ _sg_gl_discard_shader(shd);
22152+ #elif defined(SOKOL_METAL)
22153+ _sg_mtl_discard_shader(shd);
22154+ #elif defined(SOKOL_D3D11)
22155+ _sg_d3d11_discard_shader(shd);
22156+ #elif defined(SOKOL_WGPU)
22157+ _sg_wgpu_discard_shader(shd);
22158+ #elif defined(SOKOL_VULKAN)
22159+ _sg_vk_discard_shader(shd);
22160+ #elif defined(SOKOL_DUMMY_BACKEND)
22161+ _sg_dummy_discard_shader(shd);
22162+ #else
22163+ #error("INVALID BACKEND");
22164+ #endif
22165+}
22166+
22167+static inline sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) {
22168+ #if defined(_SOKOL_ANY_GL)
22169+ return _sg_gl_create_pipeline(pip, desc);
22170+ #elif defined(SOKOL_METAL)
22171+ return _sg_mtl_create_pipeline(pip, desc);
22172+ #elif defined(SOKOL_D3D11)
22173+ return _sg_d3d11_create_pipeline(pip, desc);
22174+ #elif defined(SOKOL_WGPU)
22175+ return _sg_wgpu_create_pipeline(pip, desc);
22176+ #elif defined(SOKOL_VULKAN)
22177+ return _sg_vk_create_pipeline(pip, desc);
22178+ #elif defined(SOKOL_DUMMY_BACKEND)
22179+ return _sg_dummy_create_pipeline(pip, desc);
22180+ #else
22181+ #error("INVALID BACKEND");
22182+ #endif
22183+}
22184+
22185+static inline void _sg_discard_pipeline(_sg_pipeline_t* pip) {
22186+ #if defined(_SOKOL_ANY_GL)
22187+ _sg_gl_discard_pipeline(pip);
22188+ #elif defined(SOKOL_METAL)
22189+ _sg_mtl_discard_pipeline(pip);
22190+ #elif defined(SOKOL_D3D11)
22191+ _sg_d3d11_discard_pipeline(pip);
22192+ #elif defined(SOKOL_WGPU)
22193+ _sg_wgpu_discard_pipeline(pip);
22194+ #elif defined(SOKOL_VULKAN)
22195+ _sg_vk_discard_pipeline(pip);
22196+ #elif defined(SOKOL_DUMMY_BACKEND)
22197+ _sg_dummy_discard_pipeline(pip);
22198+ #else
22199+ #error("INVALID BACKEND");
22200+ #endif
22201+}
22202+
22203+static inline sg_resource_state _sg_create_view(_sg_view_t* view, const sg_view_desc* desc) {
22204+ #if defined(_SOKOL_ANY_GL)
22205+ return _sg_gl_create_view(view, desc);
22206+ #elif defined(SOKOL_METAL)
22207+ return _sg_mtl_create_view(view, desc);
22208+ #elif defined(SOKOL_D3D11)
22209+ return _sg_d3d11_create_view(view, desc);
22210+ #elif defined(SOKOL_WGPU)
22211+ return _sg_wgpu_create_view(view, desc);
22212+ #elif defined(SOKOL_VULKAN)
22213+ return _sg_vk_create_view(view, desc);
22214+ #elif defined(SOKOL_DUMMY_BACKEND)
22215+ return _sg_dummy_create_view(view, desc);
22216+ #else
22217+ #error("INVALID BACKEND");
22218+ #endif
22219+}
22220+
22221+static inline void _sg_discard_view(_sg_view_t* view) {
22222+ #if defined(_SOKOL_ANY_GL)
22223+ _sg_gl_discard_view(view);
22224+ #elif defined(SOKOL_METAL)
22225+ _sg_mtl_discard_view(view);
22226+ #elif defined(SOKOL_D3D11)
22227+ _sg_d3d11_discard_view(view);
22228+ #elif defined(SOKOL_WGPU)
22229+ _sg_wgpu_discard_view(view);
22230+ #elif defined(SOKOL_VULKAN)
22231+ _sg_vk_discard_view(view);
22232+ #elif defined(SOKOL_DUMMY_BACKEND)
22233+ _sg_dummy_discard_view(view);
22234+ #else
22235+ #error("INVALID BACKEND");
22236+ #endif
22237+}
22238+
22239+static inline void _sg_begin_pass(const sg_pass* pass, const _sg_attachments_ptrs_t* atts) {
22240+ #if defined(_SOKOL_ANY_GL)
22241+ _sg_gl_begin_pass(pass, atts);
22242+ #elif defined(SOKOL_METAL)
22243+ _sg_mtl_begin_pass(pass, atts);
22244+ #elif defined(SOKOL_D3D11)
22245+ _sg_d3d11_begin_pass(pass, atts);
22246+ #elif defined(SOKOL_WGPU)
22247+ _sg_wgpu_begin_pass(pass, atts);
22248+ #elif defined(SOKOL_VULKAN)
22249+ _sg_vk_begin_pass(pass, atts);
22250+ #elif defined(SOKOL_DUMMY_BACKEND)
22251+ _sg_dummy_begin_pass(pass, atts);
22252+ #else
22253+ #error("INVALID BACKEND");
22254+ #endif
22255+}
22256+
22257+static inline void _sg_end_pass(const _sg_attachments_ptrs_t* atts) {
22258+ #if defined(_SOKOL_ANY_GL)
22259+ _sg_gl_end_pass(atts);
22260+ #elif defined(SOKOL_METAL)
22261+ _sg_mtl_end_pass(atts);
22262+ #elif defined(SOKOL_D3D11)
22263+ _sg_d3d11_end_pass(atts);
22264+ #elif defined(SOKOL_WGPU)
22265+ _sg_wgpu_end_pass(atts);
22266+ #elif defined(SOKOL_VULKAN)
22267+ _sg_vk_end_pass(atts);
22268+ #elif defined(SOKOL_DUMMY_BACKEND)
22269+ _sg_dummy_end_pass(atts);
22270+ #else
22271+ #error("INVALID BACKEND");
22272+ #endif
22273+}
22274+
22275+static inline void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) {
22276+ #if defined(_SOKOL_ANY_GL)
22277+ _sg_gl_apply_viewport(x, y, w, h, origin_top_left);
22278+ #elif defined(SOKOL_METAL)
22279+ _sg_mtl_apply_viewport(x, y, w, h, origin_top_left);
22280+ #elif defined(SOKOL_D3D11)
22281+ _sg_d3d11_apply_viewport(x, y, w, h, origin_top_left);
22282+ #elif defined(SOKOL_WGPU)
22283+ _sg_wgpu_apply_viewport(x, y, w, h, origin_top_left);
22284+ #elif defined(SOKOL_VULKAN)
22285+ _sg_vk_apply_viewport(x, y, w, h, origin_top_left);
22286+ #elif defined(SOKOL_DUMMY_BACKEND)
22287+ _sg_dummy_apply_viewport(x, y, w, h, origin_top_left);
22288+ #else
22289+ #error("INVALID BACKEND");
22290+ #endif
22291+}
22292+
22293+static inline void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) {
22294+ #if defined(_SOKOL_ANY_GL)
22295+ _sg_gl_apply_scissor_rect(x, y, w, h, origin_top_left);
22296+ #elif defined(SOKOL_METAL)
22297+ _sg_mtl_apply_scissor_rect(x, y, w, h, origin_top_left);
22298+ #elif defined(SOKOL_D3D11)
22299+ _sg_d3d11_apply_scissor_rect(x, y, w, h, origin_top_left);
22300+ #elif defined(SOKOL_WGPU)
22301+ _sg_wgpu_apply_scissor_rect(x, y, w, h, origin_top_left);
22302+ #elif defined(SOKOL_VULKAN)
22303+ _sg_vk_apply_scissor_rect(x, y, w, h, origin_top_left);
22304+ #elif defined(SOKOL_DUMMY_BACKEND)
22305+ _sg_dummy_apply_scissor_rect(x, y, w, h, origin_top_left);
22306+ #else
22307+ #error("INVALID BACKEND");
22308+ #endif
22309+}
22310+
22311+static inline void _sg_apply_pipeline(_sg_pipeline_t* pip) {
22312+ #if defined(_SOKOL_ANY_GL)
22313+ _sg_gl_apply_pipeline(pip);
22314+ #elif defined(SOKOL_METAL)
22315+ _sg_mtl_apply_pipeline(pip);
22316+ #elif defined(SOKOL_D3D11)
22317+ _sg_d3d11_apply_pipeline(pip);
22318+ #elif defined(SOKOL_WGPU)
22319+ _sg_wgpu_apply_pipeline(pip);
22320+ #elif defined(SOKOL_VULKAN)
22321+ _sg_vk_apply_pipeline(pip);
22322+ #elif defined(SOKOL_DUMMY_BACKEND)
22323+ _sg_dummy_apply_pipeline(pip);
22324+ #else
22325+ #error("INVALID BACKEND");
22326+ #endif
22327+}
22328+
22329+static inline bool _sg_apply_bindings(_sg_bindings_ptrs_t* bnd) {
22330+ #if defined(_SOKOL_ANY_GL)
22331+ return _sg_gl_apply_bindings(bnd);
22332+ #elif defined(SOKOL_METAL)
22333+ return _sg_mtl_apply_bindings(bnd);
22334+ #elif defined(SOKOL_D3D11)
22335+ return _sg_d3d11_apply_bindings(bnd);
22336+ #elif defined(SOKOL_WGPU)
22337+ return _sg_wgpu_apply_bindings(bnd);
22338+ #elif defined(SOKOL_VULKAN)
22339+ return _sg_vk_apply_bindings(bnd);
22340+ #elif defined(SOKOL_DUMMY_BACKEND)
22341+ return _sg_dummy_apply_bindings(bnd);
22342+ #else
22343+ #error("INVALID BACKEND");
22344+ #endif
22345+}
22346+
22347+static inline void _sg_apply_uniforms(int ub_slot, const sg_range* data) {
22348+ #if defined(_SOKOL_ANY_GL)
22349+ _sg_gl_apply_uniforms(ub_slot, data);
22350+ #elif defined(SOKOL_METAL)
22351+ _sg_mtl_apply_uniforms(ub_slot, data);
22352+ #elif defined(SOKOL_D3D11)
22353+ _sg_d3d11_apply_uniforms(ub_slot, data);
22354+ #elif defined(SOKOL_WGPU)
22355+ _sg_wgpu_apply_uniforms(ub_slot, data);
22356+ #elif defined(SOKOL_VULKAN)
22357+ _sg_vk_apply_uniforms(ub_slot, data);
22358+ #elif defined(SOKOL_DUMMY_BACKEND)
22359+ _sg_dummy_apply_uniforms(ub_slot, data);
22360+ #else
22361+ #error("INVALID BACKEND");
22362+ #endif
22363+}
22364+
22365+static inline void _sg_draw(int base_element, int num_elements, int num_instances, int base_vertex, int base_index) {
22366+ #if defined(_SOKOL_ANY_GL)
22367+ _sg_gl_draw(base_element, num_elements, num_instances, base_vertex, base_index);
22368+ #elif defined(SOKOL_METAL)
22369+ _sg_mtl_draw(base_element, num_elements, num_instances, base_vertex, base_index);
22370+ #elif defined(SOKOL_D3D11)
22371+ _sg_d3d11_draw(base_element, num_elements, num_instances, base_vertex, base_index);
22372+ #elif defined(SOKOL_WGPU)
22373+ _sg_wgpu_draw(base_element, num_elements, num_instances, base_vertex, base_index);
22374+ #elif defined(SOKOL_VULKAN)
22375+ _sg_vk_draw(base_element, num_elements, num_instances, base_vertex, base_index);
22376+ #elif defined(SOKOL_DUMMY_BACKEND)
22377+ _sg_dummy_draw(base_element, num_elements, num_instances, base_vertex, base_index);
22378+ #else
22379+ #error("INVALID BACKEND");
22380+ #endif
22381+}
22382+
22383+static inline void _sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) {
22384+ #if defined(_SOKOL_ANY_GL)
22385+ _sg_gl_dispatch(num_groups_x, num_groups_y, num_groups_z);
22386+ #elif defined(SOKOL_METAL)
22387+ _sg_mtl_dispatch(num_groups_x, num_groups_y, num_groups_z);
22388+ #elif defined(SOKOL_D3D11)
22389+ _sg_d3d11_dispatch(num_groups_x, num_groups_y, num_groups_z);
22390+ #elif defined(SOKOL_WGPU)
22391+ _sg_wgpu_dispatch(num_groups_x, num_groups_y, num_groups_z);
22392+ #elif defined(SOKOL_VULKAN)
22393+ _sg_vk_dispatch(num_groups_x, num_groups_y, num_groups_z);
22394+ #elif defined(SOKOL_DUMMY_BACKEND)
22395+ _sg_dummy_dispatch(num_groups_x, num_groups_y, num_groups_z);
22396+ #else
22397+ #error("INVALID BACKEND");
22398+ #endif
22399+}
22400+
22401+static inline void _sg_commit(void) {
22402+ #if defined(_SOKOL_ANY_GL)
22403+ _sg_gl_commit();
22404+ #elif defined(SOKOL_METAL)
22405+ _sg_mtl_commit();
22406+ #elif defined(SOKOL_D3D11)
22407+ _sg_d3d11_commit();
22408+ #elif defined(SOKOL_WGPU)
22409+ _sg_wgpu_commit();
22410+ #elif defined(SOKOL_VULKAN)
22411+ _sg_vk_commit();
22412+ #elif defined(SOKOL_DUMMY_BACKEND)
22413+ _sg_dummy_commit();
22414+ #else
22415+ #error("INVALID BACKEND");
22416+ #endif
22417+}
22418+
22419+static inline void _sg_update_buffer(_sg_buffer_t* buf, const sg_range* data) {
22420+ #if defined(_SOKOL_ANY_GL)
22421+ _sg_gl_update_buffer(buf, data);
22422+ #elif defined(SOKOL_METAL)
22423+ _sg_mtl_update_buffer(buf, data);
22424+ #elif defined(SOKOL_D3D11)
22425+ _sg_d3d11_update_buffer(buf, data);
22426+ #elif defined(SOKOL_WGPU)
22427+ _sg_wgpu_update_buffer(buf, data);
22428+ #elif defined(SOKOL_VULKAN)
22429+ _sg_vk_update_buffer(buf, data);
22430+ #elif defined(SOKOL_DUMMY_BACKEND)
22431+ _sg_dummy_update_buffer(buf, data);
22432+ #else
22433+ #error("INVALID BACKEND");
22434+ #endif
22435+}
22436+
22437+static inline void _sg_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) {
22438+ #if defined(_SOKOL_ANY_GL)
22439+ _sg_gl_append_buffer(buf, data, new_frame);
22440+ #elif defined(SOKOL_METAL)
22441+ _sg_mtl_append_buffer(buf, data, new_frame);
22442+ #elif defined(SOKOL_D3D11)
22443+ _sg_d3d11_append_buffer(buf, data, new_frame);
22444+ #elif defined(SOKOL_WGPU)
22445+ _sg_wgpu_append_buffer(buf, data, new_frame);
22446+ #elif defined(SOKOL_VULKAN)
22447+ _sg_vk_append_buffer(buf, data, new_frame);
22448+ #elif defined(SOKOL_DUMMY_BACKEND)
22449+ _sg_dummy_append_buffer(buf, data, new_frame);
22450+ #else
22451+ #error("INVALID BACKEND");
22452+ #endif
22453+}
22454+
22455+static inline void _sg_update_image(_sg_image_t* img, const sg_image_data* data) {
22456+ #if defined(_SOKOL_ANY_GL)
22457+ _sg_gl_update_image(img, data);
22458+ #elif defined(SOKOL_METAL)
22459+ _sg_mtl_update_image(img, data);
22460+ #elif defined(SOKOL_D3D11)
22461+ _sg_d3d11_update_image(img, data);
22462+ #elif defined(SOKOL_WGPU)
22463+ _sg_wgpu_update_image(img, data);
22464+ #elif defined(SOKOL_VULKAN)
22465+ _sg_vk_update_image(img, data);
22466+ #elif defined(SOKOL_DUMMY_BACKEND)
22467+ _sg_dummy_update_image(img, data);
22468+ #else
22469+ #error("INVALID BACKEND");
22470+ #endif
22471+}
22472+
22473+static inline void _sg_push_debug_group(const char* name) {
22474+ #if defined(SOKOL_METAL)
22475+ _sg_mtl_push_debug_group(name);
22476+ #else
22477+ _SOKOL_UNUSED(name);
22478+ #endif
22479+}
22480+
22481+static inline void _sg_pop_debug_group(void) {
22482+ #if defined(SOKOL_METAL)
22483+ _sg_mtl_pop_debug_group();
22484+ #endif
22485+}
22486+
22487+// ██ ██ █████ ██ ██ ██████ █████ ████████ ██ ██████ ███ ██
22488+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
22489+// ██ ██ ███████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██
22490+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
22491+// ████ ██ ██ ███████ ██ ██████ ██ ██ ██ ██ ██████ ██ ████
22492+//
22493+// >>validation
22494+#if defined(SOKOL_DEBUG)
22495+_SOKOL_PRIVATE void _sg_validate_begin(void) {
22496+ _sg.validate_error = SG_LOGITEM_OK;
22497+}
22498+
22499+_SOKOL_PRIVATE bool _sg_validate_end(void) {
22500+ if (_sg.validate_error != SG_LOGITEM_OK) {
22501+ #if !defined(SOKOL_VALIDATE_NON_FATAL)
22502+ _SG_PANIC(VALIDATION_FAILED);
22503+ return false;
22504+ #else
22505+ return false;
22506+ #endif
22507+ } else {
22508+ return true;
22509+ }
22510+}
22511+#endif
22512+
22513+_SOKOL_PRIVATE bool _sg_one(bool b0, bool b1, bool b2) {
22514+ return (b0 && !b1 && !b2) || (!b0 && b1 && !b2) || (!b0 && !b1 && b2);
22515+}
22516+
22517+_SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) {
22518+ #if !defined(SOKOL_DEBUG)
22519+ _SOKOL_UNUSED(desc);
22520+ return true;
22521+ #else
22522+ if (_sg.desc.disable_validation) {
22523+ return true;
22524+ }
22525+ SOKOL_ASSERT(desc);
22526+ _sg_validate_begin();
22527+ _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_BUFFERDESC_CANARY);
22528+ _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_BUFFERDESC_CANARY);
22529+ _SG_VALIDATE(desc->size > 0, VALIDATE_BUFFERDESC_EXPECT_NONZERO_SIZE);
22530+ _SG_VALIDATE(_sg_one(desc->usage.immutable, desc->usage.dynamic_update, desc->usage.stream_update), VALIDATE_BUFFERDESC_IMMUTABLE_DYNAMIC_STREAM);
22531+ if (_sg.features.separate_buffer_types) {
22532+ _SG_VALIDATE(_sg_one(desc->usage.vertex_buffer, desc->usage.index_buffer, desc->usage.storage_buffer), VALIDATE_BUFFERDESC_SEPARATE_BUFFER_TYPES);
22533+ }
22534+ bool injected = (0 != desc->gl_buffers[0]) ||
22535+ (0 != desc->mtl_buffers[0]) ||
22536+ (0 != desc->d3d11_buffer) ||
22537+ (0 != desc->wgpu_buffer);
22538+ if (!injected && desc->usage.immutable) {
22539+ if (desc->data.ptr) {
22540+ _SG_VALIDATE(desc->size == desc->data.size, VALIDATE_BUFFERDESC_EXPECT_MATCHING_DATA_SIZE);
22541+ } else {
22542+ _SG_VALIDATE(desc->usage.storage_buffer, VALIDATE_BUFFERDESC_EXPECT_DATA);
22543+ _SG_VALIDATE(desc->data.size == 0, VALIDATE_BUFFERDESC_EXPECT_ZERO_DATA_SIZE);
22544+ }
22545+ } else {
22546+ _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_BUFFERDESC_EXPECT_NO_DATA);
22547+ _SG_VALIDATE(desc->data.size == 0, VALIDATE_BUFFERDESC_EXPECT_ZERO_DATA_SIZE);
22548+ }
22549+ if (desc->usage.storage_buffer) {
22550+ _SG_VALIDATE(_sg.features.compute, VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED);
22551+ _SG_VALIDATE(_sg_multiple_u64(desc->size, 4), VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4);
22552+ }
22553+ return _sg_validate_end();
22554+ #endif
22555+}
22556+
22557+_SOKOL_PRIVATE void _sg_validate_image_data(const sg_image_data* data, sg_pixel_format fmt, int width, int height, int num_mips, int num_slices) {
22558+ #if !defined(SOKOL_DEBUG)
22559+ _SOKOL_UNUSED(data);
22560+ _SOKOL_UNUSED(fmt);
22561+ _SOKOL_UNUSED(width);
22562+ _SOKOL_UNUSED(height);
22563+ _SOKOL_UNUSED(num_mips);
22564+ _SOKOL_UNUSED(num_slices);
22565+ #else
22566+ for (int mip_index = 0; mip_index < num_mips; mip_index++) {
22567+ const bool has_data = data->mip_levels[mip_index].ptr != 0;
22568+ const bool has_size = data->mip_levels[mip_index].size > 0;
22569+ _SG_VALIDATE(has_data && has_size, VALIDATE_IMAGEDATA_NODATA);
22570+ const int mip_width = _sg_miplevel_dim(width, mip_index);
22571+ const int mip_height = _sg_miplevel_dim(height, mip_index);
22572+ const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1);
22573+ const int expected_size = bytes_per_slice * num_slices;
22574+ _SG_VALIDATE(expected_size == (int)data->mip_levels[mip_index].size, VALIDATE_IMAGEDATA_DATA_SIZE);
22575+ }
22576+ #endif
22577+}
22578+
22579+_SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) {
22580+ #if !defined(SOKOL_DEBUG)
22581+ _SOKOL_UNUSED(desc);
22582+ return true;
22583+ #else
22584+ if (_sg.desc.disable_validation) {
22585+ return true;
22586+ }
22587+ SOKOL_ASSERT(desc);
22588+ const sg_image_usage* usg = &desc->usage;
22589+ const bool any_attachment = usg->color_attachment || usg->resolve_attachment || usg->depth_stencil_attachment;
22590+ _sg_validate_begin();
22591+ _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_IMAGEDESC_CANARY);
22592+ _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_IMAGEDESC_CANARY);
22593+ _SG_VALIDATE(_sg_one(usg->immutable, usg->dynamic_update, usg->stream_update), VALIDATE_IMAGEDESC_IMMUTABLE_DYNAMIC_STREAM);
22594+ _SG_VALIDATE(!(usg->color_attachment && usg->depth_stencil_attachment), VALIDATE_IMAGEDESC_ATTACHMENT_COLOR_DEPTH_STENCIL);
22595+ switch (desc->type) {
22596+ case SG_IMAGETYPE_2D:
22597+ _SG_VALIDATE(desc->num_slices == 1, VALIDATE_IMAGEDESC_IMAGETYPE_2D_NUMSLICES);
22598+ break;
22599+ case SG_IMAGETYPE_CUBE:
22600+ _SG_VALIDATE(desc->num_slices == 6, VALIDATE_IMAGEDESC_IMAGETYPE_CUBE_NUMSLICES);
22601+ break;
22602+ case SG_IMAGETYPE_ARRAY:
22603+ _SG_VALIDATE((desc->num_slices >= 1) && (desc->num_slices <= _sg.limits.max_image_array_layers), VALIDATE_IMAGEDESC_IMAGETYPE_ARRAY_NUMSLICES);
22604+ break;
22605+ case SG_IMAGETYPE_3D:
22606+ _SG_VALIDATE((desc->num_slices >= 1) && (desc->num_slices <= _sg.limits.max_image_size_3d), VALIDATE_IMAGEDESC_IMAGETYPE_3D_NUMSLICES);
22607+ break;
22608+ default:
22609+ SOKOL_UNREACHABLE;
22610+ break;
22611+ }
22612+ _SG_VALIDATE(desc->width > 0, VALIDATE_IMAGEDESC_WIDTH);
22613+ _SG_VALIDATE(desc->height > 0, VALIDATE_IMAGEDESC_HEIGHT);
22614+ const sg_pixel_format fmt = desc->pixel_format;
22615+ const bool injected = (0 != desc->gl_textures[0]) ||
22616+ (0 != desc->mtl_textures[0]) ||
22617+ (0 != desc->d3d11_texture) ||
22618+ (0 != desc->wgpu_texture);
22619+ if (_sg_is_depth_or_depth_stencil_format(fmt)) {
22620+ _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE);
22621+ }
22622+ if (any_attachment || usg->storage_image) {
22623+ SOKOL_ASSERT(((int)fmt >= 0) && ((int)fmt < _SG_PIXELFORMAT_NUM));
22624+ _SG_VALIDATE(usg->immutable, VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_IMMUTABLE);
22625+ _SG_VALIDATE(desc->data.mip_levels[0].ptr==0, VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_NO_DATA);
22626+ if (any_attachment) {
22627+ _SG_VALIDATE(_sg.formats[fmt].render, VALIDATE_IMAGEDESC_ATTACHMENT_PIXELFORMAT);
22628+ if (usg->resolve_attachment) {
22629+ _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_ATTACHMENT_RESOLVE_EXPECT_NO_MSAA);
22630+ }
22631+ if (desc->sample_count > 1) {
22632+ _SG_VALIDATE(_sg.formats[fmt].msaa, VALIDATE_IMAGEDESC_ATTACHMENT_NO_MSAA_SUPPORT);
22633+ _SG_VALIDATE(desc->num_mipmaps == 1, VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_NUM_MIPMAPS);
22634+ _SG_VALIDATE(desc->type != SG_IMAGETYPE_ARRAY, VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_ARRAY_IMAGE);
22635+ _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_3D_IMAGE);
22636+ _SG_VALIDATE(desc->type != SG_IMAGETYPE_CUBE, VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_CUBE_IMAGE);
22637+ }
22638+ } else if (usg->storage_image) {
22639+ _SG_VALIDATE(_sg_is_valid_storage_image_format(fmt), VALIDATE_IMAGEDESC_STORAGEIMAGE_PIXELFORMAT);
22640+ // D3D11 doesn't allow multisampled UAVs (see: https://github.com/gpuweb/gpuweb/issues/513)
22641+ _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_STORAGEIMAGE_EXPECT_NO_MSAA);
22642+ }
22643+ } else {
22644+ _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_MSAA_BUT_NO_ATTACHMENT);
22645+ const bool valid_nonrt_fmt = !_sg_is_valid_attachment_depth_format(fmt);
22646+ _SG_VALIDATE(valid_nonrt_fmt, VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT);
22647+ const bool is_compressed = _sg_is_compressed_pixel_format(desc->pixel_format);
22648+ if (is_compressed) {
22649+ _SG_VALIDATE(usg->immutable, VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE);
22650+ }
22651+ if (!injected && usg->immutable) {
22652+ // image desc must have valid data
22653+ _sg_validate_image_data(&desc->data,
22654+ desc->pixel_format,
22655+ desc->width,
22656+ desc->height,
22657+ desc->num_mipmaps,
22658+ desc->num_slices);
22659+ } else {
22660+ // image desc must not have data
22661+ for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) {
22662+ const bool no_data = 0 == desc->data.mip_levels[mip_index].ptr;
22663+ const bool no_size = 0 == desc->data.mip_levels[mip_index].size;
22664+ if (injected) {
22665+ _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_INJECTED_NO_DATA);
22666+ }
22667+ if (!usg->immutable) {
22668+ _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA);
22669+ }
22670+ }
22671+ }
22672+ }
22673+ return _sg_validate_end();
22674+ #endif
22675+}
22676+
22677+_SOKOL_PRIVATE bool _sg_validate_sampler_desc(const sg_sampler_desc* desc) {
22678+ #if !defined(SOKOL_DEBUG)
22679+ _SOKOL_UNUSED(desc);
22680+ return true;
22681+ #else
22682+ if (_sg.desc.disable_validation) {
22683+ return true;
22684+ }
22685+ SOKOL_ASSERT(desc);
22686+ _sg_validate_begin();
22687+ _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SAMPLERDESC_CANARY);
22688+ _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SAMPLERDESC_CANARY);
22689+ // restriction from WebGPU: when anisotropy > 1, all filters must be linear
22690+ if (desc->max_anisotropy > 1) {
22691+ _SG_VALIDATE((desc->min_filter == SG_FILTER_LINEAR)
22692+ && (desc->mag_filter == SG_FILTER_LINEAR)
22693+ && (desc->mipmap_filter == SG_FILTER_LINEAR),
22694+ VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING);
22695+ }
22696+ return _sg_validate_end();
22697+ #endif
22698+}
22699+
22700+typedef struct {
22701+ uint64_t lo, hi;
22702+} _sg_u128_t;
22703+
22704+_SOKOL_PRIVATE _sg_u128_t _sg_u128(void) {
22705+ _SG_STRUCT(_sg_u128_t, res);
22706+ return res;
22707+}
22708+
22709+_SOKOL_PRIVATE _sg_u128_t _sg_validate_set_slot_bit(_sg_u128_t bits, sg_shader_stage stage, uint8_t slot) {
22710+ switch (stage) {
22711+ case SG_SHADERSTAGE_NONE:
22712+ SOKOL_ASSERT(slot < 128);
22713+ if (slot < 64) {
22714+ bits.lo |= 1ULL << slot;
22715+ } else {
22716+ bits.hi |= 1ULL << (slot - 64);
22717+ }
22718+ break;
22719+ case SG_SHADERSTAGE_VERTEX:
22720+ SOKOL_ASSERT(slot < 64);
22721+ bits.lo |= 1ULL << slot;
22722+ break;
22723+ case SG_SHADERSTAGE_FRAGMENT:
22724+ SOKOL_ASSERT(slot < 64);
22725+ bits.hi |= 1ULL << slot;
22726+ break;
22727+ case SG_SHADERSTAGE_COMPUTE:
22728+ SOKOL_ASSERT(slot < 64);
22729+ bits.lo |= 1ULL << slot;
22730+ break;
22731+ default:
22732+ SOKOL_UNREACHABLE;
22733+ break;
22734+ }
22735+ return bits;
22736+}
22737+
22738+_SOKOL_PRIVATE bool _sg_validate_slot_bits(_sg_u128_t bits, sg_shader_stage stage, uint8_t slot) {
22739+ _sg_u128_t mask = _sg_u128();
22740+ switch (stage) {
22741+ case SG_SHADERSTAGE_NONE:
22742+ SOKOL_ASSERT(slot < 128);
22743+ if (slot < 64) {
22744+ mask.lo = 1ULL << slot;
22745+ } else {
22746+ mask.hi = 1ULL << (slot - 64);
22747+ }
22748+ break;
22749+ case SG_SHADERSTAGE_VERTEX:
22750+ SOKOL_ASSERT(slot < 64);
22751+ mask.lo = 1ULL << slot;
22752+ break;
22753+ case SG_SHADERSTAGE_FRAGMENT:
22754+ SOKOL_ASSERT(slot < 64);
22755+ mask.hi = 1ULL << slot;
22756+ break;
22757+ case SG_SHADERSTAGE_COMPUTE:
22758+ SOKOL_ASSERT(slot < 64);
22759+ mask.lo = 1ULL << slot;
22760+ break;
22761+ default:
22762+ SOKOL_UNREACHABLE;
22763+ break;
22764+ }
22765+ return ((bits.lo & mask.lo) == 0) && ((bits.hi & mask.hi) == 0);
22766+}
22767+
22768+_SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) {
22769+ #if !defined(SOKOL_DEBUG)
22770+ _SOKOL_UNUSED(desc);
22771+ return true;
22772+ #else
22773+ if (_sg.desc.disable_validation) {
22774+ return true;
22775+ }
22776+ SOKOL_ASSERT(desc);
22777+ bool is_compute_shader = (desc->compute_func.source != 0) || (desc->compute_func.bytecode.ptr != 0);
22778+ _sg_validate_begin();
22779+ _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SHADERDESC_CANARY);
22780+ _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SHADERDESC_CANARY);
22781+ #if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) || defined(SOKOL_WGPU)
22782+ // on GL or WebGPU, must provide shader source code
22783+ if (is_compute_shader) {
22784+ _SG_VALIDATE(0 != desc->compute_func.source, VALIDATE_SHADERDESC_COMPUTE_SOURCE);
22785+ } else {
22786+ _SG_VALIDATE(0 != desc->vertex_func.source, VALIDATE_SHADERDESC_VERTEX_SOURCE);
22787+ _SG_VALIDATE(0 != desc->fragment_func.source, VALIDATE_SHADERDESC_FRAGMENT_SOURCE);
22788+ }
22789+ #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11)
22790+ // on Metal or D3D11, must provide shader source code or byte code
22791+ if (is_compute_shader) {
22792+ _SG_VALIDATE((0 != desc->compute_func.source) || (0 != desc->compute_func.bytecode.ptr), VALIDATE_SHADERDESC_COMPUTE_SOURCE_OR_BYTECODE);
22793+ } else {
22794+ _SG_VALIDATE((0 != desc->vertex_func.source)|| (0 != desc->vertex_func.bytecode.ptr), VALIDATE_SHADERDESC_VERTEX_SOURCE_OR_BYTECODE);
22795+ _SG_VALIDATE((0 != desc->fragment_func.source) || (0 != desc->fragment_func.bytecode.ptr), VALIDATE_SHADERDESC_FRAGMENT_SOURCE_OR_BYTECODE);
22796+ }
22797+ #else
22798+ // Dummy Backend, don't require source or bytecode
22799+ #endif
22800+ if (is_compute_shader) {
22801+ _SG_VALIDATE((0 == desc->vertex_func.source) && (0 == desc->vertex_func.bytecode.ptr), VALIDATE_SHADERDESC_INVALID_SHADER_COMBO);
22802+ _SG_VALIDATE((0 == desc->fragment_func.source) && (0 == desc->fragment_func.bytecode.ptr), VALIDATE_SHADERDESC_INVALID_SHADER_COMBO);
22803+ } else {
22804+ _SG_VALIDATE((0 == desc->compute_func.source) && (0 == desc->compute_func.bytecode.ptr), VALIDATE_SHADERDESC_INVALID_SHADER_COMBO);
22805+ }
22806+ #if defined(SOKOL_METAL)
22807+ if (is_compute_shader) {
22808+ int x = desc->mtl_threads_per_threadgroup.x;
22809+ int y = desc->mtl_threads_per_threadgroup.y;
22810+ int z = desc->mtl_threads_per_threadgroup.z;
22811+ _SG_VALIDATE((x > 0) && (y > 0) && (z > 0), VALIDATE_SHADERDESC_METAL_THREADS_PER_THREADGROUP_INITIALIZED);
22812+ _SG_VALIDATE(((x * y * z) & 31) == 0, VALIDATE_SHADERDESC_METAL_THREADS_PER_THREADGROUP_MULTIPLE_32);
22813+ }
22814+ #endif
22815+ for (size_t i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) {
22816+ if (desc->attrs[i].glsl_name) {
22817+ _SG_VALIDATE(strlen(desc->attrs[i].glsl_name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG);
22818+ }
22819+ if (desc->attrs[i].hlsl_sem_name) {
22820+ _SG_VALIDATE(strlen(desc->attrs[i].hlsl_sem_name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG);
22821+ }
22822+ }
22823+ // if shader byte code, the size must also be provided
22824+ if (0 != desc->vertex_func.bytecode.ptr) {
22825+ _SG_VALIDATE(desc->vertex_func.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE);
22826+ }
22827+ if (0 != desc->fragment_func.bytecode.ptr) {
22828+ _SG_VALIDATE(desc->fragment_func.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE);
22829+ }
22830+ if (0 != desc->compute_func.bytecode.ptr) {
22831+ _SG_VALIDATE(desc->compute_func.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE);
22832+ }
22833+
22834+ #if defined(SOKOL_METAL)
22835+ _sg_u128_t msl_buf_bits = _sg_u128();
22836+ _sg_u128_t msl_tex_bits = _sg_u128();
22837+ _sg_u128_t msl_smp_bits = _sg_u128();
22838+ #elif defined(SOKOL_D3D11)
22839+ _sg_u128_t hlsl_buf_bits = _sg_u128();
22840+ _sg_u128_t hlsl_srv_bits = _sg_u128();
22841+ _sg_u128_t hlsl_uav_bits = _sg_u128();
22842+ _sg_u128_t hlsl_smp_bits = _sg_u128();
22843+ #elif defined(_SOKOL_ANY_GL)
22844+ _sg_u128_t glsl_sbuf_bnd_bits = _sg_u128();
22845+ _sg_u128_t glsl_simg_bnd_bits = _sg_u128();
22846+ #elif defined(SOKOL_WGPU)
22847+ _sg_u128_t wgsl_group0_bits = _sg_u128();
22848+ _sg_u128_t wgsl_group1_bits = _sg_u128();
22849+ #elif defined(SOKOL_VULKAN)
22850+ _sg_u128_t spirv_set0_bits = _sg_u128();
22851+ _sg_u128_t spirv_set1_bits = _sg_u128();
22852+ #endif
22853+ for (size_t ub_idx = 0; ub_idx < SG_MAX_UNIFORMBLOCK_BINDSLOTS; ub_idx++) {
22854+ const sg_shader_uniform_block* ub_desc = &desc->uniform_blocks[ub_idx];
22855+ if (ub_desc->stage == SG_SHADERSTAGE_NONE) {
22856+ continue;
22857+ }
22858+ _SG_VALIDATE(ub_desc->size > 0, VALIDATE_SHADERDESC_UNIFORMBLOCK_SIZE_IS_ZERO);
22859+ #if defined(SOKOL_METAL)
22860+ _SG_VALIDATE(_sg_validate_slot_bits(msl_buf_bits, ub_desc->stage, ub_desc->msl_buffer_n), VALIDATE_SHADERDESC_UNIFORMBLOCK_METAL_BUFFER_SLOT_COLLISION);
22861+ msl_buf_bits = _sg_validate_set_slot_bit(msl_buf_bits, ub_desc->stage, ub_desc->msl_buffer_n);
22862+ #elif defined(SOKOL_D3D11)
22863+ _SG_VALIDATE(_sg_validate_slot_bits(hlsl_buf_bits, ub_desc->stage, ub_desc->hlsl_register_b_n), VALIDATE_SHADERDESC_UNIFORMBLOCK_HLSL_REGISTER_B_COLLISION);
22864+ hlsl_buf_bits = _sg_validate_set_slot_bit(hlsl_buf_bits, ub_desc->stage, ub_desc->hlsl_register_b_n);
22865+ #elif defined(SOKOL_WGPU)
22866+ _SG_VALIDATE(_sg_validate_slot_bits(wgsl_group0_bits, SG_SHADERSTAGE_NONE, ub_desc->wgsl_group0_binding_n), VALIDATE_SHADERDESC_UNIFORMBLOCK_WGSL_GROUP0_BINDING_COLLISION);
22867+ wgsl_group0_bits = _sg_validate_set_slot_bit(wgsl_group0_bits, SG_SHADERSTAGE_NONE, ub_desc->wgsl_group0_binding_n);
22868+ #elif defined(SOKOL_VULKAN)
22869+ _SG_VALIDATE(_sg_validate_slot_bits(spirv_set0_bits, SG_SHADERSTAGE_NONE, ub_desc->spirv_set0_binding_n), VALIDATE_SHADERDESC_UNIFORMBLOCK_SPIRV_SET0_BINDING_COLLISION);
22870+ spirv_set0_bits = _sg_validate_set_slot_bit(spirv_set0_bits, SG_SHADERSTAGE_NONE, ub_desc->spirv_set0_binding_n);
22871+ #endif
22872+ #if defined(_SOKOL_ANY_GL)
22873+ bool uniforms_continuous = true;
22874+ uint32_t uniform_offset = 0;
22875+ int num_uniforms = 0;
22876+ for (size_t u_index = 0; u_index < SG_MAX_UNIFORMBLOCK_MEMBERS; u_index++) {
22877+ const sg_glsl_shader_uniform* u_desc = &ub_desc->glsl_uniforms[u_index];
22878+ if (u_desc->type != SG_UNIFORMTYPE_INVALID) {
22879+ _SG_VALIDATE(uniforms_continuous, VALIDATE_SHADERDESC_UNIFORMBLOCK_NO_CONT_MEMBERS);
22880+ _SG_VALIDATE(u_desc->glsl_name, VALIDATE_SHADERDESC_UNIFORMBLOCK_UNIFORM_GLSL_NAME);
22881+ const int array_count = u_desc->array_count;
22882+ _SG_VALIDATE(array_count > 0, VALIDATE_SHADERDESC_UNIFORMBLOCK_ARRAY_COUNT);
22883+ const uint32_t u_align = _sg_uniform_alignment(u_desc->type, array_count, ub_desc->layout);
22884+ const uint32_t u_size = _sg_uniform_size(u_desc->type, array_count, ub_desc->layout);
22885+ uniform_offset = _sg_align_u32(uniform_offset, u_align);
22886+ uniform_offset += u_size;
22887+ num_uniforms++;
22888+ // with std140, arrays are only allowed for FLOAT4, INT4, MAT4
22889+ if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) {
22890+ if (array_count > 1) {
22891+ _SG_VALIDATE((u_desc->type == SG_UNIFORMTYPE_FLOAT4) || (u_desc->type == SG_UNIFORMTYPE_INT4) || (u_desc->type == SG_UNIFORMTYPE_MAT4), VALIDATE_SHADERDESC_UNIFORMBLOCK_STD140_ARRAY_TYPE);
22892+ }
22893+ }
22894+ } else {
22895+ uniforms_continuous = false;
22896+ }
22897+ }
22898+ if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) {
22899+ uniform_offset = _sg_align_u32(uniform_offset, 16);
22900+ }
22901+ _SG_VALIDATE((size_t)uniform_offset == ub_desc->size, VALIDATE_SHADERDESC_UNIFORMBLOCK_SIZE_MISMATCH);
22902+ _SG_VALIDATE(num_uniforms > 0, VALIDATE_SHADERDESC_UNIFORMBLOCK_NO_MEMBERS);
22903+ #endif
22904+ }
22905+
22906+ uint32_t texview_slot_mask = 0;
22907+ for (size_t view_idx = 0; view_idx < SG_MAX_VIEW_BINDSLOTS; view_idx++) {
22908+ const sg_shader_view* view_desc = &desc->views[view_idx];
22909+ if (view_desc->texture.stage != SG_SHADERSTAGE_NONE) {
22910+ const sg_shader_texture_view* tex_desc = &view_desc->texture;
22911+ texview_slot_mask |= (1 << view_idx);
22912+ #if defined(SOKOL_METAL)
22913+ _SG_VALIDATE(_sg_validate_slot_bits(msl_tex_bits, tex_desc->stage, tex_desc->msl_texture_n), VALIDATE_SHADERDESC_VIEW_TEXTURE_METAL_TEXTURE_SLOT_COLLISION);
22914+ msl_tex_bits = _sg_validate_set_slot_bit(msl_tex_bits, tex_desc->stage, tex_desc->msl_texture_n);
22915+ #elif defined(SOKOL_D3D11)
22916+ _SG_VALIDATE(_sg_validate_slot_bits(hlsl_srv_bits, tex_desc->stage, tex_desc->hlsl_register_t_n), VALIDATE_SHADERDESC_VIEW_TEXTURE_HLSL_REGISTER_T_COLLISION);
22917+ hlsl_srv_bits = _sg_validate_set_slot_bit(hlsl_srv_bits, tex_desc->stage, tex_desc->hlsl_register_t_n);
22918+ #elif defined(SOKOL_WGPU)
22919+ _SG_VALIDATE(_sg_validate_slot_bits(wgsl_group1_bits, SG_SHADERSTAGE_NONE, tex_desc->wgsl_group1_binding_n), VALIDATE_SHADERDESC_VIEW_TEXTURE_WGSL_GROUP1_BINDING_COLLISION);
22920+ wgsl_group1_bits = _sg_validate_set_slot_bit(wgsl_group1_bits, SG_SHADERSTAGE_NONE, tex_desc->wgsl_group1_binding_n);
22921+ #elif defined(SOKOL_VULKAN)
22922+ _SG_VALIDATE(_sg_validate_slot_bits(spirv_set1_bits, SG_SHADERSTAGE_NONE, tex_desc->spirv_set1_binding_n), VALIDATE_SHADERDESC_VIEW_TEXTURE_SPIRV_SET1_BINDING_COLLISION);
22923+ spirv_set1_bits = _sg_validate_set_slot_bit(spirv_set1_bits, SG_SHADERSTAGE_NONE, tex_desc->spirv_set1_binding_n);
22924+ #elif defined(SOKOL_DUMMY_BACKEND) || defined(_SOKOL_ANY_GL)
22925+ _SOKOL_UNUSED(tex_desc);
22926+ #endif
22927+ } else if (view_desc->storage_buffer.stage != SG_SHADERSTAGE_NONE) {
22928+ const sg_shader_storage_buffer_view* sbuf_desc = &view_desc->storage_buffer;
22929+ #if defined(SOKOL_METAL)
22930+ _SG_VALIDATE(_sg_validate_slot_bits(msl_buf_bits, sbuf_desc->stage, sbuf_desc->msl_buffer_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_METAL_BUFFER_SLOT_COLLISION);
22931+ msl_buf_bits = _sg_validate_set_slot_bit(msl_buf_bits, sbuf_desc->stage, sbuf_desc->msl_buffer_n);
22932+ #elif defined(SOKOL_D3D11)
22933+ if (sbuf_desc->readonly) {
22934+ _SG_VALIDATE(_sg_validate_slot_bits(hlsl_srv_bits, sbuf_desc->stage, sbuf_desc->hlsl_register_t_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_HLSL_REGISTER_T_COLLISION);
22935+ hlsl_srv_bits = _sg_validate_set_slot_bit(hlsl_srv_bits, sbuf_desc->stage, sbuf_desc->hlsl_register_t_n);
22936+ } else {
22937+ _SG_VALIDATE(_sg_validate_slot_bits(hlsl_uav_bits, sbuf_desc->stage, sbuf_desc->hlsl_register_u_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_HLSL_REGISTER_U_COLLISION);
22938+ hlsl_uav_bits = _sg_validate_set_slot_bit(hlsl_uav_bits, sbuf_desc->stage, sbuf_desc->hlsl_register_u_n);
22939+ }
22940+ #elif defined(_SOKOL_ANY_GL)
22941+ _SG_VALIDATE(_sg_validate_slot_bits(glsl_sbuf_bnd_bits, SG_SHADERSTAGE_NONE, sbuf_desc->glsl_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_GLSL_BINDING_COLLISION);
22942+ glsl_sbuf_bnd_bits = _sg_validate_set_slot_bit(glsl_sbuf_bnd_bits, SG_SHADERSTAGE_NONE, sbuf_desc->glsl_binding_n);
22943+ #elif defined(SOKOL_WGPU)
22944+ _SG_VALIDATE(_sg_validate_slot_bits(wgsl_group1_bits, SG_SHADERSTAGE_NONE, sbuf_desc->wgsl_group1_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_WGSL_GROUP1_BINDING_COLLISION);
22945+ wgsl_group1_bits = _sg_validate_set_slot_bit(wgsl_group1_bits, SG_SHADERSTAGE_NONE, sbuf_desc->wgsl_group1_binding_n);
22946+ #elif defined(SOKOL_VULKAN)
22947+ _SG_VALIDATE(_sg_validate_slot_bits(spirv_set1_bits, SG_SHADERSTAGE_NONE, sbuf_desc->spirv_set1_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEBUFFER_SPIRV_SET1_BINDING_COLLISION);
22948+ spirv_set1_bits = _sg_validate_set_slot_bit(spirv_set1_bits, SG_SHADERSTAGE_NONE, sbuf_desc->spirv_set1_binding_n);
22949+ #elif defined(SOKOL_DUMMY_BACKEND)
22950+ _SOKOL_UNUSED(sbuf_desc);
22951+ #endif
22952+ } else if (view_desc->storage_image.stage != SG_SHADERSTAGE_NONE) {
22953+ const sg_shader_storage_image_view* simg_desc = &view_desc->storage_image;
22954+ _SG_VALIDATE(simg_desc->stage == SG_SHADERSTAGE_COMPUTE, VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_EXPECT_COMPUTE_STAGE);
22955+ #if defined(SOKOL_METAL)
22956+ _SG_VALIDATE(_sg_validate_slot_bits(msl_tex_bits, simg_desc->stage, simg_desc->msl_texture_n), VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_METAL_TEXTURE_SLOT_COLLISION);
22957+ msl_tex_bits = _sg_validate_set_slot_bit(msl_tex_bits, simg_desc->stage, simg_desc->msl_texture_n);
22958+ #elif defined(SOKOL_D3D11)
22959+ _SG_VALIDATE(_sg_validate_slot_bits(hlsl_uav_bits, simg_desc->stage, simg_desc->hlsl_register_u_n), VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_HLSL_REGISTER_U_COLLISION);
22960+ hlsl_uav_bits = _sg_validate_set_slot_bit(hlsl_uav_bits, simg_desc->stage, simg_desc->hlsl_register_u_n);
22961+ #elif defined(_SOKOL_ANY_GL)
22962+ _SG_VALIDATE(_sg_validate_slot_bits(glsl_simg_bnd_bits, SG_SHADERSTAGE_NONE, simg_desc->glsl_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_GLSL_BINDING_COLLISION);
22963+ glsl_simg_bnd_bits = _sg_validate_set_slot_bit(glsl_simg_bnd_bits, SG_SHADERSTAGE_NONE, simg_desc->glsl_binding_n);
22964+ #elif defined(SOKOL_WGPU)
22965+ _SG_VALIDATE(_sg_validate_slot_bits(wgsl_group1_bits, SG_SHADERSTAGE_NONE, simg_desc->wgsl_group1_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_WGSL_GROUP1_BINDING_COLLISION);
22966+ wgsl_group1_bits = _sg_validate_set_slot_bit(wgsl_group1_bits, SG_SHADERSTAGE_NONE, simg_desc->wgsl_group1_binding_n);
22967+ #elif defined(SOKOL_VULKAN)
22968+ _SG_VALIDATE(_sg_validate_slot_bits(spirv_set1_bits, SG_SHADERSTAGE_NONE, simg_desc->spirv_set1_binding_n), VALIDATE_SHADERDESC_VIEW_STORAGEIMAGE_SPIRV_SET1_BINDING_COLLISION);
22969+ spirv_set1_bits = _sg_validate_set_slot_bit(spirv_set1_bits, SG_SHADERSTAGE_NONE, simg_desc->spirv_set1_binding_n);
22970+ #endif
22971+ }
22972+ }
22973+
22974+ uint32_t smp_slot_mask = 0;
22975+ for (size_t smp_idx = 0; smp_idx < SG_MAX_SAMPLER_BINDSLOTS; smp_idx++) {
22976+ const sg_shader_sampler* smp_desc = &desc->samplers[smp_idx];
22977+ if (smp_desc->stage == SG_SHADERSTAGE_NONE) {
22978+ continue;
22979+ }
22980+ smp_slot_mask |= (1 << smp_idx);
22981+ #if defined(SOKOL_METAL)
22982+ _SG_VALIDATE(_sg_validate_slot_bits(msl_smp_bits, smp_desc->stage, smp_desc->msl_sampler_n), VALIDATE_SHADERDESC_SAMPLER_METAL_SAMPLER_SLOT_COLLISION);
22983+ msl_smp_bits = _sg_validate_set_slot_bit(msl_smp_bits, smp_desc->stage, smp_desc->msl_sampler_n);
22984+ #elif defined(SOKOL_D3D11)
22985+ _SG_VALIDATE(_sg_validate_slot_bits(hlsl_smp_bits, smp_desc->stage, smp_desc->hlsl_register_s_n), VALIDATE_SHADERDESC_SAMPLER_HLSL_REGISTER_S_COLLISION);
22986+ hlsl_smp_bits = _sg_validate_set_slot_bit(hlsl_smp_bits, smp_desc->stage, smp_desc->hlsl_register_s_n);
22987+ #elif defined(SOKOL_WGPU)
22988+ _SG_VALIDATE(_sg_validate_slot_bits(wgsl_group1_bits, SG_SHADERSTAGE_NONE, smp_desc->wgsl_group1_binding_n), VALIDATE_SHADERDESC_SAMPLER_WGSL_GROUP1_BINDING_COLLISION);
22989+ wgsl_group1_bits = _sg_validate_set_slot_bit(wgsl_group1_bits, SG_SHADERSTAGE_NONE, smp_desc->wgsl_group1_binding_n);
22990+ #elif defined(SOKOL_VULKAN)
22991+ _SG_VALIDATE(_sg_validate_slot_bits(spirv_set1_bits, SG_SHADERSTAGE_NONE, smp_desc->spirv_set1_binding_n), VALIDATE_SHADERDESC_SAMPLER_SPIRV_SET1_BINDING_COLLISION);
22992+ spirv_set1_bits = _sg_validate_set_slot_bit(spirv_set1_bits, SG_SHADERSTAGE_NONE, smp_desc->spirv_set1_binding_n);
22993+ #endif
22994+ }
22995+
22996+ uint32_t ref_texview_slot_mask = 0;
22997+ uint32_t ref_smp_slot_mask = 0;
22998+ for (size_t tex_smp_idx = 0; tex_smp_idx < SG_MAX_TEXTURE_SAMPLER_PAIRS; tex_smp_idx++) {
22999+ const sg_shader_texture_sampler_pair* tex_smp_desc = &desc->texture_sampler_pairs[tex_smp_idx];
23000+ if (tex_smp_desc->stage == SG_SHADERSTAGE_NONE) {
23001+ continue;
23002+ }
23003+ #if defined(_SOKOL_ANY_GL)
23004+ _SG_VALIDATE(tex_smp_desc->glsl_name != 0, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_GLSL_NAME);
23005+ #endif
23006+ const bool view_slot_in_range = tex_smp_desc->view_slot < SG_MAX_VIEW_BINDSLOTS;
23007+ const bool smp_slot_in_range = tex_smp_desc->sampler_slot < SG_MAX_SAMPLER_BINDSLOTS;
23008+ _SG_VALIDATE(view_slot_in_range, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_VIEW_SLOT_OUT_OF_RANGE);
23009+ _SG_VALIDATE(smp_slot_in_range, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_SAMPLER_SLOT_OUT_OF_RANGE);
23010+ if (view_slot_in_range && smp_slot_in_range) {
23011+ ref_texview_slot_mask |= 1 << tex_smp_desc->view_slot;
23012+ ref_smp_slot_mask |= 1 << tex_smp_desc->sampler_slot;
23013+ const sg_shader_view* view_desc = &desc->views[tex_smp_desc->view_slot];
23014+ const sg_shader_sampler* smp_desc = &desc->samplers[tex_smp_desc->sampler_slot];
23015+ _SG_VALIDATE(view_desc->texture.stage != SG_SHADERSTAGE_NONE, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_EXPECT_TEXTURE_VIEW);
23016+ _SG_VALIDATE(view_desc->texture.stage == tex_smp_desc->stage, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_TEXTURE_STAGE_MISMATCH);
23017+ _SG_VALIDATE(smp_desc->stage == tex_smp_desc->stage, VALIDATE_SHADERDESC_TEXTURE_SAMPLER_PAIR_SAMPLER_STAGE_MISMATCH);
23018+ const bool needs_nonfiltering = (view_desc->texture.sample_type == SG_IMAGESAMPLETYPE_UINT)
23019+ || (view_desc->texture.sample_type == SG_IMAGESAMPLETYPE_SINT)
23020+ || (view_desc->texture.sample_type == SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT);
23021+ const bool needs_comparison = view_desc->texture.sample_type == SG_IMAGESAMPLETYPE_DEPTH;
23022+ if (needs_nonfiltering) {
23023+ _SG_VALIDATE(needs_nonfiltering && (smp_desc->sampler_type == SG_SAMPLERTYPE_NONFILTERING), VALIDATE_SHADERDESC_NONFILTERING_SAMPLER_REQUIRED);
23024+ }
23025+ if (needs_comparison) {
23026+ _SG_VALIDATE(needs_comparison && (smp_desc->sampler_type == SG_SAMPLERTYPE_COMPARISON), VALIDATE_SHADERDESC_COMPARISON_SAMPLER_REQUIRED);
23027+ }
23028+ }
23029+ }
23030+ // each image and sampler must be referenced by an image sampler
23031+ _SG_VALIDATE(texview_slot_mask == ref_texview_slot_mask, VALIDATE_SHADERDESC_TEXVIEW_NOT_REFERENCED_BY_TEXTURE_SAMPLER_PAIRS);
23032+ _SG_VALIDATE(smp_slot_mask == ref_smp_slot_mask, VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_TEXTURE_SAMPLER_PAIRS);
23033+
23034+ return _sg_validate_end();
23035+ #endif
23036+}
23037+
23038+_SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) {
23039+ #if !defined(SOKOL_DEBUG)
23040+ _SOKOL_UNUSED(desc);
23041+ return true;
23042+ #else
23043+ if (_sg.desc.disable_validation) {
23044+ return true;
23045+ }
23046+ SOKOL_ASSERT(desc);
23047+ _sg_validate_begin();
23048+ _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_PIPELINEDESC_CANARY);
23049+ _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_PIPELINEDESC_CANARY);
23050+ _SG_VALIDATE(desc->shader.id != SG_INVALID_ID, VALIDATE_PIPELINEDESC_SHADER);
23051+ const _sg_shader_t* shd = _sg_lookup_shader(desc->shader.id);
23052+ _SG_VALIDATE(0 != shd, VALIDATE_PIPELINEDESC_SHADER);
23053+ if (shd) {
23054+ _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PIPELINEDESC_SHADER);
23055+ if (desc->compute) {
23056+ _SG_VALIDATE(shd->cmn.is_compute, VALIDATE_PIPELINEDESC_COMPUTE_SHADER_EXPECTED);
23057+ } else {
23058+ _SG_VALIDATE(!shd->cmn.is_compute, VALIDATE_PIPELINEDESC_NO_COMPUTE_SHADER_EXPECTED);
23059+ bool attrs_cont = true;
23060+ for (size_t attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) {
23061+ const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index];
23062+ if (a_state->format == SG_VERTEXFORMAT_INVALID) {
23063+ attrs_cont = false;
23064+ continue;
23065+ }
23066+ _SG_VALIDATE(attrs_cont, VALIDATE_PIPELINEDESC_NO_CONT_ATTRS);
23067+ SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS);
23068+ // vertex format must match expected shader attribute base type (if provided)
23069+ if (shd->cmn.attrs[attr_index].base_type != SG_SHADERATTRBASETYPE_UNDEFINED) {
23070+ if (_sg_vertexformat_basetype(a_state->format) != shd->cmn.attrs[attr_index].base_type) {
23071+ _SG_VALIDATE(false, VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH);
23072+ _SG_LOGMSG(VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH, "attr format:");
23073+ _SG_LOGMSG(VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH, _sg_vertexformat_to_string(a_state->format));
23074+ _SG_LOGMSG(VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH, "shader attr base type:");
23075+ _SG_LOGMSG(VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH, _sg_shaderattrbasetype_to_string(shd->cmn.attrs[attr_index].base_type));
23076+ }
23077+ }
23078+ if (a_state->format == SG_VERTEXFORMAT_INT10_N2) {
23079+ _SG_VALIDATE(_sg.features.vertexformat_int10_n2, VALIDATE_PIPELINEDESC_ATTR_VERTEXFORMAT_INT10_N2_NOT_SUPPORTED);
23080+ }
23081+ #if defined(SOKOL_D3D11)
23082+ // on D3D11, semantic names (and semantic indices) must be provided
23083+ _SG_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), VALIDATE_PIPELINEDESC_ATTR_SEMANTICS);
23084+ #endif
23085+ }
23086+ // must only use readonly storage buffer bindings in render pipelines
23087+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
23088+ if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEBUFFER) {
23089+ _SG_VALIDATE(shd->cmn.views[i].sbuf_readonly, VALIDATE_PIPELINEDESC_SHADER_READONLY_STORAGEBUFFERS);
23090+ }
23091+ }
23092+ for (int buf_index = 0; buf_index < SG_MAX_VERTEXBUFFER_BINDSLOTS; buf_index++) {
23093+ const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[buf_index];
23094+ if (l_state->stride == 0) {
23095+ continue;
23096+ }
23097+ _SG_VALIDATE(_sg_multiple_u64((uint64_t)l_state->stride, 4), VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4);
23098+ }
23099+ }
23100+ }
23101+ if (desc->depth.pixel_format == SG_PIXELFORMAT_NONE) {
23102+ _SG_VALIDATE(desc->depth.write_enabled == false, VALIDATE_PIPELINEDESC_DEPTH_FORMAT_NONE_BUT_DEPTH_WRITE_ENABLED);
23103+ _SG_VALIDATE((desc->depth.compare == SG_COMPAREFUNC_ALWAYS) || (desc->depth.compare == SG_COMPAREFUNC_NEVER), VALIDATE_PIPELINEDESC_DEPTH_FORMAT_NONE_COMPARE_FUNC_MISMATCH);
23104+ }
23105+ for (size_t color_index = 0; color_index < (size_t)desc->color_count; color_index++) {
23106+ SOKOL_ASSERT(color_index < SG_MAX_COLOR_ATTACHMENTS);
23107+ const sg_blend_state* bs = &desc->colors[color_index].blend;
23108+ if ((bs->op_rgb == SG_BLENDOP_MIN) || (bs->op_rgb == SG_BLENDOP_MAX)) {
23109+ _SG_VALIDATE((bs->src_factor_rgb == SG_BLENDFACTOR_ONE) && (bs->dst_factor_rgb == SG_BLENDFACTOR_ONE), VALIDATE_PIPELINEDESC_BLENDOP_MINMAX_REQUIRES_BLENDFACTOR_ONE);
23110+ }
23111+ if ((bs->op_alpha == SG_BLENDOP_MIN) || (bs->op_alpha == SG_BLENDOP_MAX)) {
23112+ _SG_VALIDATE((bs->src_factor_alpha == SG_BLENDFACTOR_ONE) && (bs->dst_factor_alpha == SG_BLENDFACTOR_ONE), VALIDATE_PIPELINEDESC_BLENDOP_MINMAX_REQUIRES_BLENDFACTOR_ONE);
23113+ }
23114+ const bool needs_dualsource_blending =
23115+ _sg_is_dualsource_blendfactor(bs->src_factor_rgb) ||
23116+ _sg_is_dualsource_blendfactor(bs->dst_factor_rgb) ||
23117+ _sg_is_dualsource_blendfactor(bs->src_factor_alpha) ||
23118+ _sg_is_dualsource_blendfactor(bs->dst_factor_alpha);
23119+ if (needs_dualsource_blending) {
23120+ _SG_VALIDATE(_sg.features.dual_source_blending, VALIDATE_PIPELINEDESC_DUAL_SOURCE_BLENDING_NOT_SUPPORTED);
23121+ }
23122+ }
23123+ return _sg_validate_end();
23124+ #endif
23125+}
23126+
23127+_SOKOL_PRIVATE bool _sg_validate_view_desc(const sg_view_desc* desc) {
23128+ #if !defined(SOKOL_DEBUG)
23129+ _SOKOL_UNUSED(desc);
23130+ return true;
23131+ #else
23132+ if (_sg.desc.disable_validation) {
23133+ return true;
23134+ }
23135+ SOKOL_ASSERT(desc);
23136+ _sg_validate_begin();
23137+ _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_VIEWDESC_CANARY);
23138+ _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_VIEWDESC_CANARY);
23139+
23140+ // only one view type can be define
23141+ sg_view_type view_type = SG_VIEWTYPE_INVALID;
23142+ const sg_image_view_desc* img_desc = 0;
23143+ const sg_texture_view_desc* tex_desc = 0;
23144+ const sg_buffer_view_desc* buf_desc = 0;
23145+ if (desc->texture.image.id != SG_INVALID_ID) {
23146+ view_type = SG_VIEWTYPE_TEXTURE;
23147+ tex_desc = &desc->texture;
23148+ }
23149+ if (desc->storage_buffer.buffer.id != SG_INVALID_ID) {
23150+ _SG_VALIDATE(SG_VIEWTYPE_INVALID == view_type, VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE);
23151+ view_type = SG_VIEWTYPE_STORAGEBUFFER;
23152+ buf_desc = &desc->storage_buffer;
23153+ }
23154+ if (desc->storage_image.image.id != SG_INVALID_ID) {
23155+ _SG_VALIDATE(SG_VIEWTYPE_INVALID == view_type, VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE);
23156+ view_type = SG_VIEWTYPE_STORAGEIMAGE;
23157+ img_desc = &desc->storage_image;
23158+ }
23159+ if (desc->color_attachment.image.id != SG_INVALID_ID) {
23160+ _SG_VALIDATE(SG_VIEWTYPE_INVALID == view_type, VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE);
23161+ view_type = SG_VIEWTYPE_COLORATTACHMENT;
23162+ img_desc = &desc->color_attachment;
23163+ }
23164+ if (desc->resolve_attachment.image.id != SG_INVALID_ID) {
23165+ _SG_VALIDATE(SG_VIEWTYPE_INVALID == view_type, VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE);
23166+ view_type = SG_VIEWTYPE_RESOLVEATTACHMENT;
23167+ img_desc = &desc->resolve_attachment;
23168+ }
23169+ if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) {
23170+ _SG_VALIDATE(SG_VIEWTYPE_INVALID == view_type, VALIDATE_VIEWDESC_UNIQUE_VIEWTYPE);
23171+ view_type = SG_VIEWTYPE_DEPTHSTENCILATTACHMENT;
23172+ img_desc = &desc->depth_stencil_attachment;
23173+ }
23174+ _SG_VALIDATE(SG_VIEWTYPE_INVALID != view_type, VALIDATE_VIEWDESC_ANY_VIEWTYPE);
23175+
23176+ const _sg_buffer_t* buf = 0;
23177+ const _sg_image_t* img = 0;
23178+ bool res_valid = false;
23179+ if (buf_desc) {
23180+ SOKOL_ASSERT((img_desc == 0) && (tex_desc == 0));
23181+ buf = _sg_lookup_buffer(buf_desc->buffer.id);
23182+ _SG_VALIDATE(buf, VALIDATE_VIEWDESC_RESOURCE_ALIVE);
23183+ if (buf) {
23184+ _SG_VALIDATE(buf->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_VIEWDESC_RESOURCE_FAILED);
23185+ res_valid = buf->slot.state == SG_RESOURCESTATE_VALID;
23186+ }
23187+ } else if (img_desc) {
23188+ SOKOL_ASSERT((tex_desc == 0) && (buf_desc == 0));
23189+ img = _sg_lookup_image(img_desc->image.id);
23190+ _SG_VALIDATE(img, VALIDATE_VIEWDESC_RESOURCE_ALIVE);
23191+ if (img) {
23192+ _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_VIEWDESC_RESOURCE_FAILED);
23193+ res_valid = img->slot.state == SG_RESOURCESTATE_VALID;
23194+ }
23195+ } else {
23196+ SOKOL_ASSERT(tex_desc && (img_desc == 0) && (buf_desc == 0));
23197+ img = _sg_lookup_image(tex_desc->image.id);
23198+ _SG_VALIDATE(img, VALIDATE_VIEWDESC_RESOURCE_ALIVE);
23199+ if (img) {
23200+ _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_VIEWDESC_RESOURCE_FAILED);
23201+ res_valid = img->slot.state == SG_RESOURCESTATE_VALID;
23202+ }
23203+ }
23204+ if (res_valid) {
23205+ // check usage flags
23206+ switch (view_type) {
23207+ case SG_VIEWTYPE_STORAGEBUFFER:
23208+ SOKOL_ASSERT(buf);
23209+ _SG_VALIDATE(buf->cmn.usage.storage_buffer, VALIDATE_VIEWDESC_STORAGEBUFFER_USAGE);
23210+ break;
23211+ case SG_VIEWTYPE_STORAGEIMAGE:
23212+ SOKOL_ASSERT(img);
23213+ _SG_VALIDATE(img->cmn.usage.storage_image, VALIDATE_VIEWDESC_STORAGEIMAGE_USAGE);
23214+ _SG_VALIDATE(_sg_is_valid_storage_image_format(img->cmn.pixel_format), VALIDATE_VIEWDESC_STORAGEIMAGE_PIXELFORMAT);
23215+ break;
23216+ case SG_VIEWTYPE_TEXTURE:
23217+ if (!_sg.features.msaa_texture_bindings) {
23218+ _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_VIEWDESC_TEXTURE_EXPECT_NO_MSAA);
23219+ }
23220+ break;
23221+ case SG_VIEWTYPE_COLORATTACHMENT:
23222+ SOKOL_ASSERT(img);
23223+ _SG_VALIDATE(img->cmn.usage.color_attachment, VALIDATE_VIEWDESC_COLORATTACHMENT_USAGE);
23224+ _SG_VALIDATE(_sg_is_valid_attachment_color_format(img->cmn.pixel_format), VALIDATE_VIEWDESC_COLORATTACHMENT_PIXELFORMAT);
23225+ break;
23226+ case SG_VIEWTYPE_RESOLVEATTACHMENT:
23227+ SOKOL_ASSERT(img);
23228+ _SG_VALIDATE(img->cmn.usage.resolve_attachment, VALIDATE_VIEWDESC_RESOLVEATTACHMENT_USAGE);
23229+ _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_VIEWDESC_RESOLVEATTACHMENT_SAMPLECOUNT);
23230+ break;
23231+ case SG_VIEWTYPE_DEPTHSTENCILATTACHMENT:
23232+ SOKOL_ASSERT(img);
23233+ _SG_VALIDATE(img->cmn.usage.depth_stencil_attachment, VALIDATE_VIEWDESC_DEPTHSTENCILATTACHMENT_USAGE);
23234+ _SG_VALIDATE(_sg_is_valid_attachment_depth_format(img->cmn.pixel_format), VALIDATE_VIEWDESC_DEPTHSTENCILATTACHMENT_PIXELFORMAT);
23235+ break;
23236+ default:
23237+ SOKOL_UNREACHABLE;
23238+ break;
23239+ }
23240+ if (buf_desc) {
23241+ SOKOL_ASSERT(buf);
23242+ _SG_VALIDATE(buf_desc->offset < buf->cmn.size, VALIDATE_VIEWDESC_STORAGEBUFFER_OFFSET_VS_BUFFER_SIZE);
23243+ _SG_VALIDATE(_sg_multiple_u64((uint64_t)buf_desc->offset, 256), VALIDATE_VIEWDESC_STORAGEBUFFER_OFFSET_MULTIPLE_256);
23244+ } else if (img_desc) {
23245+ SOKOL_ASSERT(img);
23246+ _SG_VALIDATE((img_desc->mip_level >= 0) && (img_desc->mip_level < img->cmn.num_mipmaps), VALIDATE_VIEWDESC_IMAGE_MIPLEVEL);
23247+ if (img->cmn.type == SG_IMAGETYPE_2D) {
23248+ _SG_VALIDATE(img_desc->slice == 0, VALIDATE_VIEWDESC_IMAGE_2D_SLICE);
23249+ } else if (img->cmn.type == SG_IMAGETYPE_CUBE) {
23250+ _SG_VALIDATE((img_desc->slice >= 0) && (img_desc->slice < 6), VALIDATE_VIEWDESC_IMAGE_CUBEMAP_SLICE);
23251+ } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) {
23252+ _SG_VALIDATE((img_desc->slice >= 0) && (img_desc->slice < img->cmn.num_slices), VALIDATE_VIEWDESC_IMAGE_ARRAY_SLICE);
23253+ } else if (img->cmn.type == SG_IMAGETYPE_3D) {
23254+ _SG_VALIDATE(img_desc->slice == 0, VALIDATE_VIEWDESC_IMAGE_3D_SLICE);
23255+ }
23256+ } else if (tex_desc) {
23257+ SOKOL_ASSERT(img);
23258+ // NOTE: it doesn't matter here if the mip/slice count is default-zero!
23259+ int max_mip_level = tex_desc->mip_levels.base + tex_desc->mip_levels.count;
23260+ int max_slice = tex_desc->slices.base + tex_desc->slices.count;
23261+ _SG_VALIDATE((tex_desc->mip_levels.base >= 0) && (max_mip_level <= img->cmn.num_mipmaps), VALIDATE_VIEWDESC_TEXTURE_MIPLEVELS);
23262+ if (img->cmn.type == SG_IMAGETYPE_2D) {
23263+ _SG_VALIDATE((tex_desc->slices.base == 0) && (max_slice <= 1), VALIDATE_VIEWDESC_TEXTURE_2D_SLICES);
23264+ } else if (img->cmn.type == SG_IMAGETYPE_CUBE) {
23265+ _SG_VALIDATE((tex_desc->slices.base == 0) && (max_slice <= 1), VALIDATE_VIEWDESC_TEXTURE_CUBEMAP_SLICES);
23266+ } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) {
23267+ _SG_VALIDATE((tex_desc->slices.base >= 0) && (max_slice <= img->cmn.num_slices), VALIDATE_VIEWDESC_TEXTURE_ARRAY_SLICES);
23268+ } else if (img->cmn.type == SG_IMAGETYPE_3D) {
23269+ _SG_VALIDATE((tex_desc->slices.base == 0) && (max_slice <= 1), VALIDATE_VIEWDESC_TEXTURE_3D_SLICES);
23270+ }
23271+ }
23272+ }
23273+ return _sg_validate_end();
23274+ #endif
23275+}
23276+
23277+_SOKOL_PRIVATE bool _sg_validate_begin_pass(const sg_pass* pass) {
23278+ #if !defined(SOKOL_DEBUG)
23279+ _SOKOL_UNUSED(pass);
23280+ return true;
23281+ #else
23282+ if (_sg.desc.disable_validation) {
23283+ return true;
23284+ }
23285+ const bool is_invalid_swapchain_pass = pass->swapchain.invalid;
23286+ const bool is_compute_pass = pass->compute;
23287+ const bool is_swapchain_pass = !is_compute_pass && _sg_attachments_empty(&pass->attachments);
23288+ const bool is_offscreen_pass = !(is_compute_pass || is_swapchain_pass);
23289+ _sg_validate_begin();
23290+ _SG_VALIDATE(pass->_start_canary == 0, VALIDATE_BEGINPASS_CANARY);
23291+ _SG_VALIDATE(pass->_end_canary == 0, VALIDATE_BEGINPASS_CANARY);
23292+ if (is_compute_pass) {
23293+ _SG_VALIDATE(_sg_attachments_empty(&pass->attachments), VALIDATE_BEGINPASS_COMPUTEPASS_EXPECT_NO_ATTACHMENTS);
23294+ } else if (is_invalid_swapchain_pass) {
23295+ // empty block not a bug, skips to 'swapchain zeroed' validation at the end
23296+ } else if (is_swapchain_pass) {
23297+ _SG_VALIDATE(pass->swapchain.width > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH);
23298+ _SG_VALIDATE(pass->swapchain.height > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT);
23299+ _SG_VALIDATE(pass->swapchain.sample_count > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT);
23300+ _SG_VALIDATE(pass->swapchain.color_format > SG_PIXELFORMAT_NONE, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT);
23301+ // NOTE: depth buffer is optional, so depth_format is allowed to be invalid
23302+ // NOTE: the GL framebuffer handle may actually be 0
23303+ #if defined(SOKOL_METAL)
23304+ _SG_VALIDATE(pass->swapchain.metal.current_drawable != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE);
23305+ if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) {
23306+ _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET);
23307+ } else {
23308+ _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE);
23309+ }
23310+ if (pass->swapchain.sample_count > 1) {
23311+ _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE);
23312+ } else {
23313+ _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET);
23314+ }
23315+ #elif defined(SOKOL_D3D11)
23316+ _SG_VALIDATE(pass->swapchain.d3d11.render_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW);
23317+ if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) {
23318+ _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET);
23319+ } else {
23320+ _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW);
23321+ }
23322+ if (pass->swapchain.sample_count > 1) {
23323+ _SG_VALIDATE(pass->swapchain.d3d11.resolve_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW);
23324+ } else {
23325+ _SG_VALIDATE(pass->swapchain.d3d11.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET);
23326+ }
23327+ #elif defined(SOKOL_WGPU)
23328+ _SG_VALIDATE(pass->swapchain.wgpu.render_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW);
23329+ if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) {
23330+ _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET);
23331+ } else {
23332+ _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW);
23333+ }
23334+ if (pass->swapchain.sample_count > 1) {
23335+ _SG_VALIDATE(pass->swapchain.wgpu.resolve_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW);
23336+ } else {
23337+ _SG_VALIDATE(pass->swapchain.wgpu.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET);
23338+ }
23339+ #elif defined(SOKOL_VULKAN)
23340+ _SG_VALIDATE(pass->swapchain.vulkan.render_image != 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERIMAGE);
23341+ _SG_VALIDATE(pass->swapchain.vulkan.render_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERVIEW);
23342+ if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) {
23343+ _SG_VALIDATE(pass->swapchain.vulkan.depth_stencil_image == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_DEPTHSTENCILIMAGE_NOTSET);
23344+ _SG_VALIDATE(pass->swapchain.vulkan.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_DEPTHSTENCILVIEW_NOTSET);
23345+ } else {
23346+ _SG_VALIDATE(pass->swapchain.vulkan.depth_stencil_image != 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_DEPTHSTENCILIMAGE);
23347+ _SG_VALIDATE(pass->swapchain.vulkan.depth_stencil_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_DEPTHSTENCILVIEW);
23348+ }
23349+ if (pass->swapchain.sample_count > 1) {
23350+ _SG_VALIDATE(pass->swapchain.vulkan.resolve_image != 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RESOLVEIMAGE);
23351+ _SG_VALIDATE(pass->swapchain.vulkan.resolve_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RESOLVEVIEW);
23352+ } else {
23353+ _SG_VALIDATE(pass->swapchain.vulkan.resolve_image == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RESOLVEIMAGE_NOTSET);
23354+ _SG_VALIDATE(pass->swapchain.vulkan.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RESOLVEVIEW_NOTSET);
23355+ }
23356+ _SG_VALIDATE(pass->swapchain.vulkan.render_finished_semaphore != 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERFINISHEDSEMAPHORE);
23357+ _SG_VALIDATE(pass->swapchain.vulkan.present_complete_semaphore != 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_PRESENTCOMPLETESEMAPHORE);
23358+ #endif
23359+ } else {
23360+ // this is an 'offscreen pass'
23361+ bool has_color_atts = false;
23362+ bool has_depth_stencil_atts = false;
23363+ bool atts_cont = true;
23364+ int color_width = -1, color_height = -1, color_sample_count = -1;
23365+ for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) {
23366+ if (pass->attachments.colors[att_index].id == SG_INVALID_ID) {
23367+ atts_cont = false;
23368+ continue;
23369+ }
23370+ has_color_atts = true;
23371+ _SG_VALIDATE(atts_cont, VALIDATE_BEGINPASS_COLORATTACHMENTVIEWS_CONTINUOUS);
23372+ const _sg_view_t* view = _sg_lookup_view(pass->attachments.colors[att_index].id);
23373+ // the view object must be alive
23374+ _SG_VALIDATE(view != 0, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_ALIVE);
23375+ if (view) {
23376+ // the view object must be in valid state
23377+ _SG_VALIDATE(view->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_VALID);
23378+ if (view->slot.state == SG_RESOURCESTATE_VALID) {
23379+ // the view object must be a color attachment view
23380+ _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_COLORATTACHMENT, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_TYPE);
23381+ // the view's image object must be alive and valid
23382+ const _sg_image_t* img = _sg_image_ref_ptr_or_null(&view->cmn.img.ref);
23383+ _SG_VALIDATE(img, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_IMAGE_ALIVE);
23384+ if (img) {
23385+ _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_IMAGE_VALID);
23386+ if (img->slot.state == SG_RESOURCESTATE_VALID) {
23387+ if (color_width == -1) {
23388+ color_width = _sg_image_view_dim(view).width;
23389+ color_height = _sg_image_view_dim(view).height;
23390+ color_sample_count = img->cmn.sample_count;
23391+ } else {
23392+ _SG_VALIDATE(color_width == _sg_image_view_dim(view).width, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SIZES);
23393+ _SG_VALIDATE(color_height == _sg_image_view_dim(view).height, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SIZES);
23394+ _SG_VALIDATE(color_sample_count == img->cmn.sample_count, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SAMPLECOUNTS_EQUAL);
23395+ }
23396+ }
23397+ }
23398+ }
23399+ }
23400+ }
23401+ // check resolve views
23402+ for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) {
23403+ if (pass->attachments.resolves[att_index].id == SG_INVALID_ID) {
23404+ continue;
23405+ }
23406+ _SG_VALIDATE(pass->attachments.colors[att_index].id != SG_INVALID_ID, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_NO_COLORATTACHMENTVIEW);
23407+ const _sg_view_t* view = _sg_lookup_view(pass->attachments.resolves[att_index].id);
23408+ // the view object must be alive
23409+ _SG_VALIDATE(view != 0, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_ALIVE);
23410+ if (view) {
23411+ // the view object must be in valid state
23412+ _SG_VALIDATE(view->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_VALID);
23413+ if (view->slot.state == SG_RESOURCESTATE_VALID) {
23414+ // the view object must be a resolve attachment view
23415+ _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_RESOLVEATTACHMENT, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_TYPE);
23416+ // the view's image object must be alive and valid
23417+ const _sg_image_t* img = _sg_image_ref_ptr_or_null(&view->cmn.img.ref);
23418+ _SG_VALIDATE(img, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_IMAGE_ALIVE);
23419+ if (img) {
23420+ _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_IMAGE_VALID);
23421+ if (img->slot.state == SG_RESOURCESTATE_VALID) {
23422+ if (color_width != -1) {
23423+ _SG_VALIDATE(color_sample_count > 1, VALIDATE_BEGINPASS_COLORATTACHMENTVIEW_SAMPLECOUNT);
23424+ _SG_VALIDATE(color_width == _sg_image_view_dim(view).width, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_SIZES);
23425+ _SG_VALIDATE(color_height == _sg_image_view_dim(view).height, VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_SIZES);
23426+ }
23427+ }
23428+ }
23429+ }
23430+ }
23431+ }
23432+ // check depth-stencil view
23433+ if (pass->attachments.depth_stencil.id != SG_INVALID_ID) {
23434+ has_depth_stencil_atts = true;
23435+ const _sg_view_t* view = _sg_lookup_view(pass->attachments.depth_stencil.id);
23436+ // the view object must be valid
23437+ _SG_VALIDATE(view != 0, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_ALIVE);
23438+ if (view) {
23439+ // the view object must be in valid state
23440+ _SG_VALIDATE(view->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_VALID);
23441+ if (view->slot.state == SG_RESOURCESTATE_VALID) {
23442+ // the view object must be a depth stencil attachment view
23443+ _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_DEPTHSTENCILATTACHMENT, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_TYPE);
23444+ // the view's image object must be alive and valid
23445+ const _sg_image_t* img = _sg_image_ref_ptr_or_null(&view->cmn.img.ref);
23446+ _SG_VALIDATE(img, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_IMAGE_ALIVE);
23447+ if (img) {
23448+ _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_IMAGE_VALID);
23449+ if (img->slot.state == SG_RESOURCESTATE_VALID) {
23450+ if (color_width != -1) {
23451+ _SG_VALIDATE(color_width == _sg_image_view_dim(view).width, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_SIZES);
23452+ _SG_VALIDATE(color_height == _sg_image_view_dim(view).height, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_SIZES);
23453+ _SG_VALIDATE(color_sample_count == img->cmn.sample_count, VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_SAMPLECOUNT);
23454+ }
23455+ }
23456+ }
23457+ }
23458+ }
23459+ }
23460+ // must have at least color- or depth-stencil-attachments
23461+ _SG_VALIDATE(has_color_atts || has_depth_stencil_atts, VALIDATE_BEGINPASS_ATTACHMENTS_EXPECTED);
23462+ }
23463+ if (is_compute_pass || is_offscreen_pass || is_invalid_swapchain_pass) {
23464+ _SG_VALIDATE(pass->swapchain.width == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH_NOTSET);
23465+ _SG_VALIDATE(pass->swapchain.height == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT_NOTSET);
23466+ _SG_VALIDATE(pass->swapchain.sample_count == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT_NOTSET);
23467+ _SG_VALIDATE(pass->swapchain.color_format == _SG_PIXELFORMAT_DEFAULT, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT_NOTSET);
23468+ _SG_VALIDATE(pass->swapchain.depth_format == _SG_PIXELFORMAT_DEFAULT, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_DEPTHFORMAT_NOTSET);
23469+ #if defined(SOKOL_METAL)
23470+ _SG_VALIDATE(pass->swapchain.metal.current_drawable == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE_NOTSET);
23471+ _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET);
23472+ _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET);
23473+ #elif defined(SOKOL_D3D11)
23474+ _SG_VALIDATE(pass->swapchain.d3d11.render_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW_NOTSET);
23475+ _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET);
23476+ _SG_VALIDATE(pass->swapchain.d3d11.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET);
23477+ #elif defined(SOKOL_WGPU)
23478+ _SG_VALIDATE(pass->swapchain.wgpu.render_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW_NOTSET);
23479+ _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET);
23480+ _SG_VALIDATE(pass->swapchain.wgpu.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET);
23481+ #elif defined(_SOKOL_ANY_GL)
23482+ _SG_VALIDATE(pass->swapchain.gl.framebuffer == 0, VALIDATE_BEGINPASS_SWAPCHAIN_GL_EXPECT_FRAMEBUFFER_NOTSET);
23483+ #elif defined(SOKOL_VULKAN)
23484+ _SG_VALIDATE(pass->swapchain.vulkan.render_image == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERIMAGE_NOTSET);
23485+ _SG_VALIDATE(pass->swapchain.vulkan.render_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERVIEW_NOTSET);
23486+ _SG_VALIDATE(pass->swapchain.vulkan.depth_stencil_image == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_DEPTHSTENCILIMAGE_NOTSET);
23487+ _SG_VALIDATE(pass->swapchain.vulkan.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_DEPTHSTENCILVIEW_NOTSET);
23488+ _SG_VALIDATE(pass->swapchain.vulkan.resolve_image == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RESOLVEIMAGE_NOTSET);
23489+ _SG_VALIDATE(pass->swapchain.vulkan.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RESOLVEVIEW_NOTSET);
23490+ _SG_VALIDATE(pass->swapchain.vulkan.render_finished_semaphore == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_RENDERFINISHEDSEMAPHORE_NOTSET);
23491+ _SG_VALIDATE(pass->swapchain.vulkan.present_complete_semaphore == 0, VALIDATE_BEGINPASS_SWAPCHAIN_VULKAN_EXPECT_PRESENTCOMPLETESEMAPHORE_NOTSET);
23492+ #endif
23493+ }
23494+ return _sg_validate_end();
23495+ #endif
23496+}
23497+
23498+_SOKOL_PRIVATE bool _sg_validate_apply_viewport(int x, int y, int width, int height, bool origin_top_left) {
23499+ _SOKOL_UNUSED(x);
23500+ _SOKOL_UNUSED(y);
23501+ _SOKOL_UNUSED(width);
23502+ _SOKOL_UNUSED(height);
23503+ _SOKOL_UNUSED(origin_top_left);
23504+ #if !defined(SOKOL_DEBUG)
23505+ return true;
23506+ #else
23507+ if (_sg.desc.disable_validation) {
23508+ return true;
23509+ }
23510+ _sg_validate_begin();
23511+ _SG_VALIDATE(_sg.cur_pass.in_pass && !_sg.cur_pass.is_compute, VALIDATE_AVP_RENDERPASS_EXPECTED);
23512+ return _sg_validate_end();
23513+ #endif
23514+}
23515+
23516+_SOKOL_PRIVATE bool _sg_validate_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) {
23517+ _SOKOL_UNUSED(x);
23518+ _SOKOL_UNUSED(y);
23519+ _SOKOL_UNUSED(width);
23520+ _SOKOL_UNUSED(height);
23521+ _SOKOL_UNUSED(origin_top_left);
23522+ #if !defined(SOKOL_DEBUG)
23523+ return true;
23524+ #else
23525+ if (_sg.desc.disable_validation) {
23526+ return true;
23527+ }
23528+ _sg_validate_begin();
23529+ _SG_VALIDATE(_sg.cur_pass.in_pass && !_sg.cur_pass.is_compute, VALIDATE_ASR_RENDERPASS_EXPECTED);
23530+ return _sg_validate_end();
23531+ #endif
23532+}
23533+
23534+_SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) {
23535+ #if !defined(SOKOL_DEBUG)
23536+ _SOKOL_UNUSED(pip_id);
23537+ return true;
23538+ #else
23539+ if (_sg.desc.disable_validation) {
23540+ return true;
23541+ }
23542+ _sg_validate_begin();
23543+ // the pipeline object must be alive and valid
23544+ _SG_VALIDATE(pip_id.id != SG_INVALID_ID, VALIDATE_APIP_PIPELINE_VALID_ID);
23545+ const _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
23546+ _SG_VALIDATE(pip != 0, VALIDATE_APIP_PIPELINE_EXISTS);
23547+ if (!pip) {
23548+ return _sg_validate_end();
23549+ }
23550+ _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_PIPELINE_VALID);
23551+
23552+ // the pipeline's shader must be alive and valid
23553+ _SG_VALIDATE(_sg.cur_pass.in_pass, VALIDATE_APIP_PASS_EXPECTED);
23554+ const bool shd_alive = _sg_shader_ref_alive(&pip->cmn.shader);
23555+ const _sg_shader_t* shd = shd_alive ? _sg_shader_ref_ptr(&pip->cmn.shader) : 0;
23556+ _SG_VALIDATE(shd_alive, VALIDATE_APIP_PIPELINE_SHADER_ALIVE);
23557+ if (shd_alive) {
23558+ _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_PIPELINE_SHADER_VALID);
23559+ } else {
23560+ return _sg_validate_end();
23561+ }
23562+
23563+ if (pip->cmn.is_compute) {
23564+ _SG_VALIDATE(_sg.cur_pass.is_compute, VALIDATE_APIP_COMPUTEPASS_EXPECTED);
23565+ } else {
23566+ _SG_VALIDATE(!_sg.cur_pass.is_compute, VALIDATE_APIP_RENDERPASS_EXPECTED);
23567+ if (_sg_attachments_empty(&_sg.cur_pass.atts)) {
23568+ // a swapchain pass
23569+ _SG_VALIDATE(pip->cmn.color_count == 1, VALIDATE_APIP_SWAPCHAIN_COLOR_COUNT);
23570+ _SG_VALIDATE(pip->cmn.colors[0].pixel_format == _sg.cur_pass.swapchain.color_fmt, VALIDATE_APIP_SWAPCHAIN_COLOR_FORMAT);
23571+ _SG_VALIDATE(pip->cmn.depth.pixel_format == _sg.cur_pass.swapchain.depth_fmt, VALIDATE_APIP_SWAPCHAIN_DEPTH_FORMAT);
23572+ _SG_VALIDATE(pip->cmn.sample_count == _sg.cur_pass.swapchain.sample_count, VALIDATE_APIP_SWAPCHAIN_SAMPLE_COUNT);
23573+ } else {
23574+ // an offscreen render pass check that pipeline attributes match current pass attachment attributes
23575+ const _sg_attachments_ptrs_t atts_ptrs = _sg_attachments_ptrs(&_sg.cur_pass.atts);
23576+ const bool alive = _sg_attachments_alive(&atts_ptrs);
23577+ _SG_VALIDATE(alive, VALIDATE_APIP_ATTACHMENTS_ALIVE);
23578+ if (alive) {
23579+ _SG_VALIDATE(pip->cmn.color_count == atts_ptrs.num_color_views, VALIDATE_APIP_COLORATTACHMENTS_COUNT);
23580+ for (int i = 0; i < pip->cmn.color_count; i++) {
23581+ const _sg_view_t* clr_view = atts_ptrs.color_views[i];
23582+ SOKOL_ASSERT(clr_view);
23583+ _SG_VALIDATE(clr_view->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_COLORATTACHMENTS_VIEW_VALID);
23584+ const _sg_image_t* clr_img = _sg_image_ref_ptr(&clr_view->cmn.img.ref);
23585+ SOKOL_ASSERT(clr_img);
23586+ _SG_VALIDATE(clr_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_COLORATTACHMENTS_IMAGE_VALID);
23587+ _SG_VALIDATE(pip->cmn.colors[i].pixel_format == clr_img->cmn.pixel_format, VALIDATE_APIP_COLORATTACHMENTS_FORMAT);
23588+ _SG_VALIDATE(pip->cmn.sample_count == clr_img->cmn.sample_count, VALIDATE_APIP_ATTACHMENT_SAMPLE_COUNT);
23589+ }
23590+ const _sg_view_t* ds_view = atts_ptrs.ds_view;
23591+ if (ds_view) {
23592+ _SG_VALIDATE(ds_view->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_DEPTHSTENCILATTACHMENT_VIEW_VALID);
23593+ const _sg_image_t* ds_img = _sg_image_ref_ptr(&ds_view->cmn.img.ref);
23594+ SOKOL_ASSERT(ds_img);
23595+ _SG_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_DEPTHSTENCILATTACHMENT_IMAGE_VALID);
23596+ _SG_VALIDATE(pip->cmn.depth.pixel_format == ds_img->cmn.pixel_format, VALIDATE_APIP_DEPTHSTENCILATTACHMENT_FORMAT);
23597+ _SG_VALIDATE(pip->cmn.sample_count == ds_img->cmn.sample_count, VALIDATE_APIP_ATTACHMENT_SAMPLE_COUNT);
23598+ } else {
23599+ _SG_VALIDATE(pip->cmn.depth.pixel_format == SG_PIXELFORMAT_NONE, VALIDATE_APIP_DEPTHSTENCILATTACHMENT_FORMAT);
23600+ }
23601+ }
23602+ }
23603+ }
23604+ return _sg_validate_end();
23605+ #endif
23606+}
23607+
23608+_SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) {
23609+ #if !defined(SOKOL_DEBUG)
23610+ _SOKOL_UNUSED(bindings);
23611+ return true;
23612+ #else
23613+ if (_sg.desc.disable_validation) {
23614+ return true;
23615+ }
23616+ _sg_validate_begin();
23617+
23618+ // must be called in a pass
23619+ _SG_VALIDATE(_sg.cur_pass.in_pass, VALIDATE_ABND_PASS_EXPECTED);
23620+
23621+ // bindings must not be empty
23622+ bool has_any_bindings = bindings->index_buffer.id != SG_INVALID_ID;
23623+ if (!has_any_bindings) for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) {
23624+ has_any_bindings |= bindings->vertex_buffers[i].id != SG_INVALID_ID;
23625+ }
23626+ if (!has_any_bindings) for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
23627+ has_any_bindings |= bindings->views[i].id != SG_INVALID_ID;
23628+ }
23629+ if (!has_any_bindings) for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
23630+ has_any_bindings |= bindings->samplers[i].id != SG_INVALID_ID;
23631+ }
23632+ _SG_VALIDATE(has_any_bindings, VALIDATE_ABND_EMPTY_BINDINGS);
23633+
23634+ // a pipeline object must have been applied
23635+ const bool pip_null = _sg_pipeline_ref_null(&_sg.cur_pip);
23636+ const bool pip_alive = _sg_pipeline_ref_alive(&_sg.cur_pip);
23637+ _SG_VALIDATE(!pip_null, VALIDATE_ABND_NO_PIPELINE);
23638+ _SG_VALIDATE(pip_alive, VALIDATE_ABND_PIPELINE_ALIVE);
23639+ if (!pip_alive) {
23640+ return _sg_validate_end();
23641+ }
23642+ const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(&_sg.cur_pip);
23643+ _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ABND_PIPELINE_VALID);
23644+
23645+ const bool shd_alive = _sg_shader_ref_alive(&pip->cmn.shader);
23646+ _SG_VALIDATE(shd_alive, VALIDATE_ABND_PIPELINE_SHADER_ALIVE);
23647+ if (!shd_alive) {
23648+ return _sg_validate_end();
23649+ }
23650+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
23651+ _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ABND_PIPELINE_SHADER_VALID);
23652+
23653+ if (_sg.cur_pass.is_compute) {
23654+ for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) {
23655+ _SG_VALIDATE(bindings->vertex_buffers[i].id == SG_INVALID_ID, VALIDATE_ABND_COMPUTE_EXPECTED_NO_VBUFS);
23656+ }
23657+ } else {
23658+ for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) {
23659+ if (pip->cmn.vertex_buffer_layout_active[i]) {
23660+ _SG_VALIDATE(bindings->vertex_buffers[i].id != SG_INVALID_ID, VALIDATE_ABND_EXPECTED_VBUF);
23661+ if (bindings->vertex_buffers[i].id != SG_INVALID_ID) {
23662+ const _sg_buffer_t* buf = _sg_lookup_buffer(bindings->vertex_buffers[i].id);
23663+ _SG_VALIDATE(buf != 0, VALIDATE_ABND_VBUF_ALIVE);
23664+ // NOTE: state != VALID is legal and skips rendering!
23665+ if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) {
23666+ _SG_VALIDATE(buf->cmn.usage.vertex_buffer, VALIDATE_ABND_VBUF_USAGE);
23667+ _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_VBUF_OVERFLOW);
23668+ }
23669+ }
23670+ }
23671+ }
23672+ }
23673+
23674+ if (_sg.cur_pass.is_compute) {
23675+ _SG_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, VALIDATE_ABND_COMPUTE_EXPECTED_NO_IBUF);
23676+ } else {
23677+ // index buffer expected or not, and index buffer still exists
23678+ if (pip->cmn.index_type == SG_INDEXTYPE_NONE) {
23679+ // pipeline defines non-indexed rendering, but index buffer provided
23680+ _SG_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, VALIDATE_ABND_EXPECTED_NO_IBUF);
23681+ } else {
23682+ // pipeline defines indexed rendering, but no index buffer provided
23683+ _SG_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, VALIDATE_ABND_EXPECTED_IBUF);
23684+ }
23685+ if (bindings->index_buffer.id != SG_INVALID_ID) {
23686+ // buffer in index-buffer-slot must have index buffer usage
23687+ const _sg_buffer_t* buf = _sg_lookup_buffer(bindings->index_buffer.id);
23688+ _SG_VALIDATE(buf != 0, VALIDATE_ABND_IBUF_ALIVE);
23689+ // NOTE: state != VALID is legal and skips rendering!
23690+ if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) {
23691+ _SG_VALIDATE(buf->cmn.usage.index_buffer, VALIDATE_ABND_IBUF_USAGE);
23692+ _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_IBUF_OVERFLOW);
23693+ }
23694+ }
23695+ }
23696+
23697+ // has expected view bindings
23698+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
23699+ if (shd->cmn.views[i].view_type != SG_VIEWTYPE_INVALID) {
23700+ _SG_VALIDATE(bindings->views[i].id != SG_INVALID_ID, VALIDATE_ABND_EXPECTED_VIEW_BINDING);
23701+ if (bindings->views[i].id != SG_INVALID_ID) {
23702+ const _sg_view_t* view = _sg_lookup_view(bindings->views[i].id);
23703+ _SG_VALIDATE(view != 0, VALIDATE_ABND_VIEW_ALIVE);
23704+ // the view object must be alive
23705+ if (view) {
23706+ // NOTE: an invalid view state is allowed and skips rendering
23707+ if (view->slot.state == SG_RESOURCESTATE_VALID) {
23708+ if (shd->cmn.views[i].view_type == SG_VIEWTYPE_TEXTURE) {
23709+ // the view object must be a texture view
23710+ _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_TEXTURE, VALIDATE_ABND_EXPECT_TEXVIEW);
23711+ // NOTE: an invalid image ref is allowed and skips rendering
23712+ if (_sg_image_ref_valid(&view->cmn.img.ref)) {
23713+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
23714+ _SG_VALIDATE(img->cmn.type == shd->cmn.views[i].image_type, VALIDATE_ABND_TEXVIEW_IMAGETYPE_MISMATCH);
23715+ if (shd->cmn.views[i].multisampled) {
23716+ _SG_VALIDATE(img->cmn.sample_count > 1, VALIDATE_ABND_TEXVIEW_EXPECTED_MULTISAMPLED_IMAGE);
23717+ } else {
23718+ _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_TEXVIEW_EXPECTED_NON_MULTISAMPLED_IMAGE);
23719+ }
23720+ const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format];
23721+ switch (shd->cmn.views[i].sample_type) {
23722+ case SG_IMAGESAMPLETYPE_FLOAT:
23723+ _SG_VALIDATE(info->filter, VALIDATE_ABND_TEXVIEW_EXPECTED_FILTERABLE_IMAGE);
23724+ break;
23725+ case SG_IMAGESAMPLETYPE_DEPTH:
23726+ _SG_VALIDATE(info->depth, VALIDATE_ABND_TEXVIEW_EXPECTED_DEPTH_IMAGE);
23727+ break;
23728+ default:
23729+ break;
23730+ }
23731+ }
23732+ } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEBUFFER) {
23733+ // the view object must be a storage buffer view
23734+ _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_STORAGEBUFFER, VALIDATE_ABND_EXPECT_SBVIEW);
23735+ // NOTE: an invalid buffer ref is allowed and skips rendering
23736+ if (_sg_buffer_ref_valid(&view->cmn.buf.ref)) {
23737+ const _sg_buffer_t* buf = _sg_buffer_ref_ptr(&view->cmn.buf.ref);
23738+ if (!shd->cmn.views[i].sbuf_readonly) {
23739+ _SG_VALIDATE(buf->cmn.usage.immutable, VALIDATE_ABND_SBVIEW_READWRITE_IMMUTABLE);
23740+ }
23741+ }
23742+ } else if (shd->cmn.views[i].view_type == SG_VIEWTYPE_STORAGEIMAGE) {
23743+ // the view object must be a storage-image-view
23744+ _SG_VALIDATE(view->cmn.type == SG_VIEWTYPE_STORAGEIMAGE, VALIDATE_ABND_EXPECT_SIMGVIEW);
23745+ // storage images only allowed in compute passes
23746+ _SG_VALIDATE(_sg.cur_pass.is_compute, VALIDATE_ABND_SIMGVIEW_COMPUTE_PASS_EXPECTED);
23747+ // NOTE: an invalid image ref is allowed and skips rendering
23748+ if (_sg_image_ref_valid(&view->cmn.img.ref)) {
23749+ const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref);
23750+ _SG_VALIDATE(img->cmn.type == shd->cmn.views[i].image_type, VALIDATE_ABND_SIMGVIEW_IMAGETYPE_MISMATCH);
23751+ _SG_VALIDATE(img->cmn.pixel_format == shd->cmn.views[i].access_format, VALIDATE_ABND_SIMGVIEW_ACCESSFORMAT);
23752+ }
23753+ }
23754+ }
23755+ }
23756+ }
23757+ }
23758+ }
23759+
23760+ // has expected samplers
23761+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
23762+ if (shd->cmn.samplers[i].stage != SG_SHADERSTAGE_NONE) {
23763+ _SG_VALIDATE(bindings->samplers[i].id != SG_INVALID_ID, VALIDATE_ABND_EXPECTED_SAMPLER_BINDING);
23764+ if (bindings->samplers[i].id != SG_INVALID_ID) {
23765+ const _sg_sampler_t* smp = _sg_lookup_sampler(bindings->samplers[i].id);
23766+ _SG_VALIDATE(smp != 0, VALIDATE_ABND_SAMPLER_ALIVE);
23767+ if (smp) {
23768+ // NOTE: for invalid samplers don't skip rendering, but are actually an error
23769+ _SG_VALIDATE(smp->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ABND_SAMPLER_VALID);
23770+ if (shd->cmn.samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARISON) {
23771+ _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_UNEXPECTED_SAMPLER_COMPARE_NEVER);
23772+ } else {
23773+ _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_EXPECTED_SAMPLER_COMPARE_NEVER);
23774+ }
23775+ if (shd->cmn.samplers[i].sampler_type == SG_SAMPLERTYPE_NONFILTERING) {
23776+ const bool nonfiltering = (smp->cmn.min_filter != SG_FILTER_LINEAR)
23777+ && (smp->cmn.mag_filter != SG_FILTER_LINEAR)
23778+ && (smp->cmn.mipmap_filter != SG_FILTER_LINEAR);
23779+ _SG_VALIDATE(nonfiltering, VALIDATE_ABND_EXPECTED_NONFILTERING_SAMPLER);
23780+ }
23781+ }
23782+ }
23783+ }
23784+ }
23785+
23786+ // the same image cannot be used as texture binding and pass attachment or storage image binding
23787+ for (size_t tex_view_idx = 0; tex_view_idx < SG_MAX_VIEW_BINDSLOTS; tex_view_idx++) {
23788+ if (shd->cmn.views[tex_view_idx].view_type == SG_VIEWTYPE_TEXTURE) {
23789+ if (bindings->views[tex_view_idx].id == SG_INVALID_ID) {
23790+ continue;
23791+ }
23792+ const _sg_view_t* tex_view = _sg_lookup_view(bindings->views[tex_view_idx].id);
23793+ if (tex_view) {
23794+ const uint32_t img_id = tex_view->cmn.img.ref.sref.id;
23795+ if (!_sg_attachments_empty(&_sg.cur_pass.atts)) {
23796+ const _sg_view_t* ds_view = _sg_lookup_view(_sg.cur_pass.atts.depth_stencil.id);
23797+ if (ds_view) {
23798+ _SG_VALIDATE(img_id != ds_view->cmn.img.ref.sref.id, VALIDATE_ABND_TEXTURE_BINDING_VS_DEPTHSTENCIL_ATTACHMENT);
23799+ }
23800+ for (size_t att_idx = 0; att_idx < SG_MAX_COLOR_ATTACHMENTS; att_idx++) {
23801+ const _sg_view_t* color_view = _sg_lookup_view(_sg.cur_pass.atts.colors[att_idx].id);
23802+ if (color_view) {
23803+ _SG_VALIDATE(img_id != color_view->cmn.img.ref.sref.id, VALIDATE_ABND_TEXTURE_BINDING_VS_COLOR_ATTACHMENT);
23804+ }
23805+ const _sg_view_t* resolve_view = _sg_lookup_view(_sg.cur_pass.atts.resolves[att_idx].id);
23806+ if (resolve_view) {
23807+ _SG_VALIDATE(img_id != resolve_view->cmn.img.ref.sref.id, VALIDATE_ABND_TEXTURE_BINDING_VS_RESOLVE_ATTACHMENT);
23808+ }
23809+ }
23810+ }
23811+ for (size_t simg_view_idx = 0; simg_view_idx < SG_MAX_VIEW_BINDSLOTS; simg_view_idx++) {
23812+ if (shd->cmn.views[simg_view_idx].view_type == SG_VIEWTYPE_STORAGEIMAGE) {
23813+ if (bindings->views[simg_view_idx].id == SG_INVALID_ID) {
23814+ continue;
23815+ }
23816+ const _sg_view_t* simg_view = _sg_lookup_view(bindings->views[simg_view_idx].id);
23817+ if (simg_view) {
23818+ _SG_VALIDATE(img_id != simg_view->cmn.img.ref.sref.id, VALIDATE_ABND_TEXTURE_VS_STORAGEIMAGE_BINDING);
23819+ }
23820+ }
23821+ }
23822+ }
23823+ }
23824+ }
23825+ return _sg_validate_end();
23826+ #endif
23827+}
23828+
23829+_SOKOL_PRIVATE bool _sg_validate_apply_uniforms(int ub_slot, const sg_range* data) {
23830+ #if !defined(SOKOL_DEBUG)
23831+ _SOKOL_UNUSED(ub_slot);
23832+ _SOKOL_UNUSED(data);
23833+ return true;
23834+ #else
23835+ if (_sg.desc.disable_validation) {
23836+ return true;
23837+ }
23838+ SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS));
23839+ _sg_validate_begin();
23840+ _SG_VALIDATE(_sg.cur_pass.in_pass, VALIDATE_AU_PASS_EXPECTED);
23841+ const _sg_pipeline_ref_t* pip_ref = &_sg.cur_pip;
23842+ const bool pip_null = _sg_pipeline_ref_null(pip_ref);
23843+ const bool pip_alive = _sg_pipeline_ref_alive(pip_ref);
23844+ _SG_VALIDATE(!pip_null, VALIDATE_AU_NO_PIPELINE);
23845+ _SG_VALIDATE(pip_alive, VALIDATE_AU_PIPELINE_ALIVE);
23846+ if (pip_alive) {
23847+ const _sg_pipeline_t* pip = _sg_pipeline_ref_ptr(pip_ref);
23848+ _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_AU_PIPELINE_VALID);
23849+ const _sg_shader_ref_t* shd_ref = &pip->cmn.shader;
23850+ const bool shd_alive = _sg_shader_ref_alive(shd_ref);
23851+ _SG_VALIDATE(shd_alive, VALIDATE_AU_PIPELINE_SHADER_ALIVE);
23852+ if (shd_alive) {
23853+ const _sg_shader_t* shd = _sg_shader_ref_ptr(shd_ref);
23854+ _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_AU_PIPELINE_SHADER_VALID);
23855+ _SG_VALIDATE(shd->cmn.uniform_blocks[ub_slot].stage != SG_SHADERSTAGE_NONE, VALIDATE_AU_NO_UNIFORMBLOCK_AT_SLOT);
23856+ _SG_VALIDATE(data->size == shd->cmn.uniform_blocks[ub_slot].size, VALIDATE_AU_SIZE);
23857+ }
23858+ }
23859+ return _sg_validate_end();
23860+ #endif
23861+}
23862+
23863+_SOKOL_PRIVATE bool _sg_validate_draw(int base_element, int num_elements, int num_instances) {
23864+ #if !defined(SOKOL_DEBUG)
23865+ _SOKOL_UNUSED(base_element);
23866+ _SOKOL_UNUSED(num_elements);
23867+ _SOKOL_UNUSED(num_instances);
23868+ return true;
23869+ #else
23870+ if (_sg.desc.disable_validation) {
23871+ return true;
23872+ }
23873+ _sg_validate_begin();
23874+ _SG_VALIDATE(_sg.cur_pass.in_pass && !_sg.cur_pass.is_compute, VALIDATE_DRAW_RENDERPASS_EXPECTED);
23875+ _SG_VALIDATE(base_element >= 0, VALIDATE_DRAW_BASEELEMENT_GE_ZERO);
23876+ _SG_VALIDATE(num_elements >= 0, VALIDATE_DRAW_NUMELEMENTS_GE_ZERO);
23877+ _SG_VALIDATE(num_instances >= 0, VALIDATE_DRAW_NUMINSTANCES_GE_ZERO);
23878+ _SG_VALIDATE(_sg.required_bindings_and_uniforms == _sg.applied_bindings_and_uniforms, VALIDATE_DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING);
23879+ return _sg_validate_end();
23880+ #endif
23881+}
23882+
23883+_SOKOL_PRIVATE bool _sg_validate_draw_ex(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) {
23884+ #if !defined(SOKOL_DEBUG)
23885+ _SOKOL_UNUSED(base_element);
23886+ _SOKOL_UNUSED(num_elements);
23887+ _SOKOL_UNUSED(num_instances);
23888+ _SOKOL_UNUSED(base_vertex);
23889+ _SOKOL_UNUSED(base_instance);
23890+ return true;
23891+ #else
23892+ if (_sg.desc.disable_validation) {
23893+ return true;
23894+ }
23895+ _sg_validate_begin();
23896+ _SG_VALIDATE(_sg.cur_pass.in_pass && !_sg.cur_pass.is_compute, VALIDATE_DRAW_EX_RENDERPASS_EXPECTED);
23897+ // NOTE: base_vertex is allowed to be < 0
23898+ _SG_VALIDATE(base_element >= 0, VALIDATE_DRAW_EX_BASEELEMENT_GE_ZERO);
23899+ _SG_VALIDATE(num_elements >= 0, VALIDATE_DRAW_EX_NUMELEMENTS_GE_ZERO);
23900+ _SG_VALIDATE(num_instances >= 0, VALIDATE_DRAW_EX_NUMINSTANCES_GE_ZERO);
23901+ _SG_VALIDATE(base_instance >= 0, VALIDATE_DRAW_EX_BASEINSTANCE_GE_ZERO);
23902+ if (base_vertex != 0) {
23903+ _SG_VALIDATE(_sg.features.draw_base_vertex, VALIDATE_DRAW_EX_BASEVERTEX_NOT_SUPPORTED);
23904+ }
23905+ if (base_instance > 0) {
23906+ _SG_VALIDATE(_sg.features.draw_base_instance, VALIDATE_DRAW_EX_BASEINSTANCE_NOT_SUPPORTED);
23907+ }
23908+ if (!_sg.use_indexed_draw) {
23909+ _SG_VALIDATE(base_vertex == 0, VALIDATE_DRAW_EX_BASEVERTEX_VS_INDEXED);
23910+ }
23911+ const bool use_instanced_draw = (num_instances > 1) || _sg.use_instanced_draw;
23912+ if (!use_instanced_draw) {
23913+ _SG_VALIDATE(base_instance == 0, VALIDATE_DRAW_EX_BASEINSTANCE_VS_INSTANCED);
23914+ }
23915+ _SG_VALIDATE(_sg.required_bindings_and_uniforms == _sg.applied_bindings_and_uniforms, VALIDATE_DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING);
23916+ return _sg_validate_end();
23917+ #endif
23918+}
23919+
23920+_SOKOL_PRIVATE bool _sg_validate_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) {
23921+ #if !defined(SOKOL_DEBUG)
23922+ _SOKOL_UNUSED(num_groups_x);
23923+ _SOKOL_UNUSED(num_groups_y);
23924+ _SOKOL_UNUSED(num_groups_z);
23925+ return true;
23926+ #else
23927+ if (_sg.desc.disable_validation) {
23928+ return true;
23929+ }
23930+ _sg_validate_begin();
23931+ _SG_VALIDATE(_sg.cur_pass.in_pass && _sg.cur_pass.is_compute, VALIDATE_DISPATCH_COMPUTEPASS_EXPECTED);
23932+ _SG_VALIDATE((num_groups_x >= 0) && (num_groups_x < (1<<16)), VALIDATE_DISPATCH_NUMGROUPSX);
23933+ _SG_VALIDATE((num_groups_y >= 0) && (num_groups_y < (1<<16)), VALIDATE_DISPATCH_NUMGROUPSY);
23934+ _SG_VALIDATE((num_groups_z >= 0) && (num_groups_z < (1<<16)), VALIDATE_DISPATCH_NUMGROUPSZ);
23935+ _SG_VALIDATE(_sg.required_bindings_and_uniforms == _sg.applied_bindings_and_uniforms, VALIDATE_DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING);
23936+ return _sg_validate_end();
23937+ #endif
23938+}
23939+
23940+_SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const sg_range* data) {
23941+ #if !defined(SOKOL_DEBUG)
23942+ _SOKOL_UNUSED(buf);
23943+ _SOKOL_UNUSED(data);
23944+ return true;
23945+ #else
23946+ if (_sg.desc.disable_validation) {
23947+ return true;
23948+ }
23949+ SOKOL_ASSERT(buf && data && data->ptr);
23950+ _sg_validate_begin();
23951+ _SG_VALIDATE(!buf->cmn.usage.immutable, VALIDATE_UPDATEBUF_USAGE);
23952+ _SG_VALIDATE(buf->cmn.size >= (int)data->size, VALIDATE_UPDATEBUF_SIZE);
23953+ _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_ONCE);
23954+ _SG_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_APPEND);
23955+ return _sg_validate_end();
23956+ #endif
23957+}
23958+
23959+_SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const sg_range* data) {
23960+ #if !defined(SOKOL_DEBUG)
23961+ _SOKOL_UNUSED(buf);
23962+ _SOKOL_UNUSED(data);
23963+ return true;
23964+ #else
23965+ if (_sg.desc.disable_validation) {
23966+ return true;
23967+ }
23968+ SOKOL_ASSERT(buf && data && data->ptr);
23969+ _sg_validate_begin();
23970+ _SG_VALIDATE(!buf->cmn.usage.immutable, VALIDATE_APPENDBUF_USAGE);
23971+ _SG_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), VALIDATE_APPENDBUF_SIZE);
23972+ _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_APPENDBUF_UPDATE);
23973+ return _sg_validate_end();
23974+ #endif
23975+}
23976+
23977+_SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_image_data* data) {
23978+ #if !defined(SOKOL_DEBUG)
23979+ _SOKOL_UNUSED(img);
23980+ _SOKOL_UNUSED(data);
23981+ return true;
23982+ #else
23983+ if (_sg.desc.disable_validation) {
23984+ return true;
23985+ }
23986+ SOKOL_ASSERT(img && data);
23987+ _sg_validate_begin();
23988+ _SG_VALIDATE(!img->cmn.usage.immutable, VALIDATE_UPDIMG_USAGE);
23989+ _SG_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, VALIDATE_UPDIMG_ONCE);
23990+ _sg_validate_image_data(data,
23991+ img->cmn.pixel_format,
23992+ img->cmn.width,
23993+ img->cmn.height,
23994+ img->cmn.num_mipmaps,
23995+ img->cmn.num_slices);
23996+ return _sg_validate_end();
23997+ #endif
23998+}
23999+
24000+_SOKOL_PRIVATE bool _sg_validate_shader_binding_limits(const sg_shader_desc* desc) {
24001+ SOKOL_ASSERT(desc);
24002+
24003+ // NOTE: this validation check is also active in release mode, if a shader uses
24004+ // more bindings than allowed, shader creation will fail
24005+ int vs_num_tex = 0;
24006+ int fs_num_tex = 0;
24007+ int cs_num_tex = 0;
24008+ int vs_num_sbuf = 0;
24009+ int fs_num_sbuf = 0;
24010+ int cs_num_sbuf = 0;
24011+ int vs_num_simg = 0;
24012+ int fs_num_simg = 0;
24013+ int cs_num_simg = 0;
24014+ int vs_num_texsmp = 0;
24015+ int fs_num_texsmp = 0;
24016+ int cs_num_texsmp = 0;
24017+ for (size_t i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
24018+ switch (desc->views[i].texture.stage) {
24019+ case SG_SHADERSTAGE_VERTEX: vs_num_tex++; break;
24020+ case SG_SHADERSTAGE_FRAGMENT: fs_num_tex++; break;
24021+ case SG_SHADERSTAGE_COMPUTE: cs_num_tex++; break;
24022+ default: break;
24023+ }
24024+ switch (desc->views[i].storage_buffer.stage) {
24025+ case SG_SHADERSTAGE_VERTEX: vs_num_sbuf++; break;
24026+ case SG_SHADERSTAGE_FRAGMENT: fs_num_sbuf++; break;
24027+ case SG_SHADERSTAGE_COMPUTE: cs_num_sbuf++; break;
24028+ default: break;
24029+ }
24030+ switch (desc->views[i].storage_image.stage) {
24031+ case SG_SHADERSTAGE_VERTEX: vs_num_simg++; break;
24032+ case SG_SHADERSTAGE_FRAGMENT: fs_num_simg++; break;
24033+ case SG_SHADERSTAGE_COMPUTE: cs_num_simg++; break;
24034+ default: break;
24035+ }
24036+ }
24037+ for (size_t i = 0; i < SG_MAX_TEXTURE_SAMPLER_PAIRS; i++) {
24038+ switch (desc->texture_sampler_pairs[i].stage) {
24039+ case SG_SHADERSTAGE_VERTEX: vs_num_texsmp++; break;
24040+ case SG_SHADERSTAGE_FRAGMENT: fs_num_texsmp++; break;
24041+ case SG_SHADERSTAGE_COMPUTE: cs_num_texsmp++; break;
24042+ default: break;
24043+ }
24044+ }
24045+ const int max_tex = _sg.limits.max_texture_bindings_per_stage;
24046+ const int max_sbuf = _sg.limits.max_storage_buffer_bindings_per_stage;
24047+ const int max_simg = _sg.limits.max_storage_image_bindings_per_stage;
24048+ bool retval = true;
24049+ if (vs_num_tex > max_tex) {
24050+ _SG_ERROR(SHADERDESC_TOO_MANY_VERTEXSTAGE_TEXTURES);
24051+ retval = false;
24052+ }
24053+ if (fs_num_tex > max_tex) {
24054+ _SG_ERROR(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_TEXTURES);
24055+ retval = false;
24056+ }
24057+ if (cs_num_tex > max_tex) {
24058+ _SG_ERROR(SHADERDESC_TOO_MANY_COMPUTESTAGE_TEXTURES);
24059+ retval = false;
24060+ }
24061+ if (vs_num_sbuf > max_sbuf) {
24062+ _SG_ERROR(SHADERDESC_TOO_MANY_VERTEXSTAGE_STORAGEBUFFERS);
24063+ retval = false;
24064+ }
24065+ if (fs_num_sbuf > max_sbuf) {
24066+ _SG_ERROR(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_STORAGEBUFFERS);
24067+ retval = false;
24068+ }
24069+ if (cs_num_sbuf > max_sbuf) {
24070+ _SG_ERROR(SHADERDESC_TOO_MANY_COMPUTESTAGE_STORAGEBUFFERS);
24071+ retval = false;
24072+ }
24073+ if (vs_num_simg > max_simg) {
24074+ _SG_ERROR(SHADERDESC_TOO_MANY_VERTEXSTAGE_STORAGEIMAGES);
24075+ retval = false;
24076+ }
24077+ if (fs_num_simg > max_simg) {
24078+ _SG_ERROR(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_STORAGEIMAGES);
24079+ retval = false;
24080+ }
24081+ if (cs_num_simg > max_simg) {
24082+ _SG_ERROR(SHADERDESC_TOO_MANY_COMPUTESTAGE_STORAGEIMAGES);
24083+ retval = false;
24084+ }
24085+ if (vs_num_texsmp > max_tex) {
24086+ _SG_ERROR(SHADERDESC_TOO_MANY_VERTEXSTAGE_TEXTURESAMPLERPAIRS);
24087+ retval = false;
24088+ }
24089+ if (fs_num_texsmp > max_tex) {
24090+ _SG_ERROR(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_TEXTURESAMPLERPAIRS);
24091+ retval = false;
24092+ }
24093+ if (cs_num_texsmp > max_tex) {
24094+ _SG_ERROR(SHADERDESC_TOO_MANY_COMPUTESTAGE_TEXTURESAMPLERPAIRS);
24095+ retval = false;
24096+ }
24097+ return retval;
24098+}
24099+
24100+_SOKOL_PRIVATE bool _sg_validate_pass_attachment_limits(const sg_pass* pass) {
24101+ SOKOL_ASSERT(pass);
24102+ int num_color_atts = 0;
24103+ int num_resolve_atts = 0;
24104+ for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) {
24105+ if (pass->attachments.colors[att_index].id != SG_INVALID_ID) {
24106+ num_color_atts += 1;
24107+ }
24108+ if (pass->attachments.resolves[att_index].id != SG_INVALID_ID) {
24109+ num_resolve_atts += 1;
24110+ }
24111+ }
24112+ bool retval = true;
24113+ int max_color_atts = _sg.limits.max_color_attachments;
24114+ if (num_color_atts > max_color_atts) {
24115+ _SG_ERROR(BEGINPASS_TOO_MANY_COLOR_ATTACHMENTS);
24116+ retval = false;
24117+ }
24118+ // max_color_attachments not a bug
24119+ if (num_resolve_atts > max_color_atts) {
24120+ _SG_ERROR(BEGINPASS_TOO_MANY_RESOLVE_ATTACHMENTS);
24121+ retval = false;
24122+ }
24123+ return retval;
24124+}
24125+
24126+// ██████ ███████ ███████ ██████ ██ ██ ██████ ██████ ███████ ███████
24127+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
24128+// ██████ █████ ███████ ██ ██ ██ ██ ██████ ██ █████ ███████
24129+// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
24130+// ██ ██ ███████ ███████ ██████ ██████ ██ ██ ██████ ███████ ███████
24131+//
24132+// >>resources
24133+_SOKOL_PRIVATE sg_buffer_usage _sg_buffer_usage_defaults(const sg_buffer_usage* usg) {
24134+ sg_buffer_usage def = *usg;
24135+ if (!(def.vertex_buffer || def.index_buffer || def.storage_buffer)) {
24136+ def.vertex_buffer = true;
24137+ }
24138+ if (!(def.immutable || def.stream_update || def.dynamic_update)) {
24139+ def.immutable = true;
24140+ }
24141+ return def;
24142+}
24143+
24144+
24145+_SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* desc) {
24146+ sg_buffer_desc def = *desc;
24147+ def.usage = _sg_buffer_usage_defaults(&def.usage);
24148+ if (def.size == 0) {
24149+ def.size = def.data.size;
24150+ }
24151+ return def;
24152+}
24153+
24154+_SOKOL_PRIVATE sg_image_usage _sg_image_usage_defaults(const sg_image_usage *usg) {
24155+ sg_image_usage def = *usg;
24156+ if (!(def.immutable || def.stream_update || def.dynamic_update)) {
24157+ def.immutable = true;
24158+ }
24159+ return def;
24160+}
24161+
24162+_SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) {
24163+ sg_image_desc def = *desc;
24164+ def.type = _sg_def(def.type, SG_IMAGETYPE_2D);
24165+ def.usage = _sg_image_usage_defaults(&def.usage);
24166+ def.num_slices = _sg_def(def.num_slices, def.type == SG_IMAGETYPE_CUBE ? 6 : 1);
24167+ def.num_mipmaps = _sg_def(def.num_mipmaps, 1);
24168+ if (def.usage.color_attachment || def.usage.resolve_attachment) {
24169+ def.pixel_format = _sg_def(def.pixel_format, _sg.desc.environment.defaults.color_format);
24170+ def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count);
24171+ } else if (def.usage.depth_stencil_attachment) {
24172+ def.pixel_format = _sg_def(def.pixel_format, _sg.desc.environment.defaults.depth_format);
24173+ def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count);
24174+ } else {
24175+ def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8);
24176+ def.sample_count = _sg_def(def.sample_count, 1);
24177+ }
24178+ return def;
24179+}
24180+
24181+_SOKOL_PRIVATE sg_sampler_desc _sg_sampler_desc_defaults(const sg_sampler_desc* desc) {
24182+ sg_sampler_desc def = *desc;
24183+ def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST);
24184+ def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST);
24185+ def.mipmap_filter = _sg_def(def.mipmap_filter, SG_FILTER_NEAREST);
24186+ def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT);
24187+ def.wrap_v = _sg_def(def.wrap_v, SG_WRAP_REPEAT);
24188+ def.wrap_w = _sg_def(def.wrap_w, SG_WRAP_REPEAT);
24189+ def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX);
24190+ def.border_color = _sg_def(def.border_color, SG_BORDERCOLOR_OPAQUE_BLACK);
24191+ def.compare = _sg_def(def.compare, SG_COMPAREFUNC_NEVER);
24192+ def.max_anisotropy = _sg_def(def.max_anisotropy, 1);
24193+ return def;
24194+}
24195+
24196+_SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* desc) {
24197+ sg_shader_desc def = *desc;
24198+ #if defined(SOKOL_METAL)
24199+ def.vertex_func.entry = _sg_def(def.vertex_func.entry, "_main");
24200+ def.fragment_func.entry = _sg_def(def.fragment_func.entry, "_main");
24201+ def.compute_func.entry = _sg_def(def.compute_func.entry, "_main");
24202+ #else
24203+ def.vertex_func.entry = _sg_def(def.vertex_func.entry, "main");
24204+ def.fragment_func.entry = _sg_def(def.fragment_func.entry, "main");
24205+ def.compute_func.entry = _sg_def(def.compute_func.entry, "main");
24206+ #endif
24207+ #if defined(SOKOL_D3D11)
24208+ if (def.vertex_func.source) {
24209+ def.vertex_func.d3d11_target = _sg_def(def.vertex_func.d3d11_target, "vs_4_0");
24210+ }
24211+ if (def.fragment_func.source) {
24212+ def.fragment_func.d3d11_target = _sg_def(def.fragment_func.d3d11_target, "ps_4_0");
24213+ }
24214+ if (def.compute_func.source) {
24215+ def.compute_func.d3d11_target = _sg_def(def.fragment_func.d3d11_target,"cs_5_0");
24216+ }
24217+ #endif
24218+ def.mtl_threads_per_threadgroup.y = _sg_def(desc->mtl_threads_per_threadgroup.y, 1);
24219+ def.mtl_threads_per_threadgroup.z = _sg_def(desc->mtl_threads_per_threadgroup.z, 1);
24220+ for (size_t ub_index = 0; ub_index < SG_MAX_UNIFORMBLOCK_BINDSLOTS; ub_index++) {
24221+ sg_shader_uniform_block* ub_desc = &def.uniform_blocks[ub_index];
24222+ if (ub_desc->stage != SG_SHADERSTAGE_NONE) {
24223+ ub_desc->layout = _sg_def(ub_desc->layout, SG_UNIFORMLAYOUT_NATIVE);
24224+ for (size_t u_index = 0; u_index < SG_MAX_UNIFORMBLOCK_MEMBERS; u_index++) {
24225+ sg_glsl_shader_uniform* u_desc = &ub_desc->glsl_uniforms[u_index];
24226+ if (u_desc->type == SG_UNIFORMTYPE_INVALID) {
24227+ break;
24228+ }
24229+ u_desc->array_count = _sg_def(u_desc->array_count, 1);
24230+ }
24231+ }
24232+ }
24233+ for (size_t view_index = 0; view_index < SG_MAX_VIEW_BINDSLOTS; view_index++) {
24234+ sg_shader_view* view_desc = &def.views[view_index];
24235+ if (view_desc->texture.stage != SG_SHADERSTAGE_NONE) {
24236+ view_desc->texture.image_type = _sg_def(view_desc->texture.image_type, SG_IMAGETYPE_2D);
24237+ view_desc->texture.sample_type = _sg_def(view_desc->texture.sample_type, SG_IMAGESAMPLETYPE_FLOAT);
24238+ } else if (view_desc->storage_image.stage != SG_SHADERSTAGE_NONE) {
24239+ view_desc->storage_image.image_type = _sg_def(view_desc->storage_image.image_type, SG_IMAGETYPE_2D);
24240+ }
24241+ }
24242+ for (size_t smp_index = 0; smp_index < SG_MAX_SAMPLER_BINDSLOTS; smp_index++) {
24243+ sg_shader_sampler* smp_desc = &def.samplers[smp_index];
24244+ if (smp_desc->stage != SG_SHADERSTAGE_NONE) {
24245+ smp_desc->sampler_type = _sg_def(smp_desc->sampler_type, SG_SAMPLERTYPE_FILTERING);
24246+ }
24247+ }
24248+ return def;
24249+}
24250+
24251+_SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_desc* desc) {
24252+ sg_pipeline_desc def = *desc;
24253+
24254+ // FIXME: should we actually do all this stuff for a compute pipeline?
24255+
24256+ def.primitive_type = _sg_def(def.primitive_type, SG_PRIMITIVETYPE_TRIANGLES);
24257+ def.index_type = _sg_def(def.index_type, SG_INDEXTYPE_NONE);
24258+ def.cull_mode = _sg_def(def.cull_mode, SG_CULLMODE_NONE);
24259+ def.face_winding = _sg_def(def.face_winding, SG_FACEWINDING_CW);
24260+ def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count);
24261+
24262+ def.stencil.front.compare = _sg_def(def.stencil.front.compare, SG_COMPAREFUNC_ALWAYS);
24263+ def.stencil.front.fail_op = _sg_def(def.stencil.front.fail_op, SG_STENCILOP_KEEP);
24264+ def.stencil.front.depth_fail_op = _sg_def(def.stencil.front.depth_fail_op, SG_STENCILOP_KEEP);
24265+ def.stencil.front.pass_op = _sg_def(def.stencil.front.pass_op, SG_STENCILOP_KEEP);
24266+ def.stencil.back.compare = _sg_def(def.stencil.back.compare, SG_COMPAREFUNC_ALWAYS);
24267+ def.stencil.back.fail_op = _sg_def(def.stencil.back.fail_op, SG_STENCILOP_KEEP);
24268+ def.stencil.back.depth_fail_op = _sg_def(def.stencil.back.depth_fail_op, SG_STENCILOP_KEEP);
24269+ def.stencil.back.pass_op = _sg_def(def.stencil.back.pass_op, SG_STENCILOP_KEEP);
24270+
24271+ def.depth.compare = _sg_def(def.depth.compare, SG_COMPAREFUNC_ALWAYS);
24272+ def.depth.pixel_format = _sg_def(def.depth.pixel_format, _sg.desc.environment.defaults.depth_format);
24273+ if (def.colors[0].pixel_format == SG_PIXELFORMAT_NONE) {
24274+ // special case depth-only rendering, enforce a color count of 0
24275+ def.color_count = 0;
24276+ } else {
24277+ def.color_count = _sg_def(def.color_count, 1);
24278+ }
24279+ if (def.color_count > SG_MAX_COLOR_ATTACHMENTS) {
24280+ def.color_count = SG_MAX_COLOR_ATTACHMENTS;
24281+ }
24282+ for (int i = 0; i < def.color_count; i++) {
24283+ sg_color_target_state* cs = &def.colors[i];
24284+ cs->pixel_format = _sg_def(cs->pixel_format, _sg.desc.environment.defaults.color_format);
24285+ cs->write_mask = _sg_def(cs->write_mask, SG_COLORMASK_RGBA);
24286+ sg_blend_state* bs = &def.colors[i].blend;
24287+ bs->op_rgb = _sg_def(bs->op_rgb, SG_BLENDOP_ADD);
24288+ bs->src_factor_rgb = _sg_def(bs->src_factor_rgb, SG_BLENDFACTOR_ONE);
24289+ if ((bs->op_rgb == SG_BLENDOP_MIN) || (bs->op_rgb == SG_BLENDOP_MAX)) {
24290+ bs->dst_factor_rgb = _sg_def(bs->dst_factor_rgb, SG_BLENDFACTOR_ONE);
24291+ } else {
24292+ bs->dst_factor_rgb = _sg_def(bs->dst_factor_rgb, SG_BLENDFACTOR_ZERO);
24293+ }
24294+ bs->op_alpha = _sg_def(bs->op_alpha, SG_BLENDOP_ADD);
24295+ bs->src_factor_alpha = _sg_def(bs->src_factor_alpha, SG_BLENDFACTOR_ONE);
24296+ if ((bs->op_alpha == SG_BLENDOP_MIN) || (bs->op_alpha == SG_BLENDOP_MAX)) {
24297+ bs->dst_factor_alpha = _sg_def(bs->dst_factor_alpha, SG_BLENDFACTOR_ONE);
24298+ } else {
24299+ bs->dst_factor_alpha = _sg_def(bs->dst_factor_alpha, SG_BLENDFACTOR_ZERO);
24300+ }
24301+ }
24302+
24303+ for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) {
24304+ sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index];
24305+ if (a_state->format == SG_VERTEXFORMAT_INVALID) {
24306+ break;
24307+ }
24308+ SOKOL_ASSERT((a_state->buffer_index >= 0) && (a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS));
24309+ sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[a_state->buffer_index];
24310+ l_state->step_func = _sg_def(l_state->step_func, SG_VERTEXSTEP_PER_VERTEX);
24311+ l_state->step_rate = _sg_def(l_state->step_rate, 1);
24312+ }
24313+
24314+ // resolve vertex layout strides and offsets
24315+ _SG_STRUCT(int, auto_offset[SG_MAX_VERTEXBUFFER_BINDSLOTS]);
24316+ bool use_auto_offset = true;
24317+ for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) {
24318+ // to use computed offsets, *all* attr offsets must be 0
24319+ if (def.layout.attrs[attr_index].offset != 0) {
24320+ use_auto_offset = false;
24321+ }
24322+ }
24323+ for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) {
24324+ sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index];
24325+ if (a_state->format == SG_VERTEXFORMAT_INVALID) {
24326+ break;
24327+ }
24328+ SOKOL_ASSERT((a_state->buffer_index >= 0) && (a_state->buffer_index < SG_MAX_VERTEXBUFFER_BINDSLOTS));
24329+ if (use_auto_offset) {
24330+ a_state->offset = auto_offset[a_state->buffer_index];
24331+ }
24332+ auto_offset[a_state->buffer_index] += _sg_vertexformat_bytesize(a_state->format);
24333+ }
24334+ // compute vertex strides if needed
24335+ for (int buf_index = 0; buf_index < SG_MAX_VERTEXBUFFER_BINDSLOTS; buf_index++) {
24336+ sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[buf_index];
24337+ if (l_state->stride == 0) {
24338+ l_state->stride = auto_offset[buf_index];
24339+ }
24340+ }
24341+
24342+ return def;
24343+}
24344+
24345+_SOKOL_PRIVATE sg_view_desc _sg_view_desc_defaults(const sg_view_desc* desc) {
24346+ sg_view_desc def = *desc;
24347+ return def;
24348+}
24349+
24350+_SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) {
24351+ sg_buffer res;
24352+ int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool);
24353+ if (_SG_INVALID_SLOT_INDEX != slot_index) {
24354+ res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index);
24355+ _sg_resource_stats_inc(buffers.allocated);
24356+ } else {
24357+ res.id = SG_INVALID_ID;
24358+ _SG_ERROR(BUFFER_POOL_EXHAUSTED);
24359+ }
24360+ return res;
24361+}
24362+
24363+_SOKOL_PRIVATE sg_image _sg_alloc_image(void) {
24364+ sg_image res;
24365+ int slot_index = _sg_pool_alloc_index(&_sg.pools.image_pool);
24366+ if (_SG_INVALID_SLOT_INDEX != slot_index) {
24367+ res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index);
24368+ _sg_resource_stats_inc(images.allocated);
24369+ } else {
24370+ res.id = SG_INVALID_ID;
24371+ _SG_ERROR(IMAGE_POOL_EXHAUSTED);
24372+ }
24373+ return res;
24374+}
24375+
24376+_SOKOL_PRIVATE sg_sampler _sg_alloc_sampler(void) {
24377+ sg_sampler res;
24378+ int slot_index = _sg_pool_alloc_index(&_sg.pools.sampler_pool);
24379+ if (_SG_INVALID_SLOT_INDEX != slot_index) {
24380+ res.id = _sg_slot_alloc(&_sg.pools.sampler_pool, &_sg.pools.samplers[slot_index].slot, slot_index);
24381+ _sg_resource_stats_inc(samplers.allocated);
24382+ } else {
24383+ res.id = SG_INVALID_ID;
24384+ _SG_ERROR(SAMPLER_POOL_EXHAUSTED);
24385+ }
24386+ return res;
24387+}
24388+
24389+_SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) {
24390+ sg_shader res;
24391+ int slot_index = _sg_pool_alloc_index(&_sg.pools.shader_pool);
24392+ if (_SG_INVALID_SLOT_INDEX != slot_index) {
24393+ res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index);
24394+ _sg_resource_stats_inc(shaders.allocated);
24395+ } else {
24396+ res.id = SG_INVALID_ID;
24397+ _SG_ERROR(SHADER_POOL_EXHAUSTED);
24398+ }
24399+ return res;
24400+}
24401+
24402+_SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) {
24403+ sg_pipeline res;
24404+ int slot_index = _sg_pool_alloc_index(&_sg.pools.pipeline_pool);
24405+ if (_SG_INVALID_SLOT_INDEX != slot_index) {
24406+ res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index);
24407+ _sg_resource_stats_inc(pipelines.allocated);
24408+ } else {
24409+ res.id = SG_INVALID_ID;
24410+ _SG_ERROR(PIPELINE_POOL_EXHAUSTED);
24411+ }
24412+ return res;
24413+}
24414+
24415+_SOKOL_PRIVATE sg_view _sg_alloc_view(void) {
24416+ sg_view res;
24417+ int slot_index = _sg_pool_alloc_index(&_sg.pools.view_pool);
24418+ if (_SG_INVALID_SLOT_INDEX != slot_index) {
24419+ res.id = _sg_slot_alloc(&_sg.pools.view_pool, &_sg.pools.views[slot_index].slot, slot_index);
24420+ _sg_resource_stats_inc(views.allocated);
24421+ } else {
24422+ res.id = SG_INVALID_ID;
24423+ _SG_ERROR(VIEW_POOL_EXHAUSTED);
24424+ }
24425+ return res;
24426+}
24427+
24428+_SOKOL_PRIVATE void _sg_dealloc_buffer(_sg_buffer_t* buf) {
24429+ SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC) && (buf->slot.id != SG_INVALID_ID));
24430+ _sg_pool_free_index(&_sg.pools.buffer_pool, _sg_slot_index(buf->slot.id));
24431+ _sg_slot_reset(&buf->slot);
24432+ _sg_resource_stats_inc(buffers.deallocated);
24433+}
24434+
24435+_SOKOL_PRIVATE void _sg_dealloc_image(_sg_image_t* img) {
24436+ SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC) && (img->slot.id != SG_INVALID_ID));
24437+ _sg_pool_free_index(&_sg.pools.image_pool, _sg_slot_index(img->slot.id));
24438+ _sg_slot_reset(&img->slot);
24439+ _sg_resource_stats_inc(images.deallocated);
24440+}
24441+
24442+_SOKOL_PRIVATE void _sg_dealloc_sampler(_sg_sampler_t* smp) {
24443+ SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC) && (smp->slot.id != SG_INVALID_ID));
24444+ _sg_pool_free_index(&_sg.pools.sampler_pool, _sg_slot_index(smp->slot.id));
24445+ _sg_slot_reset(&smp->slot);
24446+ _sg_resource_stats_inc(samplers.deallocated);
24447+}
24448+
24449+_SOKOL_PRIVATE void _sg_dealloc_shader(_sg_shader_t* shd) {
24450+ SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC) && (shd->slot.id != SG_INVALID_ID));
24451+ _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd->slot.id));
24452+ _sg_slot_reset(&shd->slot);
24453+ _sg_resource_stats_inc(shaders.deallocated);
24454+}
24455+
24456+_SOKOL_PRIVATE void _sg_dealloc_pipeline(_sg_pipeline_t* pip) {
24457+ SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC) && (pip->slot.id != SG_INVALID_ID));
24458+ _sg_pool_free_index(&_sg.pools.pipeline_pool, _sg_slot_index(pip->slot.id));
24459+ _sg_slot_reset(&pip->slot);
24460+ _sg_resource_stats_inc(pipelines.deallocated);
24461+}
24462+
24463+_SOKOL_PRIVATE void _sg_dealloc_view(_sg_view_t* view) {
24464+ SOKOL_ASSERT(view && (view->slot.state == SG_RESOURCESTATE_ALLOC) && (view->slot.id != SG_INVALID_ID));
24465+ _sg_pool_free_index(&_sg.pools.view_pool, _sg_slot_index(view->slot.id));
24466+ _sg_slot_reset(&view->slot);
24467+ _sg_resource_stats_inc(views.deallocated);
24468+}
24469+
24470+_SOKOL_PRIVATE void _sg_init_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) {
24471+ SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC));
24472+ SOKOL_ASSERT(desc);
24473+ if (_sg_validate_buffer_desc(desc)) {
24474+ _sg_buffer_common_init(&buf->cmn, desc);
24475+ buf->slot.state = _sg_create_buffer(buf, desc);
24476+ } else {
24477+ buf->slot.state = SG_RESOURCESTATE_FAILED;
24478+ }
24479+ SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED));
24480+ _sg_resource_stats_inc(buffers.inited);
24481+}
24482+
24483+_SOKOL_PRIVATE void _sg_init_image(_sg_image_t* img, const sg_image_desc* desc) {
24484+ SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC));
24485+ SOKOL_ASSERT(desc);
24486+ if (_sg_validate_image_desc(desc)) {
24487+ _sg_image_common_init(&img->cmn, desc);
24488+ img->slot.state = _sg_create_image(img, desc);
24489+ } else {
24490+ img->slot.state = SG_RESOURCESTATE_FAILED;
24491+ }
24492+ SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED));
24493+ _sg_resource_stats_inc(images.inited);
24494+}
24495+
24496+_SOKOL_PRIVATE void _sg_init_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) {
24497+ SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC));
24498+ SOKOL_ASSERT(desc);
24499+ if (_sg_validate_sampler_desc(desc)) {
24500+ _sg_sampler_common_init(&smp->cmn, desc);
24501+ smp->slot.state = _sg_create_sampler(smp, desc);
24502+ } else {
24503+ smp->slot.state = SG_RESOURCESTATE_FAILED;
24504+ }
24505+ SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID)||(smp->slot.state == SG_RESOURCESTATE_FAILED));
24506+ _sg_resource_stats_inc(samplers.inited);
24507+}
24508+
24509+_SOKOL_PRIVATE void _sg_init_shader(_sg_shader_t* shd, const sg_shader_desc* desc) {
24510+ SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC));
24511+ SOKOL_ASSERT(desc);
24512+ if (!_sg_validate_shader_desc(desc)) {
24513+ shd->slot.state = SG_RESOURCESTATE_FAILED;
24514+ return;
24515+ }
24516+ if (!_sg_validate_shader_binding_limits(desc)) {
24517+ shd->slot.state = SG_RESOURCESTATE_FAILED;
24518+ return;
24519+ }
24520+ _sg_shader_common_init(&shd->cmn, desc);
24521+ shd->slot.state = _sg_create_shader(shd, desc);
24522+ SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED));
24523+ _sg_resource_stats_inc(shaders.inited);
24524+}
24525+
24526+_SOKOL_PRIVATE void _sg_init_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) {
24527+ SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC));
24528+ SOKOL_ASSERT(desc);
24529+ if (_sg_validate_pipeline_desc(desc)) {
24530+ _sg_shader_t* shd = _sg_lookup_shader(desc->shader.id);
24531+ if (shd && (shd->slot.state == SG_RESOURCESTATE_VALID)) {
24532+ _sg_pipeline_common_init(&pip->cmn, desc, shd);
24533+ pip->slot.state = _sg_create_pipeline(pip, desc);
24534+ } else {
24535+ pip->slot.state = SG_RESOURCESTATE_FAILED;
24536+ }
24537+ } else {
24538+ pip->slot.state = SG_RESOURCESTATE_FAILED;
24539+ }
24540+ SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED));
24541+ _sg_resource_stats_inc(pipelines.inited);
24542+}
24543+
24544+_SOKOL_PRIVATE void _sg_init_view(_sg_view_t* view, const sg_view_desc* desc) {
24545+ SOKOL_ASSERT(view && view->slot.state == SG_RESOURCESTATE_ALLOC);
24546+ SOKOL_ASSERT(desc);
24547+ if (_sg_validate_view_desc(desc)) {
24548+ uint32_t buf_id = desc->storage_buffer.buffer.id;
24549+ uint32_t img_id = desc->texture.image.id;
24550+ img_id = img_id ? img_id : desc->storage_image.image.id;
24551+ img_id = img_id ? img_id : desc->color_attachment.image.id;
24552+ img_id = img_id ? img_id : desc->resolve_attachment.image.id;
24553+ img_id = img_id ? img_id : desc->depth_stencil_attachment.image.id;
24554+ _sg_buffer_t* buf = buf_id ? _sg_lookup_buffer(buf_id) : 0;
24555+ _sg_image_t* img = img_id ? _sg_lookup_image(img_id) : 0;
24556+ sg_resource_state res_state = SG_RESOURCESTATE_INVALID;
24557+ if (buf) {
24558+ SOKOL_ASSERT(!img);
24559+ res_state = buf->slot.state;
24560+ } else if (img) {
24561+ SOKOL_ASSERT(!buf);
24562+ res_state = img->slot.state;
24563+ }
24564+ if (res_state == SG_RESOURCESTATE_VALID) {
24565+ _sg_view_common_init(&view->cmn, desc, buf, img);
24566+ view->slot.state = _sg_create_view(view, desc);
24567+ } else {
24568+ view->slot.state = SG_RESOURCESTATE_FAILED;
24569+ }
24570+ } else {
24571+ view->slot.state = SG_RESOURCESTATE_FAILED;
24572+ }
24573+ SOKOL_ASSERT((view->slot.state == SG_RESOURCESTATE_VALID) || (view->slot.state == SG_RESOURCESTATE_FAILED));
24574+ _sg_resource_stats_inc(views.inited);
24575+}
24576+
24577+_SOKOL_PRIVATE void _sg_uninit_buffer(_sg_buffer_t* buf) {
24578+ SOKOL_ASSERT(buf && ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)));
24579+ _sg_discard_buffer(buf);
24580+ _sg_reset_buffer_to_alloc_state(buf);
24581+ _sg_resource_stats_inc(buffers.uninited);
24582+}
24583+
24584+_SOKOL_PRIVATE void _sg_uninit_image(_sg_image_t* img) {
24585+ SOKOL_ASSERT(img && ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)));
24586+ _sg_discard_image(img);
24587+ _sg_reset_image_to_alloc_state(img);
24588+ _sg_resource_stats_inc(images.uninited);
24589+}
24590+
24591+_SOKOL_PRIVATE void _sg_uninit_sampler(_sg_sampler_t* smp) {
24592+ SOKOL_ASSERT(smp && ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)));
24593+ _sg_discard_sampler(smp);
24594+ _sg_reset_sampler_to_alloc_state(smp);
24595+ _sg_resource_stats_inc(samplers.uninited);
24596+}
24597+
24598+_SOKOL_PRIVATE void _sg_uninit_shader(_sg_shader_t* shd) {
24599+ SOKOL_ASSERT(shd && ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)));
24600+ _sg_discard_shader(shd);
24601+ _sg_reset_shader_to_alloc_state(shd);
24602+ _sg_resource_stats_inc(shaders.uninited);
24603+}
24604+
24605+_SOKOL_PRIVATE void _sg_uninit_pipeline(_sg_pipeline_t* pip) {
24606+ SOKOL_ASSERT(pip && ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)));
24607+ _sg_discard_pipeline(pip);
24608+ _sg_reset_pipeline_to_alloc_state(pip);
24609+ _sg_resource_stats_inc(pipelines.uninited);
24610+}
24611+
24612+_SOKOL_PRIVATE void _sg_uninit_view(_sg_view_t* view) {
24613+ SOKOL_ASSERT(view && ((view->slot.state == SG_RESOURCESTATE_VALID) || (view->slot.state == SG_RESOURCESTATE_FAILED)));
24614+ _sg_discard_view(view);
24615+ _sg_reset_view_to_alloc_state(view);
24616+ _sg_resource_stats_inc(views.uninited);
24617+}
24618+
24619+_SOKOL_PRIVATE void _sg_setup_commit_listeners(const sg_desc* desc) {
24620+ SOKOL_ASSERT(desc->max_commit_listeners > 0);
24621+ SOKOL_ASSERT(0 == _sg.commit_listeners.items);
24622+ SOKOL_ASSERT(0 == _sg.commit_listeners.num);
24623+ SOKOL_ASSERT(0 == _sg.commit_listeners.upper);
24624+ _sg.commit_listeners.num = desc->max_commit_listeners;
24625+ const size_t size = (size_t)_sg.commit_listeners.num * sizeof(sg_commit_listener);
24626+ _sg.commit_listeners.items = (sg_commit_listener*)_sg_malloc_clear(size);
24627+}
24628+
24629+_SOKOL_PRIVATE void _sg_discard_commit_listeners(void) {
24630+ SOKOL_ASSERT(0 != _sg.commit_listeners.items);
24631+ _sg_free(_sg.commit_listeners.items);
24632+ _sg.commit_listeners.items = 0;
24633+}
24634+
24635+_SOKOL_PRIVATE void _sg_notify_commit_listeners(void) {
24636+ SOKOL_ASSERT(_sg.commit_listeners.items);
24637+ for (int i = 0; i < _sg.commit_listeners.upper; i++) {
24638+ const sg_commit_listener* listener = &_sg.commit_listeners.items[i];
24639+ if (listener->func) {
24640+ listener->func(listener->user_data);
24641+ }
24642+ }
24643+}
24644+
24645+_SOKOL_PRIVATE bool _sg_add_commit_listener(const sg_commit_listener* new_listener) {
24646+ SOKOL_ASSERT(new_listener && new_listener->func);
24647+ SOKOL_ASSERT(_sg.commit_listeners.items);
24648+ // first check if the listener hadn't been added already
24649+ for (int i = 0; i < _sg.commit_listeners.upper; i++) {
24650+ const sg_commit_listener* slot = &_sg.commit_listeners.items[i];
24651+ if ((slot->func == new_listener->func) && (slot->user_data == new_listener->user_data)) {
24652+ _SG_ERROR(IDENTICAL_COMMIT_LISTENER);
24653+ return false;
24654+ }
24655+ }
24656+ // first try to plug a hole
24657+ sg_commit_listener* slot = 0;
24658+ for (int i = 0; i < _sg.commit_listeners.upper; i++) {
24659+ if (_sg.commit_listeners.items[i].func == 0) {
24660+ slot = &_sg.commit_listeners.items[i];
24661+ break;
24662+ }
24663+ }
24664+ if (!slot) {
24665+ // append to end
24666+ if (_sg.commit_listeners.upper < _sg.commit_listeners.num) {
24667+ slot = &_sg.commit_listeners.items[_sg.commit_listeners.upper++];
24668+ }
24669+ }
24670+ if (!slot) {
24671+ _SG_ERROR(COMMIT_LISTENER_ARRAY_FULL);
24672+ return false;
24673+ }
24674+ *slot = *new_listener;
24675+ return true;
24676+}
24677+
24678+_SOKOL_PRIVATE bool _sg_remove_commit_listener(const sg_commit_listener* listener) {
24679+ SOKOL_ASSERT(listener && listener->func);
24680+ SOKOL_ASSERT(_sg.commit_listeners.items);
24681+ for (int i = 0; i < _sg.commit_listeners.upper; i++) {
24682+ sg_commit_listener* slot = &_sg.commit_listeners.items[i];
24683+ // both the function pointer and user data must match!
24684+ if ((slot->func == listener->func) && (slot->user_data == listener->user_data)) {
24685+ slot->func = 0;
24686+ slot->user_data = 0;
24687+ // NOTE: since _sg_add_commit_listener() already catches duplicates,
24688+ // we don't need to worry about them here
24689+ return true;
24690+ }
24691+ }
24692+ return false;
24693+}
24694+
24695+_SOKOL_PRIVATE sg_desc _sg_desc_defaults(const sg_desc* desc) {
24696+ /*
24697+ NOTE: on WebGPU, the default color pixel format MUST be provided,
24698+ it cannot be a default compile-time constant.
24699+ */
24700+ sg_desc res = *desc;
24701+ #if defined(SOKOL_WGPU)
24702+ SOKOL_ASSERT(SG_PIXELFORMAT_NONE < res.environment.defaults.color_format);
24703+ #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11)
24704+ res.environment.defaults.color_format = _sg_def(res.environment.defaults.color_format, SG_PIXELFORMAT_BGRA8);
24705+ #else
24706+ res.environment.defaults.color_format = _sg_def(res.environment.defaults.color_format, SG_PIXELFORMAT_RGBA8);
24707+ #endif
24708+ res.environment.defaults.depth_format = _sg_def(res.environment.defaults.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL);
24709+ res.environment.defaults.sample_count = _sg_def(res.environment.defaults.sample_count, 1);
24710+ res.buffer_pool_size = _sg_def(res.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE);
24711+ res.image_pool_size = _sg_def(res.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE);
24712+ res.sampler_pool_size = _sg_def(res.sampler_pool_size, _SG_DEFAULT_SAMPLER_POOL_SIZE);
24713+ res.shader_pool_size = _sg_def(res.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE);
24714+ res.pipeline_pool_size = _sg_def(res.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE);
24715+ res.view_pool_size = _sg_def(res.view_pool_size, _SG_DEFAULT_VIEW_POOL_SIZE);
24716+ res.uniform_buffer_size = _sg_def(res.uniform_buffer_size, _SG_DEFAULT_UB_SIZE);
24717+ res.max_commit_listeners = _sg_def(res.max_commit_listeners, _SG_DEFAULT_MAX_COMMIT_LISTENERS);
24718+ res.wgpu.bindgroups_cache_size = _sg_def(res.wgpu.bindgroups_cache_size, _SG_DEFAULT_WGPU_BINDGROUP_CACHE_SIZE);
24719+ res.vulkan.copy_staging_buffer_size = _sg_def(res.vulkan.copy_staging_buffer_size, _SG_DEFAULT_VK_COPY_STAGING_SIZE);
24720+ res.vulkan.stream_staging_buffer_size = _sg_def(res.vulkan.stream_staging_buffer_size, _SG_DEFAULT_VK_STREAM_STAGING_SIZE);
24721+ res.vulkan.descriptor_buffer_size = _sg_def(res.vulkan.descriptor_buffer_size, _SG_DEFAULT_VK_DESCRIPTOR_BUFFER_SIZE);
24722+ return res;
24723+}
24724+
24725+_SOKOL_PRIVATE sg_pass _sg_pass_defaults(const sg_pass* pass) {
24726+ sg_pass res = *pass;
24727+ if (!res.compute) {
24728+ if (!pass->swapchain.invalid && _sg_attachments_empty(&pass->attachments)) {
24729+ // this is a swapchain-pass
24730+ res.swapchain.sample_count = _sg_def(res.swapchain.sample_count, _sg.desc.environment.defaults.sample_count);
24731+ res.swapchain.color_format = _sg_def(res.swapchain.color_format, _sg.desc.environment.defaults.color_format);
24732+ res.swapchain.depth_format = _sg_def(res.swapchain.depth_format, _sg.desc.environment.defaults.depth_format);
24733+ }
24734+ res.action = _sg_pass_action_defaults(&res.action);
24735+ }
24736+ return res;
24737+}
24738+
24739+_SOKOL_PRIVATE void _sg_discard_all_resources(void) {
24740+ /* this is a bit dumb since it loops over all pool slots to
24741+ find the occupied slots, on the other hand it is only ever
24742+ executed at shutdown
24743+ NOTE: ONLY EXECUTE THIS AT SHUTDOWN
24744+ ...because the free queues will not be reset
24745+ and the resource slots not be cleared!
24746+ */
24747+ for (int i = 1; i < _sg.pools.buffer_pool.size; i++) {
24748+ sg_resource_state state = _sg.pools.buffers[i].slot.state;
24749+ if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) {
24750+ _sg_discard_buffer(&_sg.pools.buffers[i]);
24751+ }
24752+ }
24753+ for (int i = 1; i < _sg.pools.image_pool.size; i++) {
24754+ sg_resource_state state = _sg.pools.images[i].slot.state;
24755+ if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) {
24756+ _sg_discard_image(&_sg.pools.images[i]);
24757+ }
24758+ }
24759+ for (int i = 1; i < _sg.pools.sampler_pool.size; i++) {
24760+ sg_resource_state state = _sg.pools.samplers[i].slot.state;
24761+ if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) {
24762+ _sg_discard_sampler(&_sg.pools.samplers[i]);
24763+ }
24764+ }
24765+ for (int i = 1; i < _sg.pools.shader_pool.size; i++) {
24766+ sg_resource_state state = _sg.pools.shaders[i].slot.state;
24767+ if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) {
24768+ _sg_discard_shader(&_sg.pools.shaders[i]);
24769+ }
24770+ }
24771+ for (int i = 1; i < _sg.pools.pipeline_pool.size; i++) {
24772+ sg_resource_state state = _sg.pools.pipelines[i].slot.state;
24773+ if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) {
24774+ _sg_discard_pipeline(&_sg.pools.pipelines[i]);
24775+ }
24776+ }
24777+ for (int i = 1; i < _sg.pools.view_pool.size; i++) {
24778+ sg_resource_state state = _sg.pools.views[i].slot.state;
24779+ if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) {
24780+ _sg_discard_view(&_sg.pools.views[i]);
24781+ }
24782+ }
24783+}
24784+
24785+_SOKOL_PRIVATE void _sg_override_portable_limits(void) {
24786+ if (_sg.desc.enforce_portable_limits) {
24787+ _sg.limits.max_color_attachments = SG_MAX_PORTABLE_COLOR_ATTACHMENTS;
24788+ _sg.limits.max_texture_bindings_per_stage = SG_MAX_PORTABLE_TEXTURE_BINDINGS_PER_STAGE;
24789+ if (_sg.features.compute) {
24790+ _sg.limits.max_storage_buffer_bindings_per_stage = SG_MAX_PORTABLE_STORAGEBUFFER_BINDINGS_PER_STAGE;
24791+ _sg.limits.max_storage_image_bindings_per_stage = SG_MAX_PORTABLE_STORAGEIMAGE_BINDINGS_PER_STAGE;
24792+ }
24793+ }
24794+}
24795+
24796+// ██████ ██ ██ ██████ ██ ██ ██████
24797+// ██ ██ ██ ██ ██ ██ ██ ██ ██
24798+// ██████ ██ ██ ██████ ██ ██ ██
24799+// ██ ██ ██ ██ ██ ██ ██ ██
24800+// ██ ██████ ██████ ███████ ██ ██████
24801+//
24802+// >>public
24803+SOKOL_API_IMPL void sg_setup(const sg_desc* desc) {
24804+ SOKOL_ASSERT(!_sg.valid);
24805+ SOKOL_ASSERT(desc);
24806+ SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0));
24807+ SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
24808+ _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg);
24809+ _sg.desc = _sg_desc_defaults(desc);
24810+ _sg_setup_pools(&_sg.pools, &_sg.desc);
24811+ _sg_setup_commit_listeners(&_sg.desc);
24812+ _sg.frame_index = 1;
24813+ _sg.stats_enabled = true;
24814+ _sg_setup_backend(&_sg.desc);
24815+ _sg_override_portable_limits();
24816+ _sg.valid = true;
24817+}
24818+
24819+SOKOL_API_IMPL void sg_shutdown(void) {
24820+ SOKOL_ASSERT(_sg.valid);
24821+ _sg_discard_all_resources();
24822+ _sg_discard_backend();
24823+ _sg_discard_commit_listeners();
24824+ _sg_discard_pools(&_sg.pools);
24825+ _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg);
24826+}
24827+
24828+SOKOL_API_IMPL bool sg_isvalid(void) {
24829+ return _sg.valid;
24830+}
24831+
24832+SOKOL_API_IMPL sg_desc sg_query_desc(void) {
24833+ SOKOL_ASSERT(_sg.valid);
24834+ return _sg.desc;
24835+}
24836+
24837+SOKOL_API_IMPL sg_backend sg_query_backend(void) {
24838+ SOKOL_ASSERT(_sg.valid);
24839+ return _sg.backend;
24840+}
24841+
24842+SOKOL_API_IMPL sg_features sg_query_features(void) {
24843+ SOKOL_ASSERT(_sg.valid);
24844+ return _sg.features;
24845+}
24846+
24847+SOKOL_API_IMPL sg_limits sg_query_limits(void) {
24848+ SOKOL_ASSERT(_sg.valid);
24849+ return _sg.limits;
24850+}
24851+
24852+SOKOL_API_IMPL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) {
24853+ SOKOL_ASSERT(_sg.valid);
24854+ int fmt_index = (int) fmt;
24855+ SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM));
24856+ const _sg_pixelformat_info_t* src = &_sg.formats[fmt_index];
24857+ _SG_STRUCT(sg_pixelformat_info, res);
24858+ res.sample = src->sample;
24859+ res.filter = src->filter;
24860+ res.render = src->render;
24861+ res.blend = src->blend;
24862+ res.msaa = src->msaa;
24863+ res.depth = src->depth;
24864+ res.compressed = _sg_is_compressed_pixel_format(fmt);
24865+ res.read = src->read;
24866+ res.write = src->write;
24867+ if (!res.compressed) {
24868+ res.bytes_per_pixel = _sg_pixelformat_bytesize(fmt);
24869+ }
24870+ return res;
24871+}
24872+
24873+SOKOL_API_IMPL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes) {
24874+ SOKOL_ASSERT(_sg.valid);
24875+ SOKOL_ASSERT(width > 0);
24876+ SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes));
24877+ SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM));
24878+ return _sg_row_pitch(fmt, width, row_align_bytes);
24879+}
24880+
24881+SOKOL_API_IMPL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes) {
24882+ SOKOL_ASSERT(_sg.valid);
24883+ SOKOL_ASSERT((width > 0) && (height > 0));
24884+ SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes));
24885+ SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM));
24886+ return _sg_surface_pitch(fmt, width, height, row_align_bytes);
24887+}
24888+
24889+SOKOL_API_IMPL sg_stats sg_query_stats(void) {
24890+ SOKOL_ASSERT(_sg.valid);
24891+ _sg_update_alive_free_resource_stats(&_sg.stats.total.buffers, &_sg.pools.buffer_pool);
24892+ _sg_update_alive_free_resource_stats(&_sg.stats.total.images, &_sg.pools.image_pool);
24893+ _sg_update_alive_free_resource_stats(&_sg.stats.total.views, &_sg.pools.view_pool);
24894+ _sg_update_alive_free_resource_stats(&_sg.stats.total.samplers, &_sg.pools.sampler_pool);
24895+ _sg_update_alive_free_resource_stats(&_sg.stats.total.shaders, &_sg.pools.shader_pool);
24896+ _sg_update_alive_free_resource_stats(&_sg.stats.total.pipelines, &_sg.pools.pipeline_pool);
24897+ return _sg.stats;
24898+}
24899+
24900+SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks) {
24901+ SOKOL_ASSERT(_sg.valid);
24902+ SOKOL_ASSERT(trace_hooks);
24903+ _SOKOL_UNUSED(trace_hooks);
24904+ #if defined(SOKOL_TRACE_HOOKS)
24905+ sg_trace_hooks old_hooks = _sg.hooks;
24906+ _sg.hooks = *trace_hooks;
24907+ #else
24908+ static sg_trace_hooks old_hooks;
24909+ _SG_WARN(TRACE_HOOKS_NOT_ENABLED);
24910+ #endif
24911+ return old_hooks;
24912+}
24913+
24914+SOKOL_API_IMPL sg_buffer sg_alloc_buffer(void) {
24915+ SOKOL_ASSERT(_sg.valid);
24916+ sg_buffer res = _sg_alloc_buffer();
24917+ _SG_TRACE_ARGS(alloc_buffer, res);
24918+ return res;
24919+}
24920+
24921+SOKOL_API_IMPL sg_image sg_alloc_image(void) {
24922+ SOKOL_ASSERT(_sg.valid);
24923+ sg_image res = _sg_alloc_image();
24924+ _SG_TRACE_ARGS(alloc_image, res);
24925+ return res;
24926+}
24927+
24928+SOKOL_API_IMPL sg_sampler sg_alloc_sampler(void) {
24929+ SOKOL_ASSERT(_sg.valid);
24930+ sg_sampler res = _sg_alloc_sampler();
24931+ _SG_TRACE_ARGS(alloc_sampler, res);
24932+ return res;
24933+}
24934+
24935+SOKOL_API_IMPL sg_shader sg_alloc_shader(void) {
24936+ SOKOL_ASSERT(_sg.valid);
24937+ sg_shader res = _sg_alloc_shader();
24938+ _SG_TRACE_ARGS(alloc_shader, res);
24939+ return res;
24940+}
24941+
24942+SOKOL_API_IMPL sg_pipeline sg_alloc_pipeline(void) {
24943+ SOKOL_ASSERT(_sg.valid);
24944+ sg_pipeline res = _sg_alloc_pipeline();
24945+ _SG_TRACE_ARGS(alloc_pipeline, res);
24946+ return res;
24947+}
24948+
24949+SOKOL_API_IMPL sg_view sg_alloc_view(void) {
24950+ SOKOL_ASSERT(_sg.valid);
24951+ sg_view res = _sg_alloc_view();
24952+ _SG_TRACE_ARGS(alloc_view, res);
24953+ return res;
24954+}
24955+
24956+SOKOL_API_IMPL void sg_dealloc_buffer(sg_buffer buf_id) {
24957+ SOKOL_ASSERT(_sg.valid);
24958+ _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
24959+ if (buf) {
24960+ if (buf->slot.state == SG_RESOURCESTATE_ALLOC) {
24961+ _sg_dealloc_buffer(buf);
24962+ } else {
24963+ _SG_ERROR(DEALLOC_BUFFER_INVALID_STATE);
24964+ }
24965+ }
24966+ _SG_TRACE_ARGS(dealloc_buffer, buf_id);
24967+}
24968+
24969+SOKOL_API_IMPL void sg_dealloc_image(sg_image img_id) {
24970+ SOKOL_ASSERT(_sg.valid);
24971+ _sg_image_t* img = _sg_lookup_image(img_id.id);
24972+ if (img) {
24973+ if (img->slot.state == SG_RESOURCESTATE_ALLOC) {
24974+ _sg_dealloc_image(img);
24975+ } else {
24976+ _SG_ERROR(DEALLOC_IMAGE_INVALID_STATE);
24977+ }
24978+ }
24979+ _SG_TRACE_ARGS(dealloc_image, img_id);
24980+}
24981+
24982+SOKOL_API_IMPL void sg_dealloc_sampler(sg_sampler smp_id) {
24983+ SOKOL_ASSERT(_sg.valid);
24984+ _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
24985+ if (smp) {
24986+ if (smp->slot.state == SG_RESOURCESTATE_ALLOC) {
24987+ _sg_dealloc_sampler(smp);
24988+ } else {
24989+ _SG_ERROR(DEALLOC_SAMPLER_INVALID_STATE);
24990+ }
24991+ }
24992+ _SG_TRACE_ARGS(dealloc_sampler, smp_id);
24993+}
24994+
24995+SOKOL_API_IMPL void sg_dealloc_shader(sg_shader shd_id) {
24996+ SOKOL_ASSERT(_sg.valid);
24997+ _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
24998+ if (shd) {
24999+ if (shd->slot.state == SG_RESOURCESTATE_ALLOC) {
25000+ _sg_dealloc_shader(shd);
25001+ } else {
25002+ _SG_ERROR(DEALLOC_SHADER_INVALID_STATE);
25003+ }
25004+ }
25005+ _SG_TRACE_ARGS(dealloc_shader, shd_id);
25006+}
25007+
25008+SOKOL_API_IMPL void sg_dealloc_pipeline(sg_pipeline pip_id) {
25009+ SOKOL_ASSERT(_sg.valid);
25010+ _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
25011+ if (pip) {
25012+ if (pip->slot.state == SG_RESOURCESTATE_ALLOC) {
25013+ _sg_dealloc_pipeline(pip);
25014+ } else {
25015+ _SG_ERROR(DEALLOC_PIPELINE_INVALID_STATE);
25016+ }
25017+ }
25018+ _SG_TRACE_ARGS(dealloc_pipeline, pip_id);
25019+}
25020+
25021+SOKOL_API_IMPL void sg_dealloc_view(sg_view view_id) {
25022+ SOKOL_ASSERT(_sg.valid);
25023+ _sg_view_t* view = _sg_lookup_view(view_id.id);
25024+ if (view) {
25025+ if (view->slot.state == SG_RESOURCESTATE_ALLOC) {
25026+ _sg_dealloc_view(view);
25027+ } else {
25028+ _SG_ERROR(DEALLOC_VIEW_INVALID_STATE);
25029+ }
25030+ }
25031+ _SG_TRACE_ARGS(dealloc_view, view_id);
25032+}
25033+
25034+SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) {
25035+ SOKOL_ASSERT(_sg.valid);
25036+ sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc);
25037+ _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
25038+ if (buf) {
25039+ if (buf->slot.state == SG_RESOURCESTATE_ALLOC) {
25040+ _sg_init_buffer(buf, &desc_def);
25041+ SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED));
25042+ } else {
25043+ _SG_ERROR(INIT_BUFFER_INVALID_STATE);
25044+ }
25045+ }
25046+ _SG_TRACE_ARGS(init_buffer, buf_id, &desc_def);
25047+}
25048+
25049+SOKOL_API_IMPL void sg_init_image(sg_image img_id, const sg_image_desc* desc) {
25050+ SOKOL_ASSERT(_sg.valid);
25051+ sg_image_desc desc_def = _sg_image_desc_defaults(desc);
25052+ _sg_image_t* img = _sg_lookup_image(img_id.id);
25053+ if (img) {
25054+ if (img->slot.state == SG_RESOURCESTATE_ALLOC) {
25055+ _sg_init_image(img, &desc_def);
25056+ SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED));
25057+ } else {
25058+ _SG_ERROR(INIT_IMAGE_INVALID_STATE);
25059+ }
25060+ }
25061+ _SG_TRACE_ARGS(init_image, img_id, &desc_def);
25062+}
25063+
25064+SOKOL_API_IMPL void sg_init_sampler(sg_sampler smp_id, const sg_sampler_desc* desc) {
25065+ SOKOL_ASSERT(_sg.valid);
25066+ sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc);
25067+ _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
25068+ if (smp) {
25069+ if (smp->slot.state == SG_RESOURCESTATE_ALLOC) {
25070+ _sg_init_sampler(smp, &desc_def);
25071+ SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED));
25072+ } else {
25073+ _SG_ERROR(INIT_SAMPLER_INVALID_STATE);
25074+ }
25075+ }
25076+ _SG_TRACE_ARGS(init_sampler, smp_id, &desc_def);
25077+}
25078+
25079+SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) {
25080+ SOKOL_ASSERT(_sg.valid);
25081+ sg_shader_desc desc_def = _sg_shader_desc_defaults(desc);
25082+ _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
25083+ if (shd) {
25084+ if (shd->slot.state == SG_RESOURCESTATE_ALLOC) {
25085+ _sg_init_shader(shd, &desc_def);
25086+ SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED));
25087+ } else {
25088+ _SG_ERROR(INIT_SHADER_INVALID_STATE);
25089+ }
25090+ }
25091+ _SG_TRACE_ARGS(init_shader, shd_id, &desc_def);
25092+}
25093+
25094+SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) {
25095+ SOKOL_ASSERT(_sg.valid);
25096+ sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc);
25097+ _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
25098+ if (pip) {
25099+ if (pip->slot.state == SG_RESOURCESTATE_ALLOC) {
25100+ _sg_init_pipeline(pip, &desc_def);
25101+ SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED));
25102+ } else {
25103+ _SG_ERROR(INIT_PIPELINE_INVALID_STATE);
25104+ }
25105+ }
25106+ _SG_TRACE_ARGS(init_pipeline, pip_id, &desc_def);
25107+}
25108+
25109+SOKOL_API_IMPL void sg_init_view(sg_view view_id, const sg_view_desc* desc) {
25110+ SOKOL_ASSERT(_sg.valid);
25111+ sg_view_desc desc_def = _sg_view_desc_defaults(desc);
25112+ _sg_view_t* view = _sg_lookup_view(view_id.id);
25113+ if (view) {
25114+ if (view->slot.state == SG_RESOURCESTATE_ALLOC) {
25115+ _sg_init_view(view, &desc_def);
25116+ SOKOL_ASSERT((view->slot.state == SG_RESOURCESTATE_VALID)
25117+ || (view->slot.state == SG_RESOURCESTATE_FAILED)
25118+ || (view->slot.state == SG_RESOURCESTATE_ALLOC));
25119+ } else {
25120+ _SG_ERROR(INIT_VIEW_INVALID_STATE);
25121+ }
25122+ }
25123+ _SG_TRACE_ARGS(init_view, view_id, &desc_def);
25124+}
25125+
25126+SOKOL_API_IMPL void sg_uninit_buffer(sg_buffer buf_id) {
25127+ SOKOL_ASSERT(_sg.valid);
25128+ _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
25129+ if (buf) {
25130+ if ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)) {
25131+ _sg_uninit_buffer(buf);
25132+ SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC);
25133+ } else if (buf->slot.state != SG_RESOURCESTATE_ALLOC) {
25134+ _SG_ERROR(UNINIT_BUFFER_INVALID_STATE);
25135+ }
25136+ }
25137+ _SG_TRACE_ARGS(uninit_buffer, buf_id);
25138+}
25139+
25140+SOKOL_API_IMPL void sg_uninit_image(sg_image img_id) {
25141+ SOKOL_ASSERT(_sg.valid);
25142+ _sg_image_t* img = _sg_lookup_image(img_id.id);
25143+ if (img) {
25144+ if ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)) {
25145+ _sg_uninit_image(img);
25146+ SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_ALLOC);
25147+ } else if (img->slot.state != SG_RESOURCESTATE_ALLOC) {
25148+ _SG_ERROR(UNINIT_IMAGE_INVALID_STATE);
25149+ }
25150+ }
25151+ _SG_TRACE_ARGS(uninit_image, img_id);
25152+}
25153+
25154+SOKOL_API_IMPL void sg_uninit_sampler(sg_sampler smp_id) {
25155+ SOKOL_ASSERT(_sg.valid);
25156+ _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
25157+ if (smp) {
25158+ if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) {
25159+ _sg_uninit_sampler(smp);
25160+ SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC);
25161+ } else if (smp->slot.state != SG_RESOURCESTATE_ALLOC) {
25162+ _SG_ERROR(UNINIT_SAMPLER_INVALID_STATE);
25163+ }
25164+ }
25165+ _SG_TRACE_ARGS(uninit_sampler, smp_id);
25166+}
25167+
25168+SOKOL_API_IMPL void sg_uninit_shader(sg_shader shd_id) {
25169+ SOKOL_ASSERT(_sg.valid);
25170+ _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
25171+ if (shd) {
25172+ if ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)) {
25173+ _sg_uninit_shader(shd);
25174+ SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_ALLOC);
25175+ } else if (shd->slot.state != SG_RESOURCESTATE_ALLOC) {
25176+ _SG_ERROR(UNINIT_SHADER_INVALID_STATE);
25177+ }
25178+ }
25179+ _SG_TRACE_ARGS(uninit_shader, shd_id);
25180+}
25181+
25182+SOKOL_API_IMPL void sg_uninit_pipeline(sg_pipeline pip_id) {
25183+ SOKOL_ASSERT(_sg.valid);
25184+ _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
25185+ if (pip) {
25186+ if ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)) {
25187+ _sg_uninit_pipeline(pip);
25188+ SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_ALLOC);
25189+ } else if (pip->slot.state != SG_RESOURCESTATE_ALLOC) {
25190+ _SG_ERROR(UNINIT_PIPELINE_INVALID_STATE);
25191+ }
25192+ }
25193+ _SG_TRACE_ARGS(uninit_pipeline, pip_id);
25194+}
25195+
25196+SOKOL_API_IMPL void sg_uninit_view(sg_view view_id) {
25197+ SOKOL_ASSERT(_sg.valid);
25198+ _sg_view_t* view = _sg_lookup_view(view_id.id);
25199+ if (view) {
25200+ if ((view->slot.state == SG_RESOURCESTATE_VALID) || (view->slot.state == SG_RESOURCESTATE_FAILED)) {
25201+ _sg_uninit_view(view);
25202+ SOKOL_ASSERT(view->slot.state == SG_RESOURCESTATE_ALLOC);
25203+ } else if (view->slot.state != SG_RESOURCESTATE_ALLOC) {
25204+ _SG_ERROR(UNINIT_VIEW_INVALID_STATE);
25205+ }
25206+ }
25207+ _SG_TRACE_ARGS(uninit_view, view_id);
25208+}
25209+
25210+SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) {
25211+ SOKOL_ASSERT(_sg.valid);
25212+ _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
25213+ if (buf) {
25214+ if (buf->slot.state == SG_RESOURCESTATE_ALLOC) {
25215+ buf->slot.state = SG_RESOURCESTATE_FAILED;
25216+ } else {
25217+ _SG_ERROR(FAIL_BUFFER_INVALID_STATE);
25218+ }
25219+ }
25220+ _SG_TRACE_ARGS(fail_buffer, buf_id);
25221+}
25222+
25223+SOKOL_API_IMPL void sg_fail_image(sg_image img_id) {
25224+ SOKOL_ASSERT(_sg.valid);
25225+ _sg_image_t* img = _sg_lookup_image(img_id.id);
25226+ if (img) {
25227+ if (img->slot.state == SG_RESOURCESTATE_ALLOC) {
25228+ img->slot.state = SG_RESOURCESTATE_FAILED;
25229+ } else {
25230+ _SG_ERROR(FAIL_IMAGE_INVALID_STATE);
25231+ }
25232+ }
25233+ _SG_TRACE_ARGS(fail_image, img_id);
25234+}
25235+
25236+SOKOL_API_IMPL void sg_fail_sampler(sg_sampler smp_id) {
25237+ SOKOL_ASSERT(_sg.valid);
25238+ _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
25239+ if (smp) {
25240+ if (smp->slot.state == SG_RESOURCESTATE_ALLOC) {
25241+ smp->slot.state = SG_RESOURCESTATE_FAILED;
25242+ } else {
25243+ _SG_ERROR(FAIL_SAMPLER_INVALID_STATE);
25244+ }
25245+ }
25246+ _SG_TRACE_ARGS(fail_sampler, smp_id);
25247+}
25248+
25249+SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) {
25250+ SOKOL_ASSERT(_sg.valid);
25251+ _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
25252+ if (shd) {
25253+ if (shd->slot.state == SG_RESOURCESTATE_ALLOC) {
25254+ shd->slot.state = SG_RESOURCESTATE_FAILED;
25255+ } else {
25256+ _SG_ERROR(FAIL_SHADER_INVALID_STATE);
25257+ }
25258+ }
25259+ _SG_TRACE_ARGS(fail_shader, shd_id);
25260+}
25261+
25262+SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) {
25263+ SOKOL_ASSERT(_sg.valid);
25264+ _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
25265+ if (pip) {
25266+ if (pip->slot.state == SG_RESOURCESTATE_ALLOC) {
25267+ pip->slot.state = SG_RESOURCESTATE_FAILED;
25268+ } else {
25269+ _SG_ERROR(FAIL_PIPELINE_INVALID_STATE);
25270+ }
25271+ }
25272+ _SG_TRACE_ARGS(fail_pipeline, pip_id);
25273+}
25274+
25275+SOKOL_API_IMPL void sg_fail_view(sg_view view_id) {
25276+ SOKOL_ASSERT(_sg.valid);
25277+ _sg_view_t* view = _sg_lookup_view(view_id.id);
25278+ if (view) {
25279+ if (view->slot.state == SG_RESOURCESTATE_ALLOC) {
25280+ view->slot.state = SG_RESOURCESTATE_FAILED;
25281+ } else {
25282+ _SG_ERROR(FAIL_VIEW_INVALID_STATE);
25283+ }
25284+ }
25285+ _SG_TRACE_ARGS(fail_view, view_id);
25286+}
25287+
25288+SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) {
25289+ SOKOL_ASSERT(_sg.valid);
25290+ _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
25291+ sg_resource_state res = buf ? buf->slot.state : SG_RESOURCESTATE_INVALID;
25292+ return res;
25293+}
25294+
25295+SOKOL_API_IMPL sg_resource_state sg_query_image_state(sg_image img_id) {
25296+ SOKOL_ASSERT(_sg.valid);
25297+ _sg_image_t* img = _sg_lookup_image(img_id.id);
25298+ sg_resource_state res = img ? img->slot.state : SG_RESOURCESTATE_INVALID;
25299+ return res;
25300+}
25301+
25302+SOKOL_API_IMPL sg_resource_state sg_query_sampler_state(sg_sampler smp_id) {
25303+ SOKOL_ASSERT(_sg.valid);
25304+ _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
25305+ sg_resource_state res = smp ? smp->slot.state : SG_RESOURCESTATE_INVALID;
25306+ return res;
25307+}
25308+
25309+SOKOL_API_IMPL sg_resource_state sg_query_shader_state(sg_shader shd_id) {
25310+ SOKOL_ASSERT(_sg.valid);
25311+ _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
25312+ sg_resource_state res = shd ? shd->slot.state : SG_RESOURCESTATE_INVALID;
25313+ return res;
25314+}
25315+
25316+SOKOL_API_IMPL sg_resource_state sg_query_pipeline_state(sg_pipeline pip_id) {
25317+ SOKOL_ASSERT(_sg.valid);
25318+ _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
25319+ sg_resource_state res = pip ? pip->slot.state : SG_RESOURCESTATE_INVALID;
25320+ return res;
25321+}
25322+
25323+SOKOL_API_IMPL sg_resource_state sg_query_view_state(sg_view view_id) {
25324+ SOKOL_ASSERT(_sg.valid);
25325+ _sg_view_t* view = _sg_lookup_view(view_id.id);
25326+ sg_resource_state res = view ? view->slot.state : SG_RESOURCESTATE_INVALID;
25327+ return res;
25328+}
25329+
25330+SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) {
25331+ SOKOL_ASSERT(_sg.valid);
25332+ SOKOL_ASSERT(desc);
25333+ sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc);
25334+ sg_buffer buf_id = _sg_alloc_buffer();
25335+ if (buf_id.id != SG_INVALID_ID) {
25336+ _sg_buffer_t* buf = _sg_buffer_at(buf_id.id);
25337+ SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC));
25338+ _sg_init_buffer(buf, &desc_def);
25339+ SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED));
25340+ }
25341+ _SG_TRACE_ARGS(make_buffer, &desc_def, buf_id);
25342+ return buf_id;
25343+}
25344+
25345+SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) {
25346+ SOKOL_ASSERT(_sg.valid);
25347+ SOKOL_ASSERT(desc);
25348+ sg_image_desc desc_def = _sg_image_desc_defaults(desc);
25349+ sg_image img_id = _sg_alloc_image();
25350+ if (img_id.id != SG_INVALID_ID) {
25351+ _sg_image_t* img = _sg_image_at(img_id.id);
25352+ SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC));
25353+ _sg_init_image(img, &desc_def);
25354+ SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED));
25355+ }
25356+ _SG_TRACE_ARGS(make_image, &desc_def, img_id);
25357+ return img_id;
25358+}
25359+
25360+SOKOL_API_IMPL sg_sampler sg_make_sampler(const sg_sampler_desc* desc) {
25361+ SOKOL_ASSERT(_sg.valid);
25362+ SOKOL_ASSERT(desc);
25363+ sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc);
25364+ sg_sampler smp_id = _sg_alloc_sampler();
25365+ if (smp_id.id != SG_INVALID_ID) {
25366+ _sg_sampler_t* smp = _sg_sampler_at(smp_id.id);
25367+ SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC));
25368+ _sg_init_sampler(smp, &desc_def);
25369+ SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED));
25370+ }
25371+ _SG_TRACE_ARGS(make_sampler, &desc_def, smp_id);
25372+ return smp_id;
25373+}
25374+
25375+SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) {
25376+ SOKOL_ASSERT(_sg.valid);
25377+ SOKOL_ASSERT(desc);
25378+ sg_shader_desc desc_def = _sg_shader_desc_defaults(desc);
25379+ sg_shader shd_id = _sg_alloc_shader();
25380+ if (shd_id.id != SG_INVALID_ID) {
25381+ _sg_shader_t* shd = _sg_shader_at(shd_id.id);
25382+ SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC));
25383+ _sg_init_shader(shd, &desc_def);
25384+ SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED));
25385+ }
25386+ _SG_TRACE_ARGS(make_shader, &desc_def, shd_id);
25387+ return shd_id;
25388+}
25389+
25390+SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) {
25391+ SOKOL_ASSERT(_sg.valid);
25392+ SOKOL_ASSERT(desc);
25393+ sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc);
25394+ sg_pipeline pip_id = _sg_alloc_pipeline();
25395+ if (pip_id.id != SG_INVALID_ID) {
25396+ _sg_pipeline_t* pip = _sg_pipeline_at(pip_id.id);
25397+ SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC));
25398+ _sg_init_pipeline(pip, &desc_def);
25399+ SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED));
25400+ }
25401+ _SG_TRACE_ARGS(make_pipeline, &desc_def, pip_id);
25402+ return pip_id;
25403+}
25404+
25405+SOKOL_API_IMPL sg_view sg_make_view(const sg_view_desc* desc) {
25406+ SOKOL_ASSERT(_sg.valid);
25407+ SOKOL_ASSERT(desc);
25408+ sg_view_desc desc_def = _sg_view_desc_defaults(desc);
25409+ sg_view view_id = _sg_alloc_view();
25410+ if (view_id.id != SG_INVALID_ID) {
25411+ _sg_view_t* view = _sg_view_at(view_id.id);
25412+ SOKOL_ASSERT(view && (view->slot.state == SG_RESOURCESTATE_ALLOC));
25413+ _sg_init_view(view, &desc_def);
25414+ SOKOL_ASSERT((view->slot.state == SG_RESOURCESTATE_VALID) || (view->slot.state == SG_RESOURCESTATE_FAILED));
25415+ }
25416+ _SG_TRACE_ARGS(make_view, &desc_def, view_id);
25417+ return view_id;
25418+}
25419+
25420+SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) {
25421+ SOKOL_ASSERT(_sg.valid);
25422+ _SG_TRACE_ARGS(destroy_buffer, buf_id);
25423+ _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
25424+ if (buf) {
25425+ if ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)) {
25426+ _sg_uninit_buffer(buf);
25427+ SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC);
25428+ }
25429+ if (buf->slot.state == SG_RESOURCESTATE_ALLOC) {
25430+ _sg_dealloc_buffer(buf);
25431+ SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_INITIAL);
25432+ }
25433+ }
25434+}
25435+
25436+SOKOL_API_IMPL void sg_destroy_image(sg_image img_id) {
25437+ SOKOL_ASSERT(_sg.valid);
25438+ _SG_TRACE_ARGS(destroy_image, img_id);
25439+ _sg_image_t* img = _sg_lookup_image(img_id.id);
25440+ if (img) {
25441+ if ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)) {
25442+ _sg_uninit_image(img);
25443+ SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_ALLOC);
25444+ }
25445+ if (img->slot.state == SG_RESOURCESTATE_ALLOC) {
25446+ _sg_dealloc_image(img);
25447+ SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_INITIAL);
25448+ }
25449+ }
25450+}
25451+
25452+SOKOL_API_IMPL void sg_destroy_sampler(sg_sampler smp_id) {
25453+ SOKOL_ASSERT(_sg.valid);
25454+ _SG_TRACE_ARGS(destroy_sampler, smp_id);
25455+ _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
25456+ if (smp) {
25457+ if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) {
25458+ _sg_uninit_sampler(smp);
25459+ SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC);
25460+ }
25461+ if (smp->slot.state == SG_RESOURCESTATE_ALLOC) {
25462+ _sg_dealloc_sampler(smp);
25463+ SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_INITIAL);
25464+ }
25465+ }
25466+}
25467+
25468+SOKOL_API_IMPL void sg_destroy_shader(sg_shader shd_id) {
25469+ SOKOL_ASSERT(_sg.valid);
25470+ _SG_TRACE_ARGS(destroy_shader, shd_id);
25471+ _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
25472+ if (shd) {
25473+ if ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)) {
25474+ _sg_uninit_shader(shd);
25475+ SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_ALLOC);
25476+ }
25477+ if (shd->slot.state == SG_RESOURCESTATE_ALLOC) {
25478+ _sg_dealloc_shader(shd);
25479+ SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_INITIAL);
25480+ }
25481+ }
25482+}
25483+
25484+SOKOL_API_IMPL void sg_destroy_pipeline(sg_pipeline pip_id) {
25485+ SOKOL_ASSERT(_sg.valid);
25486+ _SG_TRACE_ARGS(destroy_pipeline, pip_id);
25487+ _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
25488+ if (pip) {
25489+ if ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)) {
25490+ _sg_uninit_pipeline(pip);
25491+ SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_ALLOC);
25492+ }
25493+ if (pip->slot.state == SG_RESOURCESTATE_ALLOC) {
25494+ _sg_dealloc_pipeline(pip);
25495+ SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_INITIAL);
25496+ }
25497+ }
25498+}
25499+
25500+SOKOL_API_IMPL void sg_destroy_view(sg_view view_id) {
25501+ SOKOL_ASSERT(_sg.valid);
25502+ _SG_TRACE_ARGS(destroy_view, view_id);
25503+ _sg_view_t* view = _sg_lookup_view(view_id.id);
25504+ if (view) {
25505+ if ((view->slot.state == SG_RESOURCESTATE_VALID) || (view->slot.state == SG_RESOURCESTATE_FAILED)) {
25506+ _sg_uninit_view(view);
25507+ SOKOL_ASSERT(view->slot.state == SG_RESOURCESTATE_ALLOC);
25508+ }
25509+ if (view->slot.state == SG_RESOURCESTATE_ALLOC) {
25510+ _sg_dealloc_view(view);
25511+ SOKOL_ASSERT(view->slot.state == SG_RESOURCESTATE_INITIAL);
25512+ }
25513+ }
25514+}
25515+
25516+SOKOL_API_IMPL void sg_begin_pass(const sg_pass* pass) {
25517+ SOKOL_ASSERT(_sg.valid);
25518+ SOKOL_ASSERT(!_sg.cur_pass.valid);
25519+ SOKOL_ASSERT(!_sg.cur_pass.in_pass);
25520+ SOKOL_ASSERT(_sg_attachments_empty(&_sg.cur_pass.atts));
25521+ SOKOL_ASSERT(pass);
25522+ SOKOL_ASSERT((pass->_start_canary == 0) && (pass->_end_canary == 0));
25523+ _sg.cur_pass.in_pass = true;
25524+ const sg_pass pass_def = _sg_pass_defaults(pass);
25525+ _SG_TRACE_ARGS(begin_pass, &pass_def);
25526+ if (!_sg_validate_pass_attachment_limits(&pass_def)) {
25527+ return;
25528+ }
25529+ if (!_sg_validate_begin_pass(&pass_def)) {
25530+ return;
25531+ }
25532+ const _sg_attachments_ptrs_t atts_ptrs = _sg_attachments_ptrs(&pass_def.attachments);
25533+ if (!atts_ptrs.empty) {
25534+ if (!_sg_attachments_alive(&atts_ptrs)) {
25535+ _SG_ERROR(BEGINPASS_ATTACHMENTS_ALIVE);
25536+ return;
25537+ }
25538+ _sg.cur_pass.atts = pass->attachments;
25539+ _sg.cur_pass.dim = _sg_attachments_dim(&atts_ptrs);
25540+ } else if (!pass_def.compute) {
25541+ // a swapchain pass
25542+ // NOTE: all values allowed to be zero if swapchain is invalid
25543+ _sg.cur_pass.swapchain.invalid = pass_def.swapchain.invalid;
25544+ _sg.cur_pass.swapchain.color_fmt = pass_def.swapchain.color_format;
25545+ _sg.cur_pass.swapchain.depth_fmt = pass_def.swapchain.depth_format;
25546+ _sg.cur_pass.swapchain.sample_count = pass_def.swapchain.sample_count;
25547+ _sg.cur_pass.dim.width = pass_def.swapchain.width;
25548+ _sg.cur_pass.dim.height = pass_def.swapchain.height;
25549+ }
25550+ _sg.cur_pass.action = pass_def.action;
25551+ _sg.cur_pass.valid = true; // may be overruled by backend begin-pass functions
25552+ _sg.cur_pass.is_compute = pass_def.compute;
25553+ if (_sg.cur_pass.swapchain.invalid) {
25554+ _sg.cur_pass.valid = false;
25555+ return;
25556+ }
25557+ _sg_begin_pass(&pass_def, &atts_ptrs);
25558+}
25559+
25560+SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) {
25561+ SOKOL_ASSERT(_sg.valid);
25562+ _sg_stats_inc(num_apply_viewport);
25563+ _SG_TRACE_ARGS(apply_viewport, x, y, width, height, origin_top_left);
25564+ if (!_sg.cur_pass.valid) {
25565+ return;
25566+ }
25567+ #if defined(SOKOL_DEBUG)
25568+ if (!_sg_validate_apply_viewport(x, y, width, height, origin_top_left)) {
25569+ return;
25570+ }
25571+ #endif
25572+ _sg_apply_viewport(x, y, width, height, origin_top_left);
25573+}
25574+
25575+SOKOL_API_IMPL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) {
25576+ sg_apply_viewport((int)x, (int)y, (int)width, (int)height, origin_top_left);
25577+}
25578+
25579+SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) {
25580+ SOKOL_ASSERT(_sg.valid);
25581+ _sg_stats_inc(num_apply_scissor_rect);
25582+ _SG_TRACE_ARGS(apply_scissor_rect, x, y, width, height, origin_top_left);
25583+ if (!_sg.cur_pass.valid) {
25584+ return;
25585+ }
25586+ #if defined(SOKOL_DEBUG)
25587+ if (!_sg_validate_apply_scissor_rect(x, y, width, height, origin_top_left)) {
25588+ return;
25589+ }
25590+ #endif
25591+ _sg_apply_scissor_rect(x, y, width, height, origin_top_left);
25592+}
25593+
25594+SOKOL_API_IMPL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) {
25595+ sg_apply_scissor_rect((int)x, (int)y, (int)width, (int)height, origin_top_left);
25596+}
25597+
25598+SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) {
25599+ SOKOL_ASSERT(_sg.valid);
25600+ _sg_stats_inc(num_apply_pipeline);
25601+ _SG_TRACE_ARGS(apply_pipeline, pip_id);
25602+ if (!_sg.cur_pass.valid) {
25603+ return;
25604+ }
25605+ if (!_sg_validate_apply_pipeline(pip_id)) {
25606+ _sg.next_draw_valid = false;
25607+ return;
25608+ }
25609+ _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
25610+ SOKOL_ASSERT(pip);
25611+ _sg.cur_pip = _sg_pipeline_ref(pip);
25612+
25613+ _sg.next_draw_valid = (SG_RESOURCESTATE_VALID == pip->slot.state);
25614+ if (!_sg.next_draw_valid) {
25615+ return;
25616+ }
25617+ _sg.use_indexed_draw = pip->cmn.index_type != SG_INDEXTYPE_NONE;
25618+ _sg.use_instanced_draw = pip->cmn.use_instanced_draw;
25619+
25620+ _sg_apply_pipeline(pip);
25621+
25622+ // set the expected bindings and uniform block flags
25623+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&pip->cmn.shader);
25624+ _sg.required_bindings_and_uniforms = pip->cmn.required_bindings_and_uniforms | shd->cmn.required_bindings_and_uniforms;
25625+ _sg.applied_bindings_and_uniforms = 0;
25626+}
25627+
25628+SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) {
25629+ SOKOL_ASSERT(_sg.valid);
25630+ SOKOL_ASSERT(bindings);
25631+ _sg_stats_inc(num_apply_bindings);
25632+ _SG_TRACE_ARGS(apply_bindings, bindings);
25633+ if (!_sg.cur_pass.valid) {
25634+ return;
25635+ }
25636+ _sg.applied_bindings_and_uniforms |= (1 << SG_MAX_UNIFORMBLOCK_BINDSLOTS);
25637+ if (!_sg_validate_apply_bindings(bindings)) {
25638+ _sg.next_draw_valid = false;
25639+ }
25640+ SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0));
25641+ if (!_sg_pipeline_ref_alive(&_sg.cur_pip)) {
25642+ _sg.next_draw_valid = false;
25643+ }
25644+ if (!_sg.next_draw_valid) {
25645+ return;
25646+ }
25647+
25648+ _SG_STRUCT(_sg_bindings_ptrs_t, bnd);
25649+ bnd.pip = _sg_pipeline_ref_ptr(&_sg.cur_pip);
25650+ const _sg_shader_t* shd = _sg_shader_ref_ptr(&bnd.pip->cmn.shader);
25651+ if (!_sg.cur_pass.is_compute) {
25652+ for (size_t i = 0; i < SG_MAX_VERTEXBUFFER_BINDSLOTS; i++) {
25653+ if (bnd.pip->cmn.vertex_buffer_layout_active[i]) {
25654+ SOKOL_ASSERT(bindings->vertex_buffers[i].id != SG_INVALID_ID);
25655+ bnd.vbs[i] = _sg_lookup_buffer(bindings->vertex_buffers[i].id);
25656+ bnd.vb_offsets[i] = bindings->vertex_buffer_offsets[i];
25657+ _sg.next_draw_valid &= bnd.vbs[i] && (SG_RESOURCESTATE_VALID == bnd.vbs[i]->slot.state);
25658+ }
25659+ }
25660+ if (bindings->index_buffer.id) {
25661+ bnd.ib = _sg_lookup_buffer(bindings->index_buffer.id);
25662+ bnd.ib_offset = bindings->index_buffer_offset;
25663+ _sg.next_draw_valid &= bnd.ib && (SG_RESOURCESTATE_VALID == bnd.ib->slot.state);
25664+ }
25665+ }
25666+
25667+ for (int i = 0; i < SG_MAX_VIEW_BINDSLOTS; i++) {
25668+ if (shd->cmn.views[i].view_type != SG_VIEWTYPE_INVALID) {
25669+ SOKOL_ASSERT(bindings->views[i].id != SG_INVALID_ID);
25670+ bnd.views[i] = _sg_lookup_view(bindings->views[i].id);
25671+ if (bnd.views[i]) {
25672+ if (bnd.views[i]->cmn.type == SG_VIEWTYPE_STORAGEBUFFER) {
25673+ _sg.next_draw_valid &= _sg_buffer_ref_valid(&bnd.views[i]->cmn.buf.ref);
25674+ } else {
25675+ _sg.next_draw_valid &= _sg_image_ref_valid(&bnd.views[i]->cmn.img.ref);
25676+ }
25677+ } else {
25678+ _sg.next_draw_valid = false;
25679+ }
25680+ }
25681+ }
25682+
25683+ for (size_t i = 0; i < SG_MAX_SAMPLER_BINDSLOTS; i++) {
25684+ if (shd->cmn.samplers[i].stage != SG_SHADERSTAGE_NONE) {
25685+ SOKOL_ASSERT(bindings->samplers[i].id != SG_INVALID_ID);
25686+ bnd.smps[i] = _sg_lookup_sampler(bindings->samplers[i].id);
25687+ SOKOL_ASSERT(bnd.smps[i]);
25688+ }
25689+ }
25690+
25691+ if (_sg.next_draw_valid) {
25692+ _sg.next_draw_valid &= _sg_apply_bindings(&bnd);
25693+ }
25694+}
25695+
25696+SOKOL_API_IMPL void sg_apply_uniforms(int ub_slot, const sg_range* data) {
25697+ SOKOL_ASSERT(_sg.valid);
25698+ SOKOL_ASSERT((ub_slot >= 0) && (ub_slot < SG_MAX_UNIFORMBLOCK_BINDSLOTS));
25699+ SOKOL_ASSERT(data && data->ptr && (data->size > 0));
25700+ _sg_stats_inc(num_apply_uniforms);
25701+ _sg_stats_add(size_apply_uniforms, (uint32_t)data->size);
25702+ _SG_TRACE_ARGS(apply_uniforms, ub_slot, data);
25703+ if (!_sg.cur_pass.valid) {
25704+ return;
25705+ }
25706+ _sg.applied_bindings_and_uniforms |= 1 << ub_slot;
25707+ if (!_sg_validate_apply_uniforms(ub_slot, data)) {
25708+ _sg.next_draw_valid = false;
25709+ return;
25710+ }
25711+ if (!_sg.next_draw_valid) {
25712+ return;
25713+ }
25714+ _sg_apply_uniforms(ub_slot, data);
25715+}
25716+
25717+_SOKOL_PRIVATE bool _sg_check_skip_draw(int num_elements, int num_instances) {
25718+ if (!_sg.cur_pass.valid) {
25719+ return true;
25720+ }
25721+ if (!_sg.next_draw_valid) {
25722+ return true;
25723+ }
25724+ // skip no-op draws
25725+ if ((0 == num_elements) || (0 == num_instances)) {
25726+ return true;
25727+ }
25728+ return false;
25729+}
25730+
25731+SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instances) {
25732+ SOKOL_ASSERT(_sg.valid);
25733+ _sg_stats_inc(num_draw);
25734+ _SG_TRACE_ARGS(draw, base_element, num_elements, num_instances);
25735+ if (_sg_check_skip_draw(num_elements, num_instances)) {
25736+ return;
25737+ }
25738+ #if defined(SOKOL_DEBUG)
25739+ if (!_sg_validate_draw(base_element, num_elements, num_instances)) {
25740+ return;
25741+ }
25742+ #endif
25743+ _sg_draw(base_element, num_elements, num_instances, 0, 0);
25744+}
25745+
25746+SOKOL_API_IMPL void sg_draw_ex(int base_element, int num_elements, int num_instances, int base_vertex, int base_instance) {
25747+ SOKOL_ASSERT(_sg.valid);
25748+ _sg_stats_inc(num_draw_ex);
25749+ _SG_TRACE_ARGS(draw_ex, base_element, num_elements, num_instances, base_vertex, base_instance);
25750+ if (_sg_check_skip_draw(num_elements, num_instances)) {
25751+ return;
25752+ }
25753+ #if defined(SOKOL_DEBUG)
25754+ if (!_sg_validate_draw_ex(base_element, num_elements, num_instances, base_vertex, base_instance)) {
25755+ return;
25756+ }
25757+ #endif
25758+ _sg_draw(base_element, num_elements, num_instances, base_vertex, base_instance);
25759+}
25760+
25761+SOKOL_API_IMPL void sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) {
25762+ SOKOL_ASSERT(_sg.valid);
25763+ _sg_stats_inc(num_dispatch);
25764+ _SG_TRACE_ARGS(dispatch, num_groups_x, num_groups_y, num_groups_z);
25765+ if (!_sg.cur_pass.valid) {
25766+ return;
25767+ }
25768+ if (!_sg.next_draw_valid) {
25769+ return;
25770+ }
25771+ // skip no-op dispatches
25772+ if ((0 == num_groups_x) || (0 == num_groups_y) || (0 == num_groups_z)) {
25773+ return;
25774+ }
25775+ #if defined(SOKOL_DEBUG)
25776+ if (!_sg_validate_dispatch(num_groups_x, num_groups_y, num_groups_z)) {
25777+ return;
25778+ }
25779+ #endif
25780+ _sg_dispatch(num_groups_x, num_groups_y, num_groups_z);
25781+}
25782+
25783+SOKOL_API_IMPL void sg_end_pass(void) {
25784+ SOKOL_ASSERT(_sg.valid);
25785+ SOKOL_ASSERT(_sg.cur_pass.in_pass);
25786+ _sg_stats_inc(num_passes);
25787+ _SG_TRACE_NOARGS(end_pass);
25788+ // NOTE: don't exit early if !_sg.cur_pass.valid
25789+ if (!_sg.cur_pass.swapchain.invalid) {
25790+ const _sg_attachments_ptrs_t atts_ptrs = _sg_attachments_ptrs(&_sg.cur_pass.atts);
25791+ _sg_end_pass(&atts_ptrs);
25792+ }
25793+ _sg.cur_pip = _sg_pipeline_ref(0);
25794+ _sg_clear(&_sg.cur_pass, sizeof(_sg.cur_pass));
25795+}
25796+
25797+SOKOL_API_IMPL void sg_commit(void) {
25798+ SOKOL_ASSERT(_sg.valid);
25799+ SOKOL_ASSERT(!_sg.cur_pass.valid);
25800+ SOKOL_ASSERT(!_sg.cur_pass.in_pass);
25801+ _sg_commit();
25802+ _sg_update_stats();
25803+ _sg_notify_commit_listeners();
25804+ _SG_TRACE_NOARGS(commit);
25805+ _sg.frame_index++;
25806+}
25807+
25808+SOKOL_API_IMPL void sg_reset_state_cache(void) {
25809+ SOKOL_ASSERT(_sg.valid);
25810+ _sg_reset_state_cache();
25811+ _SG_TRACE_NOARGS(reset_state_cache);
25812+}
25813+
25814+SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const sg_range* data) {
25815+ SOKOL_ASSERT(_sg.valid);
25816+ SOKOL_ASSERT(data && data->ptr && (data->size > 0));
25817+ _sg_stats_inc(num_update_buffer);
25818+ _sg_stats_add(size_update_buffer, (uint32_t)data->size);
25819+ _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
25820+ if ((data->size > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) {
25821+ if (_sg_validate_update_buffer(buf, data)) {
25822+ SOKOL_ASSERT(data->size <= (size_t)buf->cmn.size);
25823+ // only one update allowed per buffer and frame
25824+ SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index);
25825+ // update and append on same buffer in same frame not allowed
25826+ SOKOL_ASSERT(buf->cmn.append_frame_index != _sg.frame_index);
25827+ _sg_update_buffer(buf, data);
25828+ buf->cmn.update_frame_index = _sg.frame_index;
25829+ }
25830+ }
25831+ _SG_TRACE_ARGS(update_buffer, buf_id, data);
25832+}
25833+
25834+SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) {
25835+ SOKOL_ASSERT(_sg.valid);
25836+ SOKOL_ASSERT(data && data->ptr);
25837+ _sg_stats_inc(num_append_buffer);
25838+ _sg_stats_add(size_append_buffer, (uint32_t)data->size);
25839+ _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
25840+ int result;
25841+ if (buf) {
25842+ // rewind append cursor in a new frame
25843+ if (buf->cmn.append_frame_index != _sg.frame_index) {
25844+ buf->cmn.append_pos = 0;
25845+ buf->cmn.append_overflow = false;
25846+ }
25847+ if (((size_t)buf->cmn.append_pos + data->size) > (size_t)buf->cmn.size) {
25848+ buf->cmn.append_overflow = true;
25849+ }
25850+ const int start_pos = buf->cmn.append_pos;
25851+ // NOTE: the multiple-of-4 requirement for the buffer offset is coming
25852+ // from WebGPU, but we want identical behaviour between backends
25853+ SOKOL_ASSERT(_sg_multiple_u64((uint64_t)start_pos, 4));
25854+ if (buf->slot.state == SG_RESOURCESTATE_VALID) {
25855+ if (_sg_validate_append_buffer(buf, data)) {
25856+ if (!buf->cmn.append_overflow && (data->size > 0)) {
25857+ // update and append on same buffer in same frame not allowed
25858+ SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index);
25859+ _sg_append_buffer(buf, data, buf->cmn.append_frame_index != _sg.frame_index);
25860+ buf->cmn.append_pos += (int) _sg_roundup_u64(data->size, 4);
25861+ buf->cmn.append_frame_index = _sg.frame_index;
25862+ }
25863+ }
25864+ }
25865+ result = start_pos;
25866+ } else {
25867+ // FIXME: should we return -1 here?
25868+ result = 0;
25869+ }
25870+ _SG_TRACE_ARGS(append_buffer, buf_id, data, result);
25871+ return result;
25872+}
25873+
25874+SOKOL_API_IMPL bool sg_query_buffer_overflow(sg_buffer buf_id) {
25875+ SOKOL_ASSERT(_sg.valid);
25876+ _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
25877+ bool result = buf ? buf->cmn.append_overflow : false;
25878+ return result;
25879+}
25880+
25881+SOKOL_API_IMPL bool sg_query_buffer_will_overflow(sg_buffer buf_id, size_t size) {
25882+ SOKOL_ASSERT(_sg.valid);
25883+ _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
25884+ bool result = false;
25885+ if (buf) {
25886+ int append_pos = buf->cmn.append_pos;
25887+ // rewind append cursor in a new frame
25888+ if (buf->cmn.append_frame_index != _sg.frame_index) {
25889+ append_pos = 0;
25890+ }
25891+ if ((append_pos + _sg_roundup((int)size, 4)) > buf->cmn.size) {
25892+ result = true;
25893+ }
25894+ }
25895+ return result;
25896+}
25897+
25898+SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_data* data) {
25899+ SOKOL_ASSERT(_sg.valid);
25900+ _sg_stats_inc(num_update_image);
25901+ for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) {
25902+ if (data->mip_levels[mip_index].size == 0) {
25903+ break;
25904+ }
25905+ _sg_stats_add(size_update_image, (uint32_t)data->mip_levels[mip_index].size);
25906+ }
25907+ _sg_image_t* img = _sg_lookup_image(img_id.id);
25908+ if (img && img->slot.state == SG_RESOURCESTATE_VALID) {
25909+ if (_sg_validate_update_image(img, data)) {
25910+ SOKOL_ASSERT(img->cmn.upd_frame_index != _sg.frame_index);
25911+ _sg_update_image(img, data);
25912+ img->cmn.upd_frame_index = _sg.frame_index;
25913+ }
25914+ }
25915+ _SG_TRACE_ARGS(update_image, img_id, data);
25916+}
25917+
25918+SOKOL_API_IMPL void sg_push_debug_group(const char* name) {
25919+ SOKOL_ASSERT(_sg.valid);
25920+ SOKOL_ASSERT(name);
25921+ _sg_push_debug_group(name);
25922+ _SG_TRACE_ARGS(push_debug_group, name);
25923+}
25924+
25925+SOKOL_API_IMPL void sg_pop_debug_group(void) {
25926+ SOKOL_ASSERT(_sg.valid);
25927+ _sg_pop_debug_group();
25928+ _SG_TRACE_NOARGS(pop_debug_group);
25929+}
25930+
25931+SOKOL_API_IMPL bool sg_add_commit_listener(sg_commit_listener listener) {
25932+ SOKOL_ASSERT(_sg.valid);
25933+ return _sg_add_commit_listener(&listener);
25934+}
25935+
25936+SOKOL_API_IMPL bool sg_remove_commit_listener(sg_commit_listener listener) {
25937+ SOKOL_ASSERT(_sg.valid);
25938+ return _sg_remove_commit_listener(&listener);
25939+}
25940+
25941+SOKOL_API_IMPL void sg_enable_stats(void) {
25942+ SOKOL_ASSERT(_sg.valid);
25943+ _sg.stats_enabled = true;
25944+}
25945+
25946+SOKOL_API_IMPL void sg_disable_stats(void) {
25947+ SOKOL_ASSERT(_sg.valid);
25948+ _sg.stats_enabled = false;
25949+}
25950+
25951+SOKOL_API_IMPL bool sg_stats_enabled(void) {
25952+ return _sg.stats_enabled;
25953+}
25954+
25955+SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) {
25956+ SOKOL_ASSERT(_sg.valid);
25957+ _SG_STRUCT(sg_buffer_info, info);
25958+ const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
25959+ if (buf) {
25960+ info.slot.state = buf->slot.state;
25961+ info.slot.res_id = buf->slot.id;
25962+ info.slot.uninit_count = buf->slot.uninit_count;
25963+ info.update_frame_index = buf->cmn.update_frame_index;
25964+ info.append_frame_index = buf->cmn.append_frame_index;
25965+ info.append_pos = buf->cmn.append_pos;
25966+ info.append_overflow = buf->cmn.append_overflow;
25967+ #if defined(SOKOL_D3D11)
25968+ info.num_slots = 1;
25969+ info.active_slot = 0;
25970+ #else
25971+ info.num_slots = buf->cmn.num_slots;
25972+ info.active_slot = buf->cmn.active_slot;
25973+ #endif
25974+ }
25975+ return info;
25976+}
25977+
25978+SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) {
25979+ SOKOL_ASSERT(_sg.valid);
25980+ _SG_STRUCT(sg_image_info, info);
25981+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
25982+ if (img) {
25983+ info.slot.state = img->slot.state;
25984+ info.slot.res_id = img->slot.id;
25985+ info.slot.uninit_count = img->slot.uninit_count;
25986+ info.upd_frame_index = img->cmn.upd_frame_index;
25987+ #if defined(SOKOL_D3D11)
25988+ info.num_slots = 1;
25989+ info.active_slot = 0;
25990+ #else
25991+ info.num_slots = img->cmn.num_slots;
25992+ info.active_slot = img->cmn.active_slot;
25993+ #endif
25994+ }
25995+ return info;
25996+}
25997+
25998+SOKOL_API_IMPL sg_sampler_info sg_query_sampler_info(sg_sampler smp_id) {
25999+ SOKOL_ASSERT(_sg.valid);
26000+ _SG_STRUCT(sg_sampler_info, info);
26001+ const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
26002+ if (smp) {
26003+ info.slot.state = smp->slot.state;
26004+ info.slot.res_id = smp->slot.id;
26005+ info.slot.uninit_count = smp->slot.uninit_count;
26006+ }
26007+ return info;
26008+}
26009+
26010+SOKOL_API_IMPL sg_shader_info sg_query_shader_info(sg_shader shd_id) {
26011+ SOKOL_ASSERT(_sg.valid);
26012+ _SG_STRUCT(sg_shader_info, info);
26013+ const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
26014+ if (shd) {
26015+ info.slot.state = shd->slot.state;
26016+ info.slot.res_id = shd->slot.id;
26017+ info.slot.uninit_count = shd->slot.uninit_count;
26018+ }
26019+ return info;
26020+}
26021+
26022+SOKOL_API_IMPL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip_id) {
26023+ SOKOL_ASSERT(_sg.valid);
26024+ _SG_STRUCT(sg_pipeline_info, info);
26025+ const _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
26026+ if (pip) {
26027+ info.slot.state = pip->slot.state;
26028+ info.slot.res_id = pip->slot.id;
26029+ info.slot.uninit_count = pip->slot.uninit_count;
26030+ }
26031+ return info;
26032+}
26033+
26034+SOKOL_API_IMPL sg_view_info sg_query_view_info(sg_view view_id) {
26035+ SOKOL_ASSERT(_sg.valid);
26036+ _SG_STRUCT(sg_view_info, info);
26037+ const _sg_view_t* view = _sg_lookup_view(view_id.id);
26038+ if (view) {
26039+ info.slot.state = view->slot.state;
26040+ info.slot.res_id = view->slot.id;
26041+ info.slot.uninit_count = view->slot.uninit_count;
26042+ }
26043+ return info;
26044+}
26045+
26046+SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf_id) {
26047+ SOKOL_ASSERT(_sg.valid);
26048+ _SG_STRUCT(sg_buffer_desc, desc);
26049+ const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
26050+ if (buf) {
26051+ desc.size = (size_t)buf->cmn.size;
26052+ desc.usage = buf->cmn.usage;
26053+ }
26054+ return desc;
26055+}
26056+
26057+SOKOL_API_IMPL size_t sg_query_buffer_size(sg_buffer buf_id) {
26058+ SOKOL_ASSERT(_sg.valid);
26059+ const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
26060+ if (buf) {
26061+ return (size_t)buf->cmn.size;
26062+ }
26063+ return 0;
26064+}
26065+
26066+SOKOL_API_IMPL sg_buffer_usage sg_query_buffer_usage(sg_buffer buf_id) {
26067+ SOKOL_ASSERT(_sg.valid);
26068+ _SG_STRUCT(sg_buffer_usage, usg);
26069+ const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
26070+ if (buf) {
26071+ usg = buf->cmn.usage;
26072+ }
26073+ return usg;
26074+}
26075+
26076+SOKOL_API_IMPL sg_image_desc sg_query_image_desc(sg_image img_id) {
26077+ SOKOL_ASSERT(_sg.valid);
26078+ _SG_STRUCT(sg_image_desc, desc);
26079+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26080+ if (img) {
26081+ desc.type = img->cmn.type;
26082+ desc.width = img->cmn.width;
26083+ desc.height = img->cmn.height;
26084+ desc.num_slices = img->cmn.num_slices;
26085+ desc.num_mipmaps = img->cmn.num_mipmaps;
26086+ desc.usage = img->cmn.usage;
26087+ desc.pixel_format = img->cmn.pixel_format;
26088+ desc.sample_count = img->cmn.sample_count;
26089+ }
26090+ return desc;
26091+}
26092+
26093+SOKOL_API_IMPL sg_image_type sg_query_image_type(sg_image img_id) {
26094+ SOKOL_ASSERT(_sg.valid);
26095+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26096+ if (img) {
26097+ return img->cmn.type;
26098+ }
26099+ return _SG_IMAGETYPE_DEFAULT;
26100+}
26101+
26102+SOKOL_API_IMPL int sg_query_image_width(sg_image img_id) {
26103+ SOKOL_ASSERT(_sg.valid);
26104+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26105+ if (img) {
26106+ return img->cmn.width;
26107+ }
26108+ return 0;
26109+}
26110+
26111+SOKOL_API_IMPL int sg_query_image_height(sg_image img_id) {
26112+ SOKOL_ASSERT(_sg.valid);
26113+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26114+ if (img) {
26115+ return img->cmn.height;
26116+ }
26117+ return 0;
26118+}
26119+
26120+SOKOL_API_IMPL int sg_query_image_num_slices(sg_image img_id) {
26121+ SOKOL_ASSERT(_sg.valid);
26122+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26123+ if (img) {
26124+ return img->cmn.num_slices;
26125+ }
26126+ return 0;
26127+}
26128+
26129+SOKOL_API_IMPL int sg_query_image_num_mipmaps(sg_image img_id) {
26130+ SOKOL_ASSERT(_sg.valid);
26131+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26132+ if (img) {
26133+ return img->cmn.num_mipmaps;
26134+ }
26135+ return 0;
26136+}
26137+
26138+SOKOL_API_IMPL sg_pixel_format sg_query_image_pixelformat(sg_image img_id) {
26139+ SOKOL_ASSERT(_sg.valid);
26140+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26141+ if (img) {
26142+ return img->cmn.pixel_format;
26143+ }
26144+ return _SG_PIXELFORMAT_DEFAULT;
26145+}
26146+
26147+SOKOL_API_IMPL sg_image_usage sg_query_image_usage(sg_image img_id) {
26148+ SOKOL_ASSERT(_sg.valid);
26149+ _SG_STRUCT(sg_image_usage, usg);
26150+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26151+ if (img) {
26152+ usg = img->cmn.usage;
26153+ }
26154+ return usg;
26155+}
26156+
26157+SOKOL_API_IMPL int sg_query_image_sample_count(sg_image img_id) {
26158+ SOKOL_ASSERT(_sg.valid);
26159+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26160+ if (img) {
26161+ return img->cmn.sample_count;
26162+ }
26163+ return 0;
26164+}
26165+
26166+SOKOL_API_IMPL sg_view_type sg_query_view_type(sg_view view_id) {
26167+ SOKOL_ASSERT(_sg.valid);
26168+ const _sg_view_t* view = _sg_lookup_view(view_id.id);
26169+ if (view) {
26170+ return view->cmn.type;
26171+ } else {
26172+ return SG_VIEWTYPE_INVALID;
26173+ }
26174+}
26175+
26176+// NOTE: may return SG_INVALID_ID if view invalid or view not an image view
26177+SOKOL_API_IMPL sg_image sg_query_view_image(sg_view view_id) {
26178+ SOKOL_ASSERT(_sg.valid);
26179+ _SG_STRUCT(sg_image, img);
26180+ const _sg_view_t* view = _sg_lookup_view(view_id.id);
26181+ if (view) {
26182+ img.id = view->cmn.img.ref.sref.id;
26183+ }
26184+ return img;
26185+}
26186+
26187+// NOTE: may return SG_INVALID_ID if view invalid or view not a buffer view
26188+SOKOL_API_IMPL sg_buffer sg_query_view_buffer(sg_view view_id) {
26189+ SOKOL_ASSERT(_sg.valid);
26190+ _SG_STRUCT(sg_buffer, buf);
26191+ const _sg_view_t* view = _sg_lookup_view(view_id.id);
26192+ if (view) {
26193+ buf.id = view->cmn.buf.ref.sref.id;
26194+ }
26195+ return buf;
26196+}
26197+
26198+SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_desc(sg_sampler smp_id) {
26199+ SOKOL_ASSERT(_sg.valid);
26200+ _SG_STRUCT(sg_sampler_desc, desc);
26201+ const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
26202+ if (smp) {
26203+ desc.min_filter = smp->cmn.min_filter;
26204+ desc.mag_filter = smp->cmn.mag_filter;
26205+ desc.mipmap_filter = smp->cmn.mipmap_filter;
26206+ desc.wrap_u = smp->cmn.wrap_u;
26207+ desc.wrap_v = smp->cmn.wrap_v;
26208+ desc.wrap_w = smp->cmn.wrap_w;
26209+ desc.min_lod = smp->cmn.min_lod;
26210+ desc.max_lod = smp->cmn.max_lod;
26211+ desc.border_color = smp->cmn.border_color;
26212+ desc.compare = smp->cmn.compare;
26213+ desc.max_anisotropy = smp->cmn.max_anisotropy;
26214+ }
26215+ return desc;
26216+}
26217+
26218+SOKOL_API_IMPL sg_shader_desc sg_query_shader_desc(sg_shader shd_id) {
26219+ SOKOL_ASSERT(_sg.valid);
26220+ _SG_STRUCT(sg_shader_desc, desc);
26221+ const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
26222+ if (shd) {
26223+ for (size_t ub_idx = 0; ub_idx < SG_MAX_UNIFORMBLOCK_BINDSLOTS; ub_idx++) {
26224+ sg_shader_uniform_block* ub_desc = &desc.uniform_blocks[ub_idx];
26225+ const _sg_shader_uniform_block_t* ub = &shd->cmn.uniform_blocks[ub_idx];
26226+ ub_desc->stage = ub->stage;
26227+ ub_desc->size = ub->size;
26228+ }
26229+ for (size_t view_idx = 0; view_idx < SG_MAX_VIEW_BINDSLOTS; view_idx++) {
26230+ const _sg_shader_view_t* view = &shd->cmn.views[view_idx];
26231+ if (view->view_type == SG_VIEWTYPE_TEXTURE) {
26232+ sg_shader_texture_view* tex_desc = &desc.views[view_idx].texture;
26233+ tex_desc->stage = view->stage;
26234+ tex_desc->image_type = view->image_type;
26235+ tex_desc->sample_type = view->sample_type;
26236+ tex_desc->multisampled = view->multisampled;
26237+ } else if (shd->cmn.views[view_idx].view_type == SG_VIEWTYPE_STORAGEBUFFER) {
26238+ sg_shader_storage_buffer_view* sbuf_desc = &desc.views[view_idx].storage_buffer;
26239+ sbuf_desc->stage = view->stage;
26240+ sbuf_desc->readonly = view->sbuf_readonly;
26241+ } else if (shd->cmn.views[view_idx].view_type == SG_VIEWTYPE_STORAGEIMAGE) {
26242+ sg_shader_storage_image_view* simg_desc = &desc.views[view_idx].storage_image;
26243+ simg_desc->stage = view->stage;
26244+ simg_desc->access_format = view->access_format;
26245+ simg_desc->image_type = view->image_type;
26246+ simg_desc->writeonly = view->simg_writeonly;
26247+ }
26248+ }
26249+ for (size_t smp_idx = 0; smp_idx < SG_MAX_SAMPLER_BINDSLOTS; smp_idx++) {
26250+ sg_shader_sampler* smp_desc = &desc.samplers[smp_idx];
26251+ const _sg_shader_sampler_t* smp = &shd->cmn.samplers[smp_idx];
26252+ smp_desc->stage = smp->stage;
26253+ smp_desc->sampler_type = smp->sampler_type;
26254+ }
26255+ for (size_t tex_smp_idx = 0; tex_smp_idx < SG_MAX_TEXTURE_SAMPLER_PAIRS; tex_smp_idx++) {
26256+ sg_shader_texture_sampler_pair* tex_smp_desc = &desc.texture_sampler_pairs[tex_smp_idx];
26257+ const _sg_shader_texture_sampler_t* tex_smp = &shd->cmn.texture_samplers[tex_smp_idx];
26258+ tex_smp_desc->stage = tex_smp->stage;
26259+ tex_smp_desc->view_slot = tex_smp->view_slot;
26260+ tex_smp_desc->sampler_slot = tex_smp->sampler_slot;
26261+ }
26262+ }
26263+ return desc;
26264+}
26265+
26266+SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip_id) {
26267+ SOKOL_ASSERT(_sg.valid);
26268+ _SG_STRUCT(sg_pipeline_desc, desc);
26269+ const _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
26270+ if (pip) {
26271+ desc.compute = pip->cmn.is_compute;
26272+ desc.shader.id = pip->cmn.shader.sref.id;
26273+ desc.layout = pip->cmn.layout;
26274+ desc.depth = pip->cmn.depth;
26275+ desc.stencil = pip->cmn.stencil;
26276+ desc.color_count = pip->cmn.color_count;
26277+ for (int i = 0; i < pip->cmn.color_count; i++) {
26278+ desc.colors[i] = pip->cmn.colors[i];
26279+ }
26280+ desc.primitive_type = pip->cmn.primitive_type;
26281+ desc.index_type = pip->cmn.index_type;
26282+ desc.cull_mode = pip->cmn.cull_mode;
26283+ desc.face_winding = pip->cmn.face_winding;
26284+ desc.sample_count = pip->cmn.sample_count;
26285+ desc.blend_color = pip->cmn.blend_color;
26286+ desc.alpha_to_coverage_enabled = pip->cmn.alpha_to_coverage_enabled;
26287+ }
26288+ return desc;
26289+}
26290+
26291+SOKOL_API_IMPL sg_view_desc sg_query_view_desc(sg_view view_id) {
26292+ SOKOL_ASSERT(_sg.valid);
26293+ _SG_STRUCT(sg_view_desc, desc);
26294+ const _sg_view_t* view = _sg_lookup_view(view_id.id);
26295+ if (view) {
26296+ switch (view->cmn.type) {
26297+ case SG_VIEWTYPE_STORAGEBUFFER:
26298+ desc.storage_buffer.buffer.id = view->cmn.buf.ref.sref.id;
26299+ desc.storage_buffer.offset = view->cmn.buf.offset;
26300+ break;
26301+ case SG_VIEWTYPE_STORAGEIMAGE:
26302+ desc.storage_image.image.id = view->cmn.img.ref.sref.id;
26303+ desc.storage_image.mip_level = view->cmn.img.mip_level;
26304+ desc.storage_image.slice = view->cmn.img.slice;
26305+ break;
26306+ case SG_VIEWTYPE_TEXTURE:
26307+ desc.texture.image.id = view->cmn.img.ref.sref.id;
26308+ desc.texture.mip_levels.base = view->cmn.img.mip_level;
26309+ desc.texture.mip_levels.count = view->cmn.img.mip_level_count;
26310+ desc.texture.slices.base = view->cmn.img.slice;
26311+ desc.texture.slices.count = view->cmn.img.slice_count;
26312+ break;
26313+ case SG_VIEWTYPE_COLORATTACHMENT:
26314+ desc.color_attachment.image.id = view->cmn.img.ref.sref.id;
26315+ desc.color_attachment.mip_level = view->cmn.img.mip_level;
26316+ desc.color_attachment.slice = view->cmn.img.slice;
26317+ break;
26318+ case SG_VIEWTYPE_RESOLVEATTACHMENT:
26319+ desc.resolve_attachment.image.id = view->cmn.img.ref.sref.id;
26320+ desc.resolve_attachment.mip_level = view->cmn.img.mip_level;
26321+ desc.resolve_attachment.slice = view->cmn.img.slice;
26322+ break;
26323+ case SG_VIEWTYPE_DEPTHSTENCILATTACHMENT:
26324+ desc.depth_stencil_attachment.image.id = view->cmn.img.ref.sref.id;
26325+ desc.depth_stencil_attachment.mip_level = view->cmn.img.mip_level;
26326+ desc.depth_stencil_attachment.slice = view->cmn.img.slice;
26327+ break;
26328+ default:
26329+ SOKOL_UNREACHABLE;
26330+ }
26331+ }
26332+ return desc;
26333+}
26334+
26335+SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) {
26336+ SOKOL_ASSERT(_sg.valid && desc);
26337+ return _sg_buffer_desc_defaults(desc);
26338+}
26339+
26340+SOKOL_API_IMPL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) {
26341+ SOKOL_ASSERT(_sg.valid && desc);
26342+ return _sg_image_desc_defaults(desc);
26343+}
26344+
26345+SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc) {
26346+ SOKOL_ASSERT(_sg.valid && desc);
26347+ return _sg_sampler_desc_defaults(desc);
26348+}
26349+
26350+SOKOL_API_IMPL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) {
26351+ SOKOL_ASSERT(_sg.valid && desc);
26352+ return _sg_shader_desc_defaults(desc);
26353+}
26354+
26355+SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) {
26356+ SOKOL_ASSERT(_sg.valid && desc);
26357+ return _sg_pipeline_desc_defaults(desc);
26358+}
26359+
26360+SOKOL_API_IMPL sg_view_desc sg_query_view_defaults(const sg_view_desc* desc) {
26361+ SOKOL_ASSERT(_sg.valid && desc);
26362+ return _sg_view_desc_defaults(desc);
26363+}
26364+
26365+SOKOL_API_IMPL const void* sg_d3d11_device(void) {
26366+ #if defined(SOKOL_D3D11)
26367+ return (const void*) _sg.d3d11.dev;
26368+ #else
26369+ return 0;
26370+ #endif
26371+}
26372+
26373+SOKOL_API_IMPL const void* sg_d3d11_device_context(void) {
26374+ #if defined(SOKOL_D3D11)
26375+ return (const void*) _sg.d3d11.ctx;
26376+ #else
26377+ return 0;
26378+ #endif
26379+}
26380+
26381+SOKOL_API_IMPL sg_d3d11_buffer_info sg_d3d11_query_buffer_info(sg_buffer buf_id) {
26382+ SOKOL_ASSERT(_sg.valid);
26383+ _SG_STRUCT(sg_d3d11_buffer_info, res);
26384+ #if defined(SOKOL_D3D11)
26385+ const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
26386+ if (buf) {
26387+ res.buf = (const void*) buf->d3d11.buf;
26388+ }
26389+ #else
26390+ _SOKOL_UNUSED(buf_id);
26391+ #endif
26392+ return res;
26393+}
26394+
26395+SOKOL_API_IMPL sg_d3d11_image_info sg_d3d11_query_image_info(sg_image img_id) {
26396+ SOKOL_ASSERT(_sg.valid);
26397+ _SG_STRUCT(sg_d3d11_image_info, res);
26398+ #if defined(SOKOL_D3D11)
26399+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26400+ if (img) {
26401+ res.tex2d = (const void*) img->d3d11.tex2d;
26402+ res.tex3d = (const void*) img->d3d11.tex3d;
26403+ res.res = (const void*) img->d3d11.res;
26404+ }
26405+ #else
26406+ _SOKOL_UNUSED(img_id);
26407+ #endif
26408+ return res;
26409+}
26410+
26411+SOKOL_API_IMPL sg_d3d11_sampler_info sg_d3d11_query_sampler_info(sg_sampler smp_id) {
26412+ SOKOL_ASSERT(_sg.valid);
26413+ _SG_STRUCT(sg_d3d11_sampler_info, res);
26414+ #if defined(SOKOL_D3D11)
26415+ const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
26416+ if (smp) {
26417+ res.smp = (const void*) smp->d3d11.smp;
26418+ }
26419+ #else
26420+ _SOKOL_UNUSED(smp_id);
26421+ #endif
26422+ return res;
26423+}
26424+
26425+SOKOL_API_IMPL sg_d3d11_shader_info sg_d3d11_query_shader_info(sg_shader shd_id) {
26426+ SOKOL_ASSERT(_sg.valid);
26427+ _SG_STRUCT(sg_d3d11_shader_info, res);
26428+ #if defined(SOKOL_D3D11)
26429+ const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
26430+ if (shd) {
26431+ for (size_t i = 0; i < SG_MAX_UNIFORMBLOCK_BINDSLOTS; i++) {
26432+ res.cbufs[i] = (const void*) shd->d3d11.all_cbufs[i];
26433+ }
26434+ res.vs = (const void*) shd->d3d11.vs;
26435+ res.fs = (const void*) shd->d3d11.fs;
26436+ }
26437+ #else
26438+ _SOKOL_UNUSED(shd_id);
26439+ #endif
26440+ return res;
26441+}
26442+
26443+SOKOL_API_IMPL sg_d3d11_pipeline_info sg_d3d11_query_pipeline_info(sg_pipeline pip_id) {
26444+ SOKOL_ASSERT(_sg.valid);
26445+ _SG_STRUCT(sg_d3d11_pipeline_info, res);
26446+ #if defined(SOKOL_D3D11)
26447+ const _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
26448+ if (pip) {
26449+ res.il = (const void*) pip->d3d11.il;
26450+ res.rs = (const void*) pip->d3d11.rs;
26451+ res.dss = (const void*) pip->d3d11.dss;
26452+ res.bs = (const void*) pip->d3d11.bs;
26453+ }
26454+ #else
26455+ _SOKOL_UNUSED(pip_id);
26456+ #endif
26457+ return res;
26458+}
26459+
26460+SOKOL_API_IMPL sg_d3d11_view_info sg_d3d11_query_view_info(sg_view view_id) {
26461+ SOKOL_ASSERT(_sg.valid);
26462+ _SG_STRUCT(sg_d3d11_view_info, res);
26463+ #if defined(SOKOL_D3D11)
26464+ const _sg_view_t* view = _sg_lookup_view(view_id.id);
26465+ res.srv = (const void*) view->d3d11.srv;
26466+ res.uav = (const void*) view->d3d11.uav;
26467+ res.rtv = (const void*) view->d3d11.rtv;
26468+ res.dsv = (const void*) view->d3d11.dsv;
26469+ #else
26470+ _SOKOL_UNUSED(view_id);
26471+ #endif
26472+ return res;
26473+}
26474+
26475+SOKOL_API_IMPL const void* sg_mtl_device(void) {
26476+ #if defined(SOKOL_METAL)
26477+ if (nil != _sg.mtl.device) {
26478+ return (__bridge const void*) _sg.mtl.device;
26479+ } else {
26480+ return 0;
26481+ }
26482+ #else
26483+ return 0;
26484+ #endif
26485+}
26486+
26487+SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) {
26488+ #if defined(SOKOL_METAL)
26489+ if (nil != _sg.mtl.render_cmd_encoder) {
26490+ return (__bridge const void*) _sg.mtl.render_cmd_encoder;
26491+ } else {
26492+ return 0;
26493+ }
26494+ #else
26495+ return 0;
26496+ #endif
26497+}
26498+
26499+SOKOL_API_IMPL const void* sg_mtl_compute_command_encoder(void) {
26500+ #if defined(SOKOL_METAL)
26501+ if (nil != _sg.mtl.compute_cmd_encoder) {
26502+ return (__bridge const void*) _sg.mtl.compute_cmd_encoder;
26503+ } else {
26504+ return 0;
26505+ }
26506+ #else
26507+ return 0;
26508+ #endif
26509+}
26510+
26511+SOKOL_API_IMPL const void* sg_mtl_command_queue(void) {
26512+ #if defined(SOKOL_METAL)
26513+ if (nil != _sg.mtl.cmd_queue) {
26514+ return (__bridge const void*) _sg.mtl.cmd_queue;
26515+ } else {
26516+ return 0;
26517+ }
26518+ #else
26519+ return 0;
26520+ #endif
26521+}
26522+
26523+SOKOL_API_IMPL sg_mtl_buffer_info sg_mtl_query_buffer_info(sg_buffer buf_id) {
26524+ SOKOL_ASSERT(_sg.valid);
26525+ _SG_STRUCT(sg_mtl_buffer_info, res);
26526+ #if defined(SOKOL_METAL)
26527+ const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
26528+ if (buf) {
26529+ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
26530+ if (buf->mtl.buf[i] != 0) {
26531+ res.buf[i] = (__bridge void*) _sg_mtl_id(buf->mtl.buf[i]);
26532+ }
26533+ }
26534+ res.active_slot = buf->cmn.active_slot;
26535+ }
26536+ #else
26537+ _SOKOL_UNUSED(buf_id);
26538+ #endif
26539+ return res;
26540+}
26541+
26542+SOKOL_API_IMPL sg_mtl_image_info sg_mtl_query_image_info(sg_image img_id) {
26543+ SOKOL_ASSERT(_sg.valid);
26544+ _SG_STRUCT(sg_mtl_image_info, res);
26545+ #if defined(SOKOL_METAL)
26546+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26547+ if (img) {
26548+ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
26549+ if (img->mtl.tex[i] != 0) {
26550+ res.tex[i] = (__bridge void*) _sg_mtl_id(img->mtl.tex[i]);
26551+ }
26552+ }
26553+ res.active_slot = img->cmn.active_slot;
26554+ }
26555+ #else
26556+ _SOKOL_UNUSED(img_id);
26557+ #endif
26558+ return res;
26559+}
26560+
26561+SOKOL_API_IMPL sg_mtl_sampler_info sg_mtl_query_sampler_info(sg_sampler smp_id) {
26562+ SOKOL_ASSERT(_sg.valid);
26563+ _SG_STRUCT(sg_mtl_sampler_info, res);
26564+ #if defined(SOKOL_METAL)
26565+ const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
26566+ if (smp) {
26567+ if (smp->mtl.sampler_state != 0) {
26568+ res.smp = (__bridge void*) _sg_mtl_id(smp->mtl.sampler_state);
26569+ }
26570+ }
26571+ #else
26572+ _SOKOL_UNUSED(smp_id);
26573+ #endif
26574+ return res;
26575+}
26576+
26577+SOKOL_API_IMPL sg_mtl_shader_info sg_mtl_query_shader_info(sg_shader shd_id) {
26578+ SOKOL_ASSERT(_sg.valid);
26579+ _SG_STRUCT(sg_mtl_shader_info, res);
26580+ #if defined(SOKOL_METAL)
26581+ const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
26582+ if (shd) {
26583+ const int vertex_lib = shd->mtl.vertex_func.mtl_lib;
26584+ const int vertex_func = shd->mtl.vertex_func.mtl_func;
26585+ const int fragment_lib = shd->mtl.fragment_func.mtl_lib;
26586+ const int fragment_func = shd->mtl.fragment_func.mtl_func;
26587+ if (vertex_lib != 0) {
26588+ res.vertex_lib = (__bridge void*) _sg_mtl_id(vertex_lib);
26589+ }
26590+ if (fragment_lib != 0) {
26591+ res.fragment_lib = (__bridge void*) _sg_mtl_id(fragment_lib);
26592+ }
26593+ if (vertex_func != 0) {
26594+ res.vertex_func = (__bridge void*) _sg_mtl_id(vertex_func);
26595+ }
26596+ if (fragment_func != 0) {
26597+ res.fragment_func = (__bridge void*) _sg_mtl_id(fragment_func);
26598+ }
26599+ }
26600+ #else
26601+ _SOKOL_UNUSED(shd_id);
26602+ #endif
26603+ return res;
26604+}
26605+
26606+SOKOL_API_IMPL sg_mtl_pipeline_info sg_mtl_query_pipeline_info(sg_pipeline pip_id) {
26607+ SOKOL_ASSERT(_sg.valid);
26608+ _SG_STRUCT(sg_mtl_pipeline_info, res);
26609+ #if defined(SOKOL_METAL)
26610+ const _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
26611+ if (pip) {
26612+ if (pip->mtl.rps != 0) {
26613+ res.rps = (__bridge void*) _sg_mtl_id(pip->mtl.rps);
26614+ }
26615+ if (pip->mtl.dss != 0) {
26616+ res.dss = (__bridge void*) _sg_mtl_id(pip->mtl.dss);
26617+ }
26618+ }
26619+ #else
26620+ _SOKOL_UNUSED(pip_id);
26621+ #endif
26622+ return res;
26623+}
26624+
26625+SOKOL_API_IMPL const void* sg_wgpu_device(void) {
26626+ #if defined(SOKOL_WGPU)
26627+ return (const void*) _sg.wgpu.dev;
26628+ #else
26629+ return 0;
26630+ #endif
26631+}
26632+
26633+SOKOL_API_IMPL const void* sg_wgpu_queue(void) {
26634+ #if defined(SOKOL_WGPU)
26635+ return (const void*) _sg.wgpu.queue;
26636+ #else
26637+ return 0;
26638+ #endif
26639+}
26640+
26641+SOKOL_API_IMPL const void* sg_wgpu_command_encoder(void) {
26642+ #if defined(SOKOL_WGPU)
26643+ return (const void*) _sg.wgpu.cmd_enc;
26644+ #else
26645+ return 0;
26646+ #endif
26647+}
26648+
26649+SOKOL_API_IMPL const void* sg_wgpu_render_pass_encoder(void) {
26650+ #if defined(SOKOL_WGPU)
26651+ return (const void*) _sg.wgpu.rpass_enc;
26652+ #else
26653+ return 0;
26654+ #endif
26655+}
26656+
26657+SOKOL_API_IMPL const void* sg_wgpu_compute_pass_encoder(void) {
26658+ #if defined(SOKOL_WGPU)
26659+ return (const void*) _sg.wgpu.cpass_enc;
26660+ #else
26661+ return 0;
26662+ #endif
26663+}
26664+
26665+SOKOL_API_IMPL sg_wgpu_buffer_info sg_wgpu_query_buffer_info(sg_buffer buf_id) {
26666+ SOKOL_ASSERT(_sg.valid);
26667+ _SG_STRUCT(sg_wgpu_buffer_info, res);
26668+ #if defined(SOKOL_WGPU)
26669+ const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
26670+ if (buf) {
26671+ res.buf = (const void*) buf->wgpu.buf;
26672+ }
26673+ #else
26674+ _SOKOL_UNUSED(buf_id);
26675+ #endif
26676+ return res;
26677+}
26678+
26679+SOKOL_API_IMPL sg_wgpu_image_info sg_wgpu_query_image_info(sg_image img_id) {
26680+ SOKOL_ASSERT(_sg.valid);
26681+ _SG_STRUCT(sg_wgpu_image_info, res);
26682+ #if defined(SOKOL_WGPU)
26683+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26684+ if (img) {
26685+ res.tex = (const void*) img->wgpu.tex;
26686+ }
26687+ #else
26688+ _SOKOL_UNUSED(img_id);
26689+ #endif
26690+ return res;
26691+}
26692+
26693+SOKOL_API_IMPL sg_wgpu_sampler_info sg_wgpu_query_sampler_info(sg_sampler smp_id) {
26694+ SOKOL_ASSERT(_sg.valid);
26695+ _SG_STRUCT(sg_wgpu_sampler_info, res);
26696+ #if defined(SOKOL_WGPU)
26697+ const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
26698+ if (smp) {
26699+ res.smp = (const void*) smp->wgpu.smp;
26700+ }
26701+ #else
26702+ _SOKOL_UNUSED(smp_id);
26703+ #endif
26704+ return res;
26705+}
26706+
26707+SOKOL_API_IMPL sg_wgpu_shader_info sg_wgpu_query_shader_info(sg_shader shd_id) {
26708+ SOKOL_ASSERT(_sg.valid);
26709+ _SG_STRUCT(sg_wgpu_shader_info, res);
26710+ #if defined(SOKOL_WGPU)
26711+ const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
26712+ if (shd) {
26713+ res.vs_mod = (const void*) shd->wgpu.vertex_func.module;
26714+ res.fs_mod = (const void*) shd->wgpu.fragment_func.module;
26715+ res.bgl = (const void*) shd->wgpu.bgl_view_smp;
26716+ }
26717+ #else
26718+ _SOKOL_UNUSED(shd_id);
26719+ #endif
26720+ return res;
26721+}
26722+
26723+SOKOL_API_IMPL sg_wgpu_pipeline_info sg_wgpu_query_pipeline_info(sg_pipeline pip_id) {
26724+ SOKOL_ASSERT(_sg.valid);
26725+ _SG_STRUCT(sg_wgpu_pipeline_info, res);
26726+ #if defined(SOKOL_WGPU)
26727+ const _sg_pipeline_t* pip = _sg_lookup_pipeline(pip_id.id);
26728+ if (pip) {
26729+ res.render_pipeline = (const void*) pip->wgpu.rpip;
26730+ res.compute_pipeline = (const void*) pip->wgpu.cpip;
26731+ }
26732+ #else
26733+ _SOKOL_UNUSED(pip_id);
26734+ #endif
26735+ return res;
26736+}
26737+
26738+SOKOL_API_IMPL sg_wgpu_view_info sg_wgpu_query_view_info(sg_view view_id) {
26739+ SOKOL_ASSERT(_sg.valid);
26740+ _SG_STRUCT(sg_wgpu_view_info, res);
26741+ #if defined(SOKOL_WGPU)
26742+ const _sg_view_t* view = _sg_lookup_view(view_id.id);
26743+ if (view) {
26744+ res.view = (const void*) view->wgpu.view;
26745+ }
26746+ #else
26747+ _SOKOL_UNUSED(view_id);
26748+ #endif
26749+ return res;
26750+}
26751+
26752+SOKOL_API_IMPL sg_gl_buffer_info sg_gl_query_buffer_info(sg_buffer buf_id) {
26753+ SOKOL_ASSERT(_sg.valid);
26754+ _SG_STRUCT(sg_gl_buffer_info, res);
26755+ #if defined(_SOKOL_ANY_GL)
26756+ const _sg_buffer_t* buf = _sg_lookup_buffer(buf_id.id);
26757+ if (buf) {
26758+ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
26759+ res.buf[i] = buf->gl.buf[i];
26760+ }
26761+ res.active_slot = buf->cmn.active_slot;
26762+ }
26763+ #else
26764+ _SOKOL_UNUSED(buf_id);
26765+ #endif
26766+ return res;
26767+}
26768+
26769+SOKOL_API_IMPL sg_gl_image_info sg_gl_query_image_info(sg_image img_id) {
26770+ SOKOL_ASSERT(_sg.valid);
26771+ _SG_STRUCT(sg_gl_image_info, res);
26772+ #if defined(_SOKOL_ANY_GL)
26773+ const _sg_image_t* img = _sg_lookup_image(img_id.id);
26774+ if (img) {
26775+ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
26776+ res.tex[i] = img->gl.tex[i];
26777+ }
26778+ res.tex_target = img->gl.target;
26779+ res.active_slot = img->cmn.active_slot;
26780+ }
26781+ #else
26782+ _SOKOL_UNUSED(img_id);
26783+ #endif
26784+ return res;
26785+}
26786+
26787+SOKOL_API_IMPL sg_gl_sampler_info sg_gl_query_sampler_info(sg_sampler smp_id) {
26788+ SOKOL_ASSERT(_sg.valid);
26789+ _SG_STRUCT(sg_gl_sampler_info, res);
26790+ #if defined(_SOKOL_ANY_GL)
26791+ const _sg_sampler_t* smp = _sg_lookup_sampler(smp_id.id);
26792+ if (smp) {
26793+ res.smp = smp->gl.smp;
26794+ }
26795+ #else
26796+ _SOKOL_UNUSED(smp_id);
26797+ #endif
26798+ return res;
26799+}
26800+
26801+SOKOL_API_IMPL sg_gl_shader_info sg_gl_query_shader_info(sg_shader shd_id) {
26802+ SOKOL_ASSERT(_sg.valid);
26803+ _SG_STRUCT(sg_gl_shader_info, res);
26804+ #if defined(_SOKOL_ANY_GL)
26805+ const _sg_shader_t* shd = _sg_lookup_shader(shd_id.id);
26806+ if (shd) {
26807+ res.prog = shd->gl.prog;
26808+ }
26809+ #else
26810+ _SOKOL_UNUSED(shd_id);
26811+ #endif
26812+ return res;
26813+}
26814+
26815+SOKOL_API_IMPL sg_gl_view_info sg_gl_query_view_info(sg_view view_id) {
26816+ SOKOL_ASSERT(_sg.valid);
26817+ _SG_STRUCT(sg_gl_view_info, res);
26818+ #if defined(_SOKOL_ANY_GL)
26819+ const _sg_view_t* view = _sg_lookup_view(view_id.id);
26820+ if (view) {
26821+ for (size_t i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) {
26822+ res.tex_view[i] = view->gl.tex_view[i];
26823+ }
26824+ res.msaa_render_buffer = view->gl.msaa_render_buffer;
26825+ res.msaa_resolve_frame_buffer = view->gl.msaa_resolve_frame_buffer;
26826+ }
26827+ #else
26828+ _SOKOL_UNUSED(view_id);
26829+ #endif
26830+ return res;
26831+}
26832+
26833+#ifdef _MSC_VER
26834+#pragma warning(pop)
26835+#endif
26836+
26837+#endif // SOKOL_GFX_IMPL