// machine generated, do not edit

/*

    sokol_gfx.h -- simple 3D API wrapper

    Project URL: https://github.com/floooh/sokol

    Example code: https://github.com/floooh/sokol-samples

    Do this:
        #define SOKOL_IMPL or
        #define SOKOL_GFX_IMPL
    before you include this file in *one* C or C++ file to create the
    implementation.

    In the same place define one of the following to select the rendering
    backend:
        #define SOKOL_GLCORE
        #define SOKOL_GLES3
        #define SOKOL_D3D11
        #define SOKOL_METAL
        #define SOKOL_WGPU
        #define SOKOL_DUMMY_BACKEND

    I.e. for the desktop GL it should look like this:

    #include ...
    #include ...
    #define SOKOL_IMPL
    #define SOKOL_GLCORE
    #include "sokol_gfx.h"

    The dummy backend replaces the platform-specific backend code with empty
    stub functions. This is useful for writing tests that need to run on the
    command line.

    Optionally provide the following defines with your own implementations:

    SOKOL_ASSERT(c)             - your own assert macro (default: assert(c))
    SOKOL_UNREACHABLE()         - a guard macro for unreachable code (default: assert(false))
    SOKOL_GFX_API_DECL          - public function declaration prefix (default: extern)
    SOKOL_API_DECL              - same as SOKOL_GFX_API_DECL
    SOKOL_API_IMPL              - public function implementation prefix (default: -)
    SOKOL_TRACE_HOOKS           - enable trace hook callbacks (search below for TRACE HOOKS)
    SOKOL_EXTERNAL_GL_LOADER    - indicates that you're using your own GL loader, in this case
                                  sokol_gfx.h will not include any platform GL headers and disable
                                  the integrated Win32 GL loader

    If sokol_gfx.h is compiled as a DLL, define the following before
    including the declaration or implementation:

    SOKOL_DLL

    On Windows, SOKOL_DLL will define SOKOL_GFX_API_DECL as __declspec(dllexport)
    or __declspec(dllimport) as needed.

    If you want to compile without deprecated structs and functions,
    define:

    SOKOL_NO_DEPRECATED

    Optionally define the following to force debug checks and validations
    even in release mode:

    SOKOL_DEBUG - by default this is defined if _DEBUG is defined

    sokol_gfx DOES NOT:
    ===================
    - create a window, swapchain or the 3D-API context/device, you must do this
      before sokol_gfx is initialized, and pass any required information
      (like 3D device pointers) to the sokol_gfx initialization call

    - present the rendered frame, how this is done exactly usually depends
      on how the window and 3D-API context/device was created

    - provide a unified shader language, instead 3D-API-specific shader
      source-code or shader-bytecode must be provided (for the "official"
      offline shader cross-compiler / code-generator, see here:
      https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md)


    STEP BY STEP
    ============
    --- to initialize sokol_gfx, after creating a window and a 3D-API
        context/device, call:

            sg_setup(const sg_desc*)

        Depending on the selected 3D backend, sokol-gfx requires some
        information about its runtime environment, like a GPU device pointer,
        default swapchain pixel formats and so on. If you are using sokol_app.h
        for the window system glue, you can use a helper function provided in
        the sokol_glue.h header:

            #include "sokol_gfx.h"
            #include "sokol_app.h"
            #include "sokol_glue.h"
            //...
            sg_setup(&(sg_desc){
                .environment = sglue_environment(),
            });

        To get any logging output for errors and from the validation layer, you
        need to provide a logging callback. Easiest way is through sokol_log.h:

            #include "sokol_log.h"
            //...
            sg_setup(&(sg_desc){
                //...
                .logger.func = slog_func,
            });

    --- create resource objects (at least buffers, shaders and pipelines,
        and optionally images, samplers and render-pass-attachments):

            sg_buffer sg_make_buffer(const sg_buffer_desc*)
            sg_image sg_make_image(const sg_image_desc*)
            sg_sampler sg_make_sampler(const sg_sampler_desc*)
            sg_shader sg_make_shader(const sg_shader_desc*)
            sg_pipeline sg_make_pipeline(const sg_pipeline_desc*)
            sg_attachments sg_make_attachments(const sg_attachments_desc*)

    --- start a render- or compute-pass:

            sg_begin_pass(const sg_pass* pass);

        Typically, render passes render into an externally provided swapchain which
        presents the rendering result on the display. Such a 'swapchain pass'
        is started like this:

            sg_begin_pass(&(sg_pass){ .action = { ... }, .swapchain = sglue_swapchain() })

        ...where .action is an sg_pass_action struct containing actions to be performed
        at the start and end of a render pass (such as clearing the render surfaces to
        a specific color), and .swapchain is an sg_swapchain struct with all the required
        information to render into the swapchain's surfaces.

        To start an 'offscreen render pass' into sokol-gfx image objects, an sg_attachment
        object handle is required instead of an sg_swapchain struct. An offscreen
        pass is started like this (assuming attachments is an sg_attachments handle):

            sg_begin_pass(&(sg_pass){ .action = { ... }, .attachments = attachments });

        To start a compute-pass, just set the .compute item to true:

            sg_begin_pass(&(sg_pass){ .compute = true });

    --- set the pipeline state for the next draw call with:

            sg_apply_pipeline(sg_pipeline pip)

    --- fill an sg_bindings struct with the resource bindings for the next
        draw- or dispatch-call (0..N vertex buffers, 0 or 1 index buffer, 0..N images,
        samplers and storage-buffers), and call:

            sg_apply_bindings(const sg_bindings* bindings)

        to update the resource bindings. Note that in a compute pass, no vertex-
        or index-buffer bindings are allowed and will be rejected by the validation
        layer.

    --- optionally update shader uniform data with:

            sg_apply_uniforms(int ub_slot, const sg_range* data)

        Read the section 'UNIFORM DATA LAYOUT' to learn about the expected memory layout
        of the uniform data passed into sg_apply_uniforms().

    --- kick off a draw call with:

            sg_draw(int base_element, int num_elements, int num_instances)

        The sg_draw() function unifies all the different ways to render primitives
        in a single call (indexed vs non-indexed rendering, and instanced vs non-instanced
        rendering). In case of indexed rendering, base_element and num_element specify
        indices in the currently bound index buffer. In case of non-indexed rendering
        base_element and num_elements specify vertices in the currently bound
        vertex-buffer(s). To perform instanced rendering, the rendering pipeline
        must be setup for instancing (see sg_pipeline_desc below), a separate vertex buffer
        containing per-instance data must be bound, and the num_instances parameter
        must be > 1.

    --- ...or kick of a dispatch call to invoke a compute shader workload:

            sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z)

        The dispatch args define the number of 'compute workgroups' processed
        by the currently applied compute shader.

    --- finish the current pass with:

            sg_end_pass()

    --- when done with the current frame, call

            sg_commit()

    --- at the end of your program, shutdown sokol_gfx with:

            sg_shutdown()

    --- if you need to destroy resources before sg_shutdown(), call:

            sg_destroy_buffer(sg_buffer buf)
            sg_destroy_image(sg_image img)
            sg_destroy_sampler(sg_sampler smp)
            sg_destroy_shader(sg_shader shd)
            sg_destroy_pipeline(sg_pipeline pip)
            sg_destroy_attachments(sg_attachments atts)

    --- to set a new viewport rectangle, call:

            sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left)

        ...or if you want to specify the viewport rectangle with float values:

            sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left)

    --- to set a new scissor rect, call:

            sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left)

        ...or with float values:

            sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left)

        Both sg_apply_viewport() and sg_apply_scissor_rect() must be called
        inside a rendering pass (e.g. not in a compute pass, or outside a pass)

        Note that sg_begin_default_pass() and sg_begin_pass() will reset both the
        viewport and scissor rectangles to cover the entire framebuffer.

    --- to update (overwrite) the content of buffer and image resources, call:

            sg_update_buffer(sg_buffer buf, const sg_range* data)
            sg_update_image(sg_image img, const sg_image_data* data)

        Buffers and images to be updated must have been created with
        SG_USAGE_DYNAMIC or SG_USAGE_STREAM.

        Only one update per frame is allowed for buffer and image resources when
        using the sg_update_*() functions. The rationale is to have a simple
        protection from the CPU scribbling over data the GPU is currently
        using, or the CPU having to wait for the GPU

        Buffer and image updates can be partial, as long as a rendering
        operation only references the valid (updated) data in the
        buffer or image.

    --- to append a chunk of data to a buffer resource, call:

            int sg_append_buffer(sg_buffer buf, const sg_range* data)

        The difference to sg_update_buffer() is that sg_append_buffer()
        can be called multiple times per frame to append new data to the
        buffer piece by piece, optionally interleaved with draw calls referencing
        the previously written data.

        sg_append_buffer() returns a byte offset to the start of the
        written data, this offset can be assigned to
        sg_bindings.vertex_buffer_offsets[n] or
        sg_bindings.index_buffer_offset

        Code example:

        for (...) {
            const void* data = ...;
            const int num_bytes = ...;
            int offset = sg_append_buffer(buf, &(sg_range) { .ptr=data, .size=num_bytes });
            bindings.vertex_buffer_offsets[0] = offset;
            sg_apply_pipeline(pip);
            sg_apply_bindings(&bindings);
            sg_apply_uniforms(...);
            sg_draw(...);
        }

        A buffer to be used with sg_append_buffer() must have been created
        with SG_USAGE_DYNAMIC or SG_USAGE_STREAM.

        If the application appends more data to the buffer then fits into
        the buffer, the buffer will go into the "overflow" state for the
        rest of the frame.

        Any draw calls attempting to render an overflown buffer will be
        silently dropped (in debug mode this will also result in a
        validation error).

        You can also check manually if a buffer is in overflow-state by calling

            bool sg_query_buffer_overflow(sg_buffer buf)

        You can manually check to see if an overflow would occur before adding
        any data to a buffer by calling

            bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size)

        NOTE: Due to restrictions in underlying 3D-APIs, appended chunks of
        data will be 4-byte aligned in the destination buffer. This means
        that there will be gaps in index buffers containing 16-bit indices
        when the number of indices in a call to sg_append_buffer() is
        odd. This isn't a problem when each call to sg_append_buffer()
        is associated with one draw call, but will be problematic when
        a single indexed draw call spans several appended chunks of indices.

    --- to check at runtime for optional features, limits and pixelformat support,
        call:

            sg_features sg_query_features()
            sg_limits sg_query_limits()
            sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt)

    --- if you need to call into the underlying 3D-API directly, you must call:

            sg_reset_state_cache()

        ...before calling sokol_gfx functions again

    --- you can inspect the original sg_desc structure handed to sg_setup()
        by calling sg_query_desc(). This will return an sg_desc struct with
        the default values patched in instead of any zero-initialized values

    --- you can get a desc struct matching the creation attributes of a
        specific resource object via:

            sg_buffer_desc sg_query_buffer_desc(sg_buffer buf)
            sg_image_desc sg_query_image_desc(sg_image img)
            sg_sampler_desc sg_query_sampler_desc(sg_sampler smp)
            sg_shader_desc sq_query_shader_desc(sg_shader shd)
            sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip)
            sg_attachments_desc sg_query_attachments_desc(sg_attachments atts)

        ...but NOTE that the returned desc structs may be incomplete, only
        creation attributes that are kept around internally after resource
        creation will be filled in, and in some cases (like shaders) that's
        very little. Any missing attributes will be set to zero. The returned
        desc structs might still be useful as partial blueprint for creating
        similar resources if filled up with the missing attributes.

        Calling the query-desc functions on an invalid resource will return
        completely zeroed structs (it makes sense to check  the resource state
        with sg_query_*_state() first)

    --- you can query the default resource creation parameters through the functions

            sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc)
            sg_image_desc sg_query_image_defaults(const sg_image_desc* desc)
            sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc)
            sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc)
            sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc)
            sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc* desc)

        These functions take a pointer to a desc structure which may contain
        zero-initialized items for default values. These zero-init values
        will be replaced with their concrete values in the returned desc
        struct.

    --- you can inspect various internal resource runtime values via:

            sg_buffer_info sg_query_buffer_info(sg_buffer buf)
            sg_image_info sg_query_image_info(sg_image img)
            sg_sampler_info sg_query_sampler_info(sg_sampler smp)
            sg_shader_info sg_query_shader_info(sg_shader shd)
            sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip)
            sg_attachments_info sg_query_attachments_info(sg_attachments atts)

        ...please note that the returned info-structs are tied quite closely
        to sokol_gfx.h internals, and may change more often than other
        public API functions and structs.

    --- you can query frame stats and control stats collection via:

            sg_query_frame_stats()
            sg_enable_frame_stats()
            sg_disable_frame_stats()
            sg_frame_stats_enabled()

    --- you can ask at runtime what backend sokol_gfx.h has been compiled for:

            sg_backend sg_query_backend(void)

    --- call the following helper functions to compute the number of
        bytes in a texture row or surface for a specific pixel format.
        These functions might be helpful when preparing image data for consumption
        by sg_make_image() or sg_update_image():

            int sg_query_row_pitch(sg_pixel_format fmt, int width, int int row_align_bytes);
            int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes);

        Width and height are generally in number pixels, but note that 'row' has different meaning
        for uncompressed vs compressed pixel formats: for uncompressed formats, a row is identical
        with a single line if pixels, while in compressed formats, one row is a line of *compression blocks*.

        This is why calling sg_query_surface_pitch() for a compressed pixel format and height
        N, N+1, N+2, ... may return the same result.

        The row_align_bytes parammeter is for added flexibility. For image data that goes into
        the sg_make_image() or sg_update_image() this should generally be 1, because these
        functions take tightly packed image data as input no matter what alignment restrictions
        exist in the backend 3D APIs.

    ON INITIALIZATION:
    ==================
    When calling sg_setup(), a pointer to an sg_desc struct must be provided
    which contains initialization options. These options provide two types
    of information to sokol-gfx:

        (1) upper bounds and limits needed to allocate various internal
            data structures:
                - the max number of resources of each type that can
                  be alive at the same time, this is used for allocating
                  internal pools
                - the max overall size of uniform data that can be
                  updated per frame, including a worst-case alignment
                  per uniform update (this worst-case alignment is 256 bytes)
                - the max size of all dynamic resource updates (sg_update_buffer,
                  sg_append_buffer and sg_update_image) per frame
                - the max number of compute-dispatch calls in a compute pass
            Not all of those limit values are used by all backends, but it is
            good practice to provide them none-the-less.

        (2) 3D backend "environment information" in a nested sg_environment struct:
            - pointers to backend-specific context- or device-objects (for instance
              the D3D11, WebGPU or Metal device objects)
            - defaults for external swapchain pixel formats and sample counts,
              these will be used as default values in image and pipeline objects,
              and the sg_swapchain struct passed into sg_begin_pass()
            Usually you provide a complete sg_environment struct through
            a helper function, as an example look at the sglue_environment()
            function in the sokol_glue.h header.

    See the documentation block of the sg_desc struct below for more information.


    ON RENDER PASSES
    ================
    Relevant samples:
        - https://floooh.github.io/sokol-html5/offscreen-sapp.html
        - https://floooh.github.io/sokol-html5/offscreen-msaa-sapp.html
        - https://floooh.github.io/sokol-html5/mrt-sapp.html
        - https://floooh.github.io/sokol-html5/mrt-pixelformats-sapp.html

    A render pass groups rendering commands into a set of render target images
    (called 'pass attachments'). Render target images can be used in subsequent
    passes as textures (it is invalid to use the same image both as render target
    and as texture in the same pass).

    The following sokol-gfx functions must only be called inside a render-pass:

        sg_apply_viewport[f]
        sg_apply_scissor_rect[f]
        sg_draw

    The folling function may be called inside a render- or compute-pass, but
    not outside a pass:

        sg_apply_pipeline
        sg_apply_bindings
        sg_apply_uniforms

    A frame must have at least one 'swapchain render pass' which renders into an
    externally provided swapchain provided as an sg_swapchain struct to the
    sg_begin_pass() function. If you use sokol_gfx.h together with sokol_app.h,
    just call the sglue_swapchain() helper function in sokol_glue.h to
    provide the swapchain information. Otherwise the following information
    must be provided:

        - the color pixel-format of the swapchain's render surface
        - an optional depth/stencil pixel format if the swapchain
          has a depth/stencil buffer
        - an optional sample-count for MSAA rendering
        - NOTE: the above three values can be zero-initialized, in that
          case the defaults from the sg_environment struct will be used that
          had been passed to the sg_setup() function.
        - a number of backend specific objects:
            - GL/GLES3: just a GL framebuffer handle
            - D3D11:
                - an ID3D11RenderTargetView for the rendering surface
                - if MSAA is used, an ID3D11RenderTargetView as
                  MSAA resolve-target
                - an optional ID3D11DepthStencilView for the
                  depth/stencil buffer
            - WebGPU
                - a WGPUTextureView object for the rendering surface
                - if MSAA is used, a WGPUTextureView object as MSAA resolve target
                - an optional WGPUTextureView for the
            - Metal (NOTE that the roles of provided surfaces is slightly
              different in Metal than in D3D11 or WebGPU, notably, the
              CAMetalDrawable is either rendered to directly, or serves
              as MSAA resolve target):
                - a CAMetalDrawable object which is either rendered
                  into directly, or in case of MSAA rendering, serves
                  as MSAA-resolve-target
                - if MSAA is used, an multisampled MTLTexture where
                  rendering goes into
                - an optional MTLTexture for the depth/stencil buffer

    It's recommended that you create a helper function which returns an
    initialized sg_swapchain struct by value. This can then be directly plugged
    into the sg_begin_pass function like this:

        sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() });

    As an example for such a helper function check out the function sglue_swapchain()
    in the sokol_glue.h header.

    For offscreen render passes, the render target images used in a render pass
    are baked into an immutable sg_attachments object.

    For a simple offscreen scenario with one color-, one depth-stencil-render
    target and without multisampling, creating an attachment object looks like this:

    First create two render target images, one with a color pixel format,
    and one with the depth- or depth-stencil pixel format. Both images
    must have the same dimensions:

        const sg_image color_img = sg_make_image(&(sg_image_desc){
            .render_target = true,
            .width = 256,
            .height = 256,
            .pixel_format = SG_PIXELFORMAT_RGBA8,
            .sample_count = 1,
        });
        const sg_image depth_img = sg_make_image(&(sg_image_desc){
            .render_target = true,
            .width = 256,
            .height = 256,
            .pixel_format = SG_PIXELFORMAT_DEPTH,
            .sample_count = 1,
        });

    NOTE: when creating render target images, have in mind that some default values
    are aligned with the default environment attributes in the sg_environment struct
    that was passed into the sg_setup() call:

        - the default value for sg_image_desc.pixel_format is taken from
          sg_environment.defaults.color_format
        - the default value for sg_image_desc.sample_count is taken from
          sg_environment.defaults.sample_count
        - the default value for sg_image_desc.num_mipmaps is always 1

    Next create an attachments object:

        const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){
            .colors[0].image = color_img,
            .depth_stencil.image = depth_img,
        });

    This attachments object is then passed into the sg_begin_pass() function
    in place of the swapchain struct:

        sg_begin_pass(&(sg_pass){ .attachments = atts });

    Swapchain and offscreen passes form dependency trees each with a swapchain
    pass at the root, offscreen passes as nodes, and render target images as
    dependencies between passes.

    sg_pass_action structs are used to define actions that should happen at the
    start and end of rendering passes (such as clearing pass attachments to a
    specific color or depth-value, or performing an MSAA resolve operation at
    the end of a pass).

    A typical sg_pass_action object which clears the color attachment to black
    might look like this:

        const sg_pass_action = {
            .colors[0] = {
                .load_action = SG_LOADACTION_CLEAR,
                .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f }
            }
        };

    This omits the defaults for the color attachment store action, and
    the depth-stencil-attachments actions. The same pass action with the
    defaults explicitly filled in would look like this:

        const sg_pass_action pass_action = {
            .colors[0] = {
                .load_action = SG_LOADACTION_CLEAR,
                .store_action = SG_STOREACTION_STORE,
                .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f }
            },
            .depth = = {
                .load_action = SG_LOADACTION_CLEAR,
                .store_action = SG_STOREACTION_DONTCARE,
                .clear_value = 1.0f,
            },
            .stencil = {
                .load_action = SG_LOADACTION_CLEAR,
                .store_action = SG_STOREACTION_DONTCARE,
                .clear_value = 0
            }
        };

    With the sg_pass object and sg_pass_action struct in place everything
    is ready now for the actual render pass:

    Using such this prepared sg_pass_action in a swapchain pass looks like
    this:

        sg_begin_pass(&(sg_pass){
            .action = pass_action,
            .swapchain = sglue_swapchain()
        });
        ...
        sg_end_pass();

    ...of alternatively in one offscreen pass:

        sg_begin_pass(&(sg_pass){
            .action = pass_action,
            .attachments = attachments,
        });
        ...
        sg_end_pass();

    Offscreen rendering can also go into a mipmap, or a slice/face of
    a cube-, array- or 3d-image (which some restrictions, for instance
    it's not possible to create a 3D image with a depth/stencil pixel format,
    these exceptions are generally caught by the sokol-gfx validation layer).

    The mipmap/slice selection happens at attachments creation time, for instance
    to render into mipmap 2 of slice 3 of an array texture:

        const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){
            .colors[0] = {
                .image = color_img,
                .mip_level = 2,
                .slice = 3,
            },
            .depth_stencil.image = depth_img,
        });

    If MSAA offscreen rendering is desired, the multi-sample rendering result
    must be 'resolved' into a separate 'resolve image', before that image can
    be used as texture.

    Creating a simple attachments object for multisampled rendering requires
    3 attachment images: the color attachment image which has a sample
    count > 1, a resolve attachment image of the same size and pixel format
    but a sample count == 1, and a depth/stencil attachment image with
    the same size and sample count as the color attachment image:

        const sg_image color_img = sg_make_image(&(sg_image_desc){
            .render_target = true,
            .width = 256,
            .height = 256,
            .pixel_format = SG_PIXELFORMAT_RGBA8,
            .sample_count = 4,
        });
        const sg_image resolve_img = sg_make_image(&(sg_image_desc){
            .render_target = true,
            .width = 256,
            .height = 256,
            .pixel_format = SG_PIXELFORMAT_RGBA8,
            .sample_count = 1,
        });
        const sg_image depth_img = sg_make_image(&(sg_image_desc){
            .render_target = true,
            .width = 256,
            .height = 256,
            .pixel_format = SG_PIXELFORMAT_DEPTH,
            .sample_count = 4,
        });

    ...create the attachments object:

        const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){
            .colors[0].image = color_img,
            .resolves[0].image = resolve_img,
            .depth_stencil.image = depth_img,
        });

    If an attachments object defines a resolve image in a specific resolve attachment slot,
    an 'msaa resolve operation' will happen in sg_end_pass().

    In this scenario, the content of the MSAA color attachment doesn't need to be
    preserved (since it's only needed inside sg_end_pass for the msaa-resolve), so
    the .store_action should be set to "don't care":

        const sg_pass_action = {
            .colors[0] = {
                .load_action = SG_LOADACTION_CLEAR,
                .store_action = SG_STOREACTION_DONTCARE,
                .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f }
            }
        };

    The actual render pass looks as usual:

        sg_begin_pass(&(sg_pass){ .action = pass_action, .attachments = atts });
        ...
        sg_end_pass();

    ...after sg_end_pass() the only difference to the non-msaa scenario is that the
    rendering result which is going to be used as texture in a followup pass is
    in 'resolve_img', not in 'color_img' (in fact, trying to bind color_img as a
    texture would result in a validation error).


    ON COMPUTE PASSES
    =================
    Compute passes are used to update the content of storage resources
    (currently only storage buffers) by running compute shader code on
    the GPU. This will almost always be more efficient than computing
    that same data on the CPU and uploading the data via `sg_update_buffer()`.

    NOTE: compute passes are only supported on the following platforms and
    backends:

        - macOS and iOS with Metal
        - Windows with D3D11 and OpenGL
        - Linux with OpenGL or GLES3.1+
        - Web with WebGPU
        - Android with GLES3.1+

    ...this means compute shaders can't be used on the following platform/backend
    combos (the same restrictions apply to using storage buffers without compute
    shaders):

        - macOS with GL
        - iOS with GLES3
        - Web with WebGL2

    A compute pass is started with:

        sg_begin_pass(&(sg_pass){ .compute = true });

    ...and finished with:

        sg_end_pass();

    Typically the following functions will be called inside a compute pass:

        sg_apply_pipeline
        sg_apply_bindings
        sg_apply_uniforms
        sg_dispatch

    The following functions are disallowed inside a compute pass
    and will cause validation layer errors:

        sg_apply_viewport[f]
        sg_apply_scissor_rect[f]
        sg_draw

    Only special 'compute shaders' and 'compute pipelines' can be used in
    compute passes. A compute shader only has a compute-function instead
    of a vertex- and fragment-function pair, and it doesn't accept vertex-
    and index-buffers as input, only storage-buffers, textures and non-filtering
    samplers (more details on compute shaders in the following section).

    A compute pipeline is created by providing a compute shader object,
    setting the .compute creation parameter to true and not defining any
    'render state':

        sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){
            .compute = true,
            .shader = compute_shader,
        });

    The sg_apply_bindings and sg_apply_uniforms calls are the same as in
    render passes, with the exception that no vertex- and index-buffers
    can be bound in the sg_apply_bindings call.

    Finally to kick off a compute workload, call sg_dispatch with the
    number of workgroups in the x, y and z-dimension:

        sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z)

    Also see the following compute-shader samples:

        - https://floooh.github.io/sokol-webgpu/instancing-compute-sapp.html
        - https://floooh.github.io/sokol-webgpu/computeboids-sapp.html


    ON SHADER CREATION
    ==================
    sokol-gfx doesn't come with an integrated shader cross-compiler, instead
    backend-specific shader sources or binary blobs need to be provided when
    creating a shader object, along with reflection information about the
    shader resource binding interface needed to bind sokol-gfx resources to the
    proper shader inputs.

    The easiest way to provide all this shader creation data is to use the
    sokol-shdc shader compiler tool to compile shaders from a common
    GLSL syntax into backend-specific sources or binary blobs, along with
    shader interface information and uniform blocks mapped to C structs.

    To create a shader using a C header which has been code-generated by sokol-shdc:

        // include the C header code-generated by sokol-shdc:
        #include "myshader.glsl.h"
        ...

        // create shader using a code-generated helper function from the C header:
        sg_shader shd = sg_make_shader(myshader_shader_desc(sg_query_backend()));

    The samples in the 'sapp' subdirectory of the sokol-samples project
    also use the sokol-shdc approach:

        https://github.com/floooh/sokol-samples/tree/master/sapp

    If you're planning to use sokol-shdc, you can stop reading here, instead
    continue with the sokol-shdc documentation:

        https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md

    To create shaders with backend-specific shader code or binary blobs,
    the sg_make_shader() function requires the following information:

    - Shader code or shader binary blobs for the vertex- and fragment-, or the
      compute-shader-stage:
        - for the desktop GL backend, source code can be provided in '#version 410' or
          '#version 430', version 430 is required when using storage buffers and
          compute shaders support, but note that this is not available on macOS
        - for the GLES3 backend, source code must be provided in '#version 300 es' syntax
        - for the D3D11 backend, shaders can be provided as source or binary
          blobs, the source code should be in HLSL4.0 (for compatibility with old
          low-end GPUs) or preferrably in HLSL5.0 syntax, note that when
          shader source code is provided for the D3D11 backend, sokol-gfx will
          dynamically load 'd3dcompiler_47.dll'
        - for the Metal backends, shaders can be provided as source or binary blobs, the
          MSL version should be in 'metal-1.1' (other versions may work but are not tested)
        - for the WebGPU backend, shaders must be provided as WGSL source code
        - optionally the following shader-code related attributes can be provided:
            - an entry function name (only on D3D11 or Metal, but not OpenGL)
            - on D3D11 only, a compilation target (default is "vs_4_0" and "ps_4_0")

    - Information about the input vertex attributes used by the vertex shader,
      most of that backend-specific:
        - An optional 'base type' (float, signed-/unsigned-int) for each vertex
          attribute. When provided, this used by the validation layer to check
          that the CPU-side input vertex format is compatible with the input
          vertex declaration of the vertex shader.
        - Metal: no location information needed since vertex attributes are always bound
          by their attribute location defined in the shader via '[[attribute(N)]]'
        - WebGPU: no location information needed since vertex attributes are always
          bound by their attribute location defined in the shader via `@location(N)`
        - GLSL: vertex attribute names can be optionally provided, in that case their
          location will be looked up by name, otherwise, the vertex attribute location
          can be defined with 'layout(location = N)'
        - D3D11: a 'semantic name' and 'semantic index' must be provided for each vertex
          attribute, e.g. if the vertex attribute is defined as 'TEXCOORD1' in the shader,
          the semantic name would be 'TEXCOORD', and the semantic index would be '1'

      NOTE that vertex attributes currently must not have gaps. This requirement
      may be relaxed in the future.

    - Specifically for Metal compute shaders, the 'number of threads per threadgroup'
      must be provided. Normally this is extracted by sokol-shdc from the GLSL
      shader source code. For instance the following statement in the input
      GLSL:

        layout(local_size_x=64, local_size_y=1, local_size_z=1) in;

      ...will be communicated to the sokol-gfx Metal backend in the
      code-generated sg_shader_desc struct:

        (sg_shader_desc){
            .mtl_threads_per_threadgroup = { .x = 64, .y = 1, .z = 1 },
        }

    - Information about each uniform block used in the shader:
        - the shader stage of the uniform block (vertex, fragment or compute)
        - the size of the uniform block in number of bytes
        - a memory layout hint (currently 'native' or 'std140') where 'native' defines a
          backend-specific memory layout which shouldn't be used for cross-platform code.
          Only std140 guarantees a backend-agnostic memory layout.
        - a backend-specific bind slot:
            - D3D11/HLSL: the buffer register N (`register(bN)`) where N is 0..7
            - Metal/MSL: the buffer bind slot N (`[[buffer(N)]]`) where N is 0..7
            - WebGPU: the binding N in `@group(0) @binding(N)` where N is 0..15
        - For GLSL only: a description of the internal uniform block layout, which maps
          member types and their offsets on the CPU side to uniform variable names
          in the GLSL shader
        - please also NOTE the documentation sections about UNIFORM DATA LAYOUT
          and CROSS-BACKEND COMMON UNIFORM DATA LAYOUT below!

    - A description of each storage buffer used in the shader:
        - the shader stage of the storage buffer
        - a boolean 'readonly' flag, this is used for validation and hazard
          tracking in some 3D backends. Note that in render passes, only
          readonly storage buffer bindings are allowed. In compute passes, any
          read/write storage buffer binding is assumbed to be written to by the
          compute shader.
        - a backend-specific bind slot:
            - D3D11/HLSL:
                - for readonly storage buffer bindings: the texture register N
                  (`register(tN)`) where N is 0..23 (in HLSL, readonly storage
                  buffers and textures share the same bind space for
                  'shader resource views')
                - for read/write storage buffer buffer bindings: the UAV register N
                  (`register(uN)`) where N is 0..7 (in HLSL, readwrite storage
                  buffers use their own bind space for 'unordered access views')
            - Metal/MSL: the buffer bind slot N (`[[buffer(N)]]`) where N is 8..15
            - WebGPU/WGSL: the binding N in `@group(0) @binding(N)` where N is 0..127
            - GL/GLSL: the buffer binding N in `layout(binding=N)` where N is 0..7
        - note that storage buffers are not supported on all backends
          and platforms

    - A description of each texture/image used in the shader:
        - the shader stage of the texture (vertex, fragment or compute)
        - the expected image type:
            - SG_IMAGETYPE_2D
            - SG_IMAGETYPE_CUBE
            - SG_IMAGETYPE_3D
            - SG_IMAGETYPE_ARRAY
        - the expected 'image sample type':
            - SG_IMAGESAMPLETYPE_FLOAT
            - SG_IMAGESAMPLETYPE_DEPTH
            - SG_IMAGESAMPLETYPE_SINT
            - SG_IMAGESAMPLETYPE_UINT
            - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT
        - a flag whether the texture is expected to be multisampled
        - a backend-specific bind slot:
            - D3D11/HLSL: the texture register N (`register(tN)`) where N is 0..23
              (in HLSL, readonly storage buffers and texture share the same bind space)
            - Metal/MSL: the texture bind slot N (`[[texture(N)]]`) where N is 0..15
            - WebGPU/WGSL: the binding N in `@group(0) @binding(N)` where N is 0..127

    - A description of each sampler used in the shader:
        - the shader stage of the sampler (vertex, fragment or compute)
        - the expected sampler type:
            - SG_SAMPLERTYPE_FILTERING,
            - SG_SAMPLERTYPE_NONFILTERING,
            - SG_SAMPLERTYPE_COMPARISON,
        - a backend-specific bind slot:
            - D3D11/HLSL: the sampler register N (`register(sN)`) where N is 0..15
            - Metal/MSL: the sampler bind slot N (`[[sampler(N)]]`) where N is 0..15
            - WebGPU/WGSL: the binding N in `@group(0) @binding(N)` where N is 0..127

    - An array of 'image-sampler-pairs' used by the shader to sample textures,
      for D3D11, Metal and WebGPU this is used for validation purposes to check
      whether the texture and sampler are compatible with each other (especially
      WebGPU is very picky about combining the correct
      texture-sample-type with the correct sampler-type). For GLSL an
      additional 'combined-image-sampler name' must be provided because 'OpenGL
      style GLSL' cannot handle separate texture and sampler objects, but still
      groups them into a traditional GLSL 'sampler object'.

    Compatibility rules for image-sample-type vs sampler-type are as follows:

        - SG_IMAGESAMPLETYPE_FLOAT => (SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING)
        - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT => SG_SAMPLERTYPE_NONFILTERING
        - SG_IMAGESAMPLETYPE_SINT => SG_SAMPLERTYPE_NONFILTERING
        - SG_IMAGESAMPLETYPE_UINT => SG_SAMPLERTYPE_NONFILTERING
        - SG_IMAGESAMPLETYPE_DEPTH => SG_SAMPLERTYPE_COMPARISON

    Backend-specific bindslot ranges (not relevant when using sokol-shdc):

        - D3D11/HLSL:
            - separate bindslot space per shader stage
            - uniform blocks (as cbuffer): `register(b0..b7)`
            - textures and readonly storage buffers: `register(t0..t23)`
            - read/write storage buffers: `register(u0..u7)`
            - samplers: `register(s0..s15)`
        - Metal/MSL:
            - separate bindslot space per shader stage
            - uniform blocks: `[[buffer(0..7)]]`
            - storage buffers: `[[buffer(8..15)]]`
            - textures: `[[texture(0..15)]]`
            - samplers: `[[sampler(0..15)]]`
        - WebGPU/WGSL:
            - common bindslot space across shader stages
            - uniform blocks: `@group(0) @binding(0..15)`
            - textures, samplers and storage buffers: `@group(1) @binding(0..127)`
        - GL/GLSL:
            - uniforms and image-samplers are bound by name
            - storage buffers: `layout(std430, binding=0..7)` (common
              bindslot space across shader stages)

    For example code of how to create backend-specific shader objects,
    please refer to the following samples:

        - for D3D11:    https://github.com/floooh/sokol-samples/tree/master/d3d11
        - for Metal:    https://github.com/floooh/sokol-samples/tree/master/metal
        - for OpenGL:   https://github.com/floooh/sokol-samples/tree/master/glfw
        - for GLES3:    https://github.com/floooh/sokol-samples/tree/master/html5
        - for WebGPI:   https://github.com/floooh/sokol-samples/tree/master/wgpu


    ON SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT AND SG_SAMPLERTYPE_NONFILTERING
    ========================================================================
    The WebGPU backend introduces the concept of 'unfilterable-float' textures,
    which can only be combined with 'nonfiltering' samplers (this is a restriction
    specific to WebGPU, but since the same sokol-gfx code should work across
    all backend, the sokol-gfx validation layer also enforces this restriction
    - the alternative would be undefined behaviour in some backend APIs on
    some devices).

    The background is that some mobile devices (most notably iOS devices) can
    not perform linear filtering when sampling textures with certain pixel
    formats, most notable the 32F formats:

        - SG_PIXELFORMAT_R32F
        - SG_PIXELFORMAT_RG32F
        - SG_PIXELFORMAT_RGBA32F

    The information of whether a shader is going to be used with such an
    unfilterable-float texture must already be provided in the sg_shader_desc
    struct when creating the shader (see the above section "ON SHADER CREATION").

    If you are using the sokol-shdc shader compiler, the information whether a
    texture/sampler binding expects an 'unfilterable-float/nonfiltering'
    texture/sampler combination cannot be inferred from the shader source
    alone, you'll need to provide this hint via annotation-tags. For instance
    here is an example from the ozz-skin-sapp.c sample shader which samples an
    RGBA32F texture with skinning matrices in the vertex shader:

    ```glsl
    @image_sample_type joint_tex unfilterable_float
    uniform texture2D joint_tex;
    @sampler_type smp nonfiltering
    uniform sampler smp;
    ```

    This will result in SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT and
    SG_SAMPLERTYPE_NONFILTERING being written to the code-generated
    sg_shader_desc struct.


    ON VERTEX FORMATS
    =================
    Sokol-gfx implements the same strict mapping rules from CPU-side
    vertex component formats to GPU-side vertex input data types:

    - float and packed normalized CPU-side formats must be used as
      floating point base type in the vertex shader
    - packed signed-integer CPU-side formats must be used as signed
      integer base type in the vertex shader
    - packed unsigned-integer CPU-side formats must be used as unsigned
      integer base type in the vertex shader

    These mapping rules are enforced by the sokol-gfx validation layer,
    but only when sufficient reflection information is provided in
    `sg_shader_desc.attrs[].base_type`. This is the case when sokol-shdc
    is used, otherwise the default base_type will be SG_SHADERATTRBASETYPE_UNDEFINED
    which causes the sokol-gfx validation check to be skipped (of course you
    can also provide the per-attribute base type information manually when
    not using sokol-shdc).

    The detailed mapping rules from SG_VERTEXFORMAT_* to GLSL data types
    are as follows:

    - FLOAT[*] => float, vec*
    - BYTE4N => vec* (scaled to -1.0 .. +1.0)
    - UBYTE4N => vec* (scaled to 0.0 .. +1.0)
    - SHORT[*]N => vec* (scaled to -1.0 .. +1.0)
    - USHORT[*]N => vec* (scaled to 0.0 .. +1.0)
    - INT[*] => int, ivec*
    - UINT[*] => uint, uvec*
    - BYTE4 => int*
    - UBYTE4 => uint*
    - SHORT[*] => int*
    - USHORT[*] => uint*

    NOTE that sokol-gfx only provides vertex formats with sizes of a multiple
    of 4 (e.g. BYTE4N but not BYTE2N). This is because vertex components must
    be 4-byte aligned anyway.


    UNIFORM DATA LAYOUT:
    ====================
    NOTE: if you use the sokol-shdc shader compiler tool, you don't need to worry
    about the following details.

    The data that's passed into the sg_apply_uniforms() function must adhere to
    specific layout rules so that the GPU shader finds the uniform block
    items at the right offset.

    For the D3D11 and Metal backends, sokol-gfx only cares about the size of uniform
    blocks, but not about the internal layout. The data will just be copied into
    a uniform/constant buffer in a single operation and it's up you to arrange the
    CPU-side layout so that it matches the GPU side layout. This also means that with
    the D3D11 and Metal backends you are not limited to a 'cross-platform' subset
    of uniform variable types.

    If you ever only use one of the D3D11, Metal *or* WebGPU backend, you can stop reading here.

    For the GL backends, the internal layout of uniform blocks matters though,
    and you are limited to a small number of uniform variable types. This is
    because sokol-gfx must be able to locate the uniform block members in order
    to upload them to the GPU with glUniformXXX() calls.

    To describe the uniform block layout to sokol-gfx, the following information
    must be passed to the sg_make_shader() call in the sg_shader_desc struct:

        - a hint about the used packing rule (either SG_UNIFORMLAYOUT_NATIVE or
          SG_UNIFORMLAYOUT_STD140)
        - a list of the uniform block members types in the correct order they
          appear on the CPU side

    For example if the GLSL shader has the following uniform declarations:

        uniform mat4 mvp;
        uniform vec2 offset0;
        uniform vec2 offset1;
        uniform vec2 offset2;

    ...and on the CPU side, there's a similar C struct:

        typedef struct {
            float mvp[16];
            float offset0[2];
            float offset1[2];
            float offset2[2];
        } params_t;

    ...the uniform block description in the sg_shader_desc must look like this:

        sg_shader_desc desc = {
            .vs.uniform_blocks[0] = {
                .size = sizeof(params_t),
                .layout = SG_UNIFORMLAYOUT_NATIVE,  // this is the default and can be omitted
                .uniforms = {
                    // order must be the same as in 'params_t':
                    [0] = { .name = "mvp", .type = SG_UNIFORMTYPE_MAT4 },
                    [1] = { .name = "offset0", .type = SG_UNIFORMTYPE_VEC2 },
                    [2] = { .name = "offset1", .type = SG_UNIFORMTYPE_VEC2 },
                    [3] = { .name = "offset2", .type = SG_UNIFORMTYPE_VEC2 },
                }
            }
        };

    With this information sokol-gfx can now compute the correct offsets of the data items
    within the uniform block struct.

    The SG_UNIFORMLAYOUT_NATIVE packing rule works fine if only the GL backends are used,
    but for proper D3D11/Metal/GL a subset of the std140 layout must be used which is
    described in the next section:


    CROSS-BACKEND COMMON UNIFORM DATA LAYOUT
    ========================================
    For cross-platform / cross-3D-backend code it is important that the same uniform block
    layout on the CPU side can be used for all sokol-gfx backends. To achieve this,
    a common subset of the std140 layout must be used:

    - The uniform block layout hint in sg_shader_desc must be explicitly set to
      SG_UNIFORMLAYOUT_STD140.
    - Only the following GLSL uniform types can be used (with their associated sokol-gfx enums):
        - float => SG_UNIFORMTYPE_FLOAT
        - vec2  => SG_UNIFORMTYPE_FLOAT2
        - vec3  => SG_UNIFORMTYPE_FLOAT3
        - vec4  => SG_UNIFORMTYPE_FLOAT4
        - int   => SG_UNIFORMTYPE_INT
        - ivec2 => SG_UNIFORMTYPE_INT2
        - ivec3 => SG_UNIFORMTYPE_INT3
        - ivec4 => SG_UNIFORMTYPE_INT4
        - mat4  => SG_UNIFORMTYPE_MAT4
    - Alignment for those types must be as follows (in bytes):
        - float => 4
        - vec2  => 8
        - vec3  => 16
        - vec4  => 16
        - int   => 4
        - ivec2 => 8
        - ivec3 => 16
        - ivec4 => 16
        - mat4  => 16
    - Arrays are only allowed for the following types: vec4, int4, mat4.

    Note that the HLSL cbuffer layout rules are slightly different from the
    std140 layout rules, this means that the cbuffer declarations in HLSL code
    must be tweaked so that the layout is compatible with std140.

    The by far easiest way to tackle the common uniform block layout problem is
    to use the sokol-shdc shader cross-compiler tool!


    ON STORAGE BUFFERS
    ==================
    The two main purpose of storage buffers are:

        - to be populated by compute shaders with dynamically generated data
        - for providing random-access data to all shader stages

    Storage buffers can be used to pass large amounts of random access structured
    data from the CPU side to the shaders. They are similar to data textures, but are
    more convenient to use both on the CPU and shader side since they can be accessed
    in shaders as as a 1-dimensional array of struct items.

    Storage buffers are *NOT* supported on the following platform/backend combos:

    - macOS+GL (because storage buffers require GL 4.3, while macOS only goes up to GL 4.1)
    - platforms which only support a GLES3.0 context (WebGL2 and iOS)

    To use storage buffers, the following steps are required:

        - write a shader which uses storage buffers (vertex- and fragment-shaders
          can only read from storage buffers, while compute-shaders can both read
          and write storage buffers)
        - create one or more storage buffers via sg_make_buffer() with the
          buffer type SG_BUFFERTYPE_STORAGEBUFFER
        - when creating a shader via sg_make_shader(), populate the sg_shader_desc
          struct with binding info (when using sokol-shdc, this step will be taken care
          of automatically)
            - which storage buffer bind slots on the vertex-, fragment- or compute-stage
              are occupied
            - whether the storage buffer on that bind slot is readonly (readonly
              bindings are required for vertex- and fragment-shaders, and in compute
              shaders the readonly flag is used to control hazard tracking in some
              3D backends)

        - when calling sg_apply_bindings(), apply the matching bind slots with the previously
          created storage buffers
        - ...and that's it.

    For more details, see the following backend-agnostic sokol samples:

    - simple vertex pulling from a storage buffer:
        - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.c
        - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.glsl
    - instanced rendering via storage buffers (vertex- and instance-pulling):
        - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.c
        - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.glsl
    - storage buffers both on the vertex- and fragment-stage:
        - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.c
        - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.glsl
    - the Ozz animation sample rewritten to pull all rendering data from storage buffers:
        - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.cc
        - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.glsl
    - the instancing sample modified to use compute shaders:
        - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-compute-sapp.c
        - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-compute-sapp.glsl
    - the Compute Boids sample ported to sokol-gfx:
        - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/computeboids-sapp.c
        - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/computeboids-sapp.glsl

    ...also see the following backend-specific vertex pulling samples (those also don't use sokol-shdc):

    - D3D11: https://github.com/floooh/sokol-samples/blob/master/d3d11/vertexpulling-d3d11.c
    - desktop GL: https://github.com/floooh/sokol-samples/blob/master/glfw/vertexpulling-glfw.c
    - Metal: https://github.com/floooh/sokol-samples/blob/master/metal/vertexpulling-metal.c
    - WebGPU: https://github.com/floooh/sokol-samples/blob/master/wgpu/vertexpulling-wgpu.c

    ...and the backend specific compute shader samples:

    - D3D11: https://github.com/floooh/sokol-samples/blob/master/d3d11/instancing-compute-d3d11.c
    - desktop GL: https://github.com/floooh/sokol-samples/blob/master/glfw/instancing-compute-glfw.c
    - Metal: https://github.com/floooh/sokol-samples/blob/master/metal/instancing-compute-metal.c
    - WebGPU: https://github.com/floooh/sokol-samples/blob/master/wgpu/instancing-compute-wgpu.c

    Storage buffer shader authoring caveats when using sokol-shdc:

        - declare a read-only storage buffer interface block with `layout(binding=N) readonly buffer [name] { ... }`
          (where 'N' is the index in `sg_bindings.storage_buffers[N]`)
        - ...or a read/write storage buffer interface block with `layout(binding=N) buffer [name] { ... }`
        - declare a struct which describes a single array item in the storage buffer interface block
        - only put a single flexible array member into the storage buffer interface block

    E.g. a complete example in 'sokol-shdc GLSL':

        ```glsl
        @vs
        // declare a struct:
        struct sb_vertex {
            vec3 pos;
            vec4 color;
        }
        // declare a buffer interface block with a single flexible struct array:
        layout(binding=0) readonly buffer vertices {
            sb_vertex vtx[];
        }
        // in the shader function, access the storage buffer like this:
        void main() {
            vec3 pos = vtx[gl_VertexIndex].pos;
            ...
        }
        @end
        ```

    In a compute shader you can read and write the same item in the same
    storage buffer (but you'll have to be careful for random access since
    many threads of the same compute function run in parallel):

        @cs
        struct sb_item {
            vec3 pos;
            vec3 vel;
        }
        layout(binding=0) buffer items_ssbo {
            sb_item items[];
        }
        layout(local_size_x=64, local_size_y=1, local_size_z=1) in;
        void main() {
            uint idx = gl_GlobalInvocationID.x;
            vec3 pos = items[idx].pos;
            ...
            items[idx].pos = pos;
        }
        @end

    Backend-specific storage-buffer caveats (not relevant when using sokol-shdc):

        D3D11:
            - storage buffers are created as 'raw' Byte Address Buffers
              (https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-intro#raw-views-of-buffers)
            - in HLSL, use a ByteAddressBuffer for readonly access of the buffer content:
              (https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-byteaddressbuffer)
            - ...or RWByteAddressBuffer for read/write access:
              (https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-rwbyteaddressbuffer)
            - readonly-storage buffers and textures are both bound as 'shader-resource-view' and
              share the same bind slots (declared as `register(tN)` in HLSL), where N must be in the range 0..23)
            - read/write storage buffers are bound as 'unordered-access-view' (declared as `register(uN)` in HLSL
              where N is in the range 0..7)

        Metal:
            - in Metal there is no internal difference between vertex-, uniform- and
              storage-buffers, all are bound to the same 'buffer bind slots' with the
              following reserved ranges:
                - vertex shader stage:
                    - uniform buffers: slots 0..7
                    - storage buffers: slots 8..15
                    - vertex buffers: slots 15..23
                - fragment shader stage:
                    - uniform buffers: slots 0..7
                    - storage buffers: slots 8..15
            - this means in MSL, storage buffer bindings start at [[buffer(8)]] both in
              the vertex and fragment stage

        GL:
            - the GL backend doesn't use name-lookup to find storage buffer bindings, this
              means you must annotate buffers with `layout(std430, binding=N)` in GLSL
            - ...where N is 0..7 in the vertex shader, and 8..15 in the fragment shader

        WebGPU:
            - in WGSL, textures, samplers and storage buffers all use a shared
              bindspace across all shader stages on bindgroup 1:

              `@group(1) @binding(0..127)


    TRACE HOOKS:
    ============
    sokol_gfx.h optionally allows to install "trace hook" callbacks for
    each public API functions. When a public API function is called, and
    a trace hook callback has been installed for this function, the
    callback will be invoked with the parameters and result of the function.
    This is useful for things like debugging- and profiling-tools, or
    keeping track of resource creation and destruction.

    To use the trace hook feature:

    --- Define SOKOL_TRACE_HOOKS before including the implementation.

    --- Setup an sg_trace_hooks structure with your callback function
        pointers (keep all function pointers you're not interested
        in zero-initialized), optionally set the user_data member
        in the sg_trace_hooks struct.

    --- Install the trace hooks by calling sg_install_trace_hooks(),
        the return value of this function is another sg_trace_hooks
        struct which contains the previously set of trace hooks.
        You should keep this struct around, and call those previous
        functions pointers from your own trace callbacks for proper
        chaining.

    As an example of how trace hooks are used, have a look at the
    imgui/sokol_gfx_imgui.h header which implements a realtime
    debugging UI for sokol_gfx.h on top of Dear ImGui.


    MEMORY ALLOCATION OVERRIDE
    ==========================
    You can override the memory allocation functions at initialization time
    like this:

        void* my_alloc(size_t size, void* user_data) {
            return malloc(size);
        }

        void my_free(void* ptr, void* user_data) {
            free(ptr);
        }

        ...
            sg_setup(&(sg_desc){
                // ...
                .allocator = {
                    .alloc_fn = my_alloc,
                    .free_fn = my_free,
                    .user_data = ...,
                }
            });
        ...

    If no overrides are provided, malloc and free will be used.

    This only affects memory allocation calls done by sokol_gfx.h
    itself though, not any allocations in OS libraries.


    ERROR REPORTING AND LOGGING
    ===========================
    To get any logging information at all you need to provide a logging callback in the setup call
    the easiest way is to use sokol_log.h:

        #include "sokol_log.h"

        sg_setup(&(sg_desc){ .logger.func = slog_func });

    To override logging with your own callback, first write a logging function like this:

        void my_log(const char* tag,                // e.g. 'sg'
                    uint32_t log_level,             // 0=panic, 1=error, 2=warn, 3=info
                    uint32_t log_item_id,           // SG_LOGITEM_*
                    const char* message_or_null,    // a message string, may be nullptr in release mode
                    uint32_t line_nr,               // line number in sokol_gfx.h
                    const char* filename_or_null,   // source filename, may be nullptr in release mode
                    void* user_data)
        {
            ...
        }

    ...and then setup sokol-gfx like this:

        sg_setup(&(sg_desc){
            .logger = {
                .func = my_log,
                .user_data = my_user_data,
            }
        });

    The provided logging function must be reentrant (e.g. be callable from
    different threads).

    If you don't want to provide your own custom logger it is highly recommended to use
    the standard logger in sokol_log.h instead, otherwise you won't see any warnings or
    errors.


    COMMIT LISTENERS
    ================
    It's possible to hook callback functions into sokol-gfx which are called from
    inside sg_commit() in unspecified order. This is mainly useful for libraries
    that build on top of sokol_gfx.h to be notified about the end/start of a frame.

    To add a commit listener, call:

        static void my_commit_listener(void* user_data) {
            ...
        }

        bool success = sg_add_commit_listener((sg_commit_listener){
            .func = my_commit_listener,
            .user_data = ...,
        });

    The function returns false if the internal array of commit listeners is full,
    or the same commit listener had already been added.

    If the function returns true, my_commit_listener() will be called each frame
    from inside sg_commit().

    By default, 1024 distinct commit listeners can be added, but this number
    can be tweaked in the sg_setup() call:

        sg_setup(&(sg_desc){
            .max_commit_listeners = 2048,
        });

    An sg_commit_listener item is equal to another if both the function
    pointer and user_data field are equal.

    To remove a commit listener:

        bool success = sg_remove_commit_listener((sg_commit_listener){
            .func = my_commit_listener,
            .user_data = ...,
        });

    ...where the .func and .user_data field are equal to a previous
    sg_add_commit_listener() call. The function returns true if the commit
    listener item was found and removed, and false otherwise.


    RESOURCE CREATION AND DESTRUCTION IN DETAIL
    ===========================================
    The 'vanilla' way to create resource objects is with the 'make functions':

        sg_buffer sg_make_buffer(const sg_buffer_desc* desc)
        sg_image sg_make_image(const sg_image_desc* desc)
        sg_sampler sg_make_sampler(const sg_sampler_desc* desc)
        sg_shader sg_make_shader(const sg_shader_desc* desc)
        sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc)
        sg_attachments sg_make_attachments(const sg_attachments_desc* desc)

    This will result in one of three cases:

        1. The returned handle is invalid. This happens when there are no more
           free slots in the resource pool for this resource type. An invalid
           handle is associated with the INVALID resource state, for instance:

                sg_buffer buf = sg_make_buffer(...)
                if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_INVALID) {
                    // buffer pool is exhausted
                }

        2. The returned handle is valid, but creating the underlying resource
           has failed for some reason. This results in a resource object in the
           FAILED state. The reason *why* resource creation has failed differ
           by resource type. Look for log messages with more details. A failed
           resource state can be checked with:

                sg_buffer buf = sg_make_buffer(...)
                if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED) {
                    // creating the resource has failed
                }

        3. And finally, if everything goes right, the returned resource is
           in resource state VALID and ready to use. This can be checked
           with:

                sg_buffer buf = sg_make_buffer(...)
                if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID) {
                    // creating the resource has failed
                }

    When calling the 'make functions', the created resource goes through a number
    of states:

        - INITIAL: the resource slot associated with the new resource is currently
          free (technically, there is no resource yet, just an empty pool slot)
        - ALLOC: a handle for the new resource has been allocated, this just means
          a pool slot has been reserved.
        - VALID or FAILED: in VALID state any 3D API backend resource objects have
          been successfully created, otherwise if anything went wrong, the resource
          will be in FAILED state.

    Sometimes it makes sense to first grab a handle, but initialize the
    underlying resource at a later time. For instance when loading data
    asynchronously from a slow data source, you may know what buffers and
    textures are needed at an early stage of the loading process, but actually
    loading the buffer or texture content can only be completed at a later time.

    For such situations, sokol-gfx resource objects can be created in two steps.
    You can allocate a handle upfront with one of the 'alloc functions':

        sg_buffer sg_alloc_buffer(void)
        sg_image sg_alloc_image(void)
        sg_sampler sg_alloc_sampler(void)
        sg_shader sg_alloc_shader(void)
        sg_pipeline sg_alloc_pipeline(void)
        sg_attachments sg_alloc_attachments(void)

    This will return a handle with the underlying resource object in the
    ALLOC state:

        sg_image img = sg_alloc_image();
        if (sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC) {
            // allocating an image handle has succeeded, otherwise
            // the image pool is full
        }

    Such an 'incomplete' handle can be used in most sokol-gfx rendering functions
    without doing any harm, sokol-gfx will simply skip any rendering operation
    that involve resources which are not in VALID state.

    At a later time (for instance once the texture has completed loading
    asynchronously), the resource creation can be completed by calling one of
    the 'init functions', those functions take an existing resource handle and
    'desc struct':

        void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc)
        void sg_init_image(sg_image img, const sg_image_desc* desc)
        void sg_init_sampler(sg_sampler smp, const sg_sampler_desc* desc)
        void sg_init_shader(sg_shader shd, const sg_shader_desc* desc)
        void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc)
        void sg_init_attachments(sg_attachments atts, const sg_attachments_desc* desc)

    The init functions expect a resource in ALLOC state, and after the function
    returns, the resource will be either in VALID or FAILED state. Calling
    an 'alloc function' followed by the matching 'init function' is fully
    equivalent with calling the 'make function' alone.

    Destruction can also happen as a two-step process. The 'uninit functions'
    will put a resource object from the VALID or FAILED state back into the
    ALLOC state:

        void sg_uninit_buffer(sg_buffer buf)
        void sg_uninit_image(sg_image img)
        void sg_uninit_sampler(sg_sampler smp)
        void sg_uninit_shader(sg_shader shd)
        void sg_uninit_pipeline(sg_pipeline pip)
        void sg_uninit_attachments(sg_attachments pass)

    Calling the 'uninit functions' with a resource that is not in the VALID or
    FAILED state is a no-op.

    To finally free the pool slot for recycling call the 'dealloc functions':

        void sg_dealloc_buffer(sg_buffer buf)
        void sg_dealloc_image(sg_image img)
        void sg_dealloc_sampler(sg_sampler smp)
        void sg_dealloc_shader(sg_shader shd)
        void sg_dealloc_pipeline(sg_pipeline pip)
        void sg_dealloc_attachments(sg_attachments atts)

    Calling the 'dealloc functions' on a resource that's not in ALLOC state is
    a no-op, but will generate a warning log message.

    Calling an 'uninit function' and 'dealloc function' in sequence is equivalent
    with calling the associated 'destroy function':

        void sg_destroy_buffer(sg_buffer buf)
        void sg_destroy_image(sg_image img)
        void sg_destroy_sampler(sg_sampler smp)
        void sg_destroy_shader(sg_shader shd)
        void sg_destroy_pipeline(sg_pipeline pip)
        void sg_destroy_attachments(sg_attachments atts)

    The 'destroy functions' can be called on resources in any state and generally
    do the right thing (for instance if the resource is in ALLOC state, the destroy
    function will be equivalent to the 'dealloc function' and skip the 'uninit part').

    And finally to close the circle, the 'fail functions' can be called to manually
    put a resource in ALLOC state into the FAILED state:

        sg_fail_buffer(sg_buffer buf)
        sg_fail_image(sg_image img)
        sg_fail_sampler(sg_sampler smp)
        sg_fail_shader(sg_shader shd)
        sg_fail_pipeline(sg_pipeline pip)
        sg_fail_attachments(sg_attachments atts)

    This is recommended if anything went wrong outside of sokol-gfx during asynchronous
    resource setup (for instance a file loading operation failed). In this case,
    the 'fail function' should be called instead of the 'init function'.

    Calling a 'fail function' on a resource that's not in ALLOC state is a no-op,
    but will generate a warning log message.

    NOTE: that two-step resource creation usually only makes sense for buffers
    and images, but not for samplers, shaders, pipelines or attachments. Most notably, trying
    to create a pipeline object with a shader that's not in VALID state will
    trigger a validation layer error, or if the validation layer is disabled,
    result in a pipeline object in FAILED state. Same when trying to create
    an attachments object with invalid image objects.


    WEBGPU CAVEATS
    ==============
    For a general overview and design notes of the WebGPU backend see:

        https://floooh.github.io/2023/10/16/sokol-webgpu.html

    In general, don't expect an automatic speedup when switching from the WebGL2
    backend to the WebGPU backend. Some WebGPU functions currently actually
    have a higher CPU overhead than similar WebGL2 functions, leading to the
    paradoxical situation that some WebGPU code may be slower than similar WebGL2
    code.

    - when writing WGSL shader code by hand, a specific bind-slot convention
      must be used:

      All uniform block structs must use `@group(0)` and bindings in the
      range 0..15

        @group(0) @binding(0..15)

      All textures, samplers and storage buffers must use `@group(1)` and
      bindings must be in the range 0..127:

        @group(1) @binding(0..127)

      Note that the number of texture, sampler and storage buffer bindings
      is still limited despite the large bind range:

        - up to 16 textures and sampler across all shader stages
        - up to 8 storage buffers across all shader stages

      If you use sokol-shdc to generate WGSL shader code, you don't need to worry
      about the above binding conventions since sokol-shdc.

    - The sokol-gfx WebGPU backend uses the sg_desc.uniform_buffer_size item
      to allocate a single per-frame uniform buffer which must be big enough
      to hold all data written by sg_apply_uniforms() during a single frame,
      including a worst-case 256-byte alignment (e.g. each sg_apply_uniform
      call will cost at least 256 bytes of uniform buffer size). The default size
      is 4 MB, which is enough for 16384 sg_apply_uniform() calls per
      frame (assuming the uniform data 'payload' is less than 256 bytes
      per call). These rules are the same as for the Metal backend, so if
      you are already using the Metal backend you'll be fine.

    - sg_apply_bindings(): the sokol-gfx WebGPU backend implements a bindgroup
      cache to prevent excessive creation and destruction of BindGroup objects
      when calling sg_apply_bindings(). The number of slots in the bindgroups
      cache is defined in sg_desc.wgpu_bindgroups_cache_size when calling
      sg_setup. The cache size must be a power-of-2 number, with the default being
      1024. The bindgroups cache behaviour can be observed by calling the new
      function sg_query_frame_stats(), where the following struct items are
      of interest:

        .wgpu.num_bindgroup_cache_hits
        .wgpu.num_bindgroup_cache_misses
        .wgpu.num_bindgroup_cache_collisions
        .wgpu_num_bindgroup_cache_invalidates
        .wgpu.num_bindgroup_cache_vs_hash_key_mismatch

      The value to pay attention to is `.wgpu.num_bindgroup_cache_collisions`,
      if this number is consistently higher than a few percent of the
      .wgpu.num_set_bindgroup value, it might be a good idea to bump the
      bindgroups cache size to the next power-of-2.

    - sg_apply_viewport(): WebGPU currently has a unique restriction that viewport
      rectangles must be contained entirely within the framebuffer. As a shitty
      workaround sokol_gfx.h will clip incoming viewport rectangles against
      the framebuffer, but this will distort the clipspace-to-screenspace mapping.
      There's no proper way to handle this inside sokol_gfx.h, this must be fixed
      in a future WebGPU update (see: https://github.com/gpuweb/gpuweb/issues/373
      and https://github.com/gpuweb/gpuweb/pull/5025)

    - The sokol shader compiler generally adds `diagnostic(off, derivative_uniformity);`
      into the WGSL output. Currently only the Chrome WebGPU implementation seems
      to recognize this.

    - Likewise, the following sokol-gfx pixel formats are not supported in WebGPU:
      R16, R16SN, RG16, RG16SN, RGBA16, RGBA16SN.
      Unlike unsupported vertex formats, unsupported pixel formats can be queried
      in cross-backend code via sg_query_pixel_format() though.

    - The Emscripten WebGPU shim currently doesn't support the Closure minification
      post-link-step (e.g. currently the emcc argument '--closure 1' or '--closure 2'
      will generate broken Javascript code.

    - sokol-gfx requires the WebGPU device feature `depth32float-stencil8` to be enabled
      (this should be widely supported)

    - sokol-gfx expects that the WebGPU device feature `float32-filterable` to *not* be
      enabled (since this would exclude all iOS devices)


    LICENSE
    =======
    zlib/libpng license

    Copyright (c) 2018 Andre Weissflog

    This software is provided 'as-is', without any express or implied warranty.
    In no event will the authors be held liable for any damages arising from the
    use of this software.

    Permission is granted to anyone to use this software for any purpose,
    including commercial applications, and to alter it and redistribute it
    freely, subject to the following restrictions:

        1. The origin of this software must not be misrepresented; you must not
        claim that you wrote the original software. If you use this software in a
        product, an acknowledgment in the product documentation would be
        appreciated but is not required.

        2. Altered source versions must be plainly marked as such, and must not
        be misrepresented as being the original software.

        3. This notice may not be removed or altered from any source
        distribution.

*/

#module_parameters(DEBUG := false, USE_GL := false, USE_DLL := false);

#scope_export;

#if OS == .WINDOWS {
    #if USE_DLL {
        #if USE_GL {
            #system_library,link_always "gdi32"; #system_library,link_always "dxguid"; #system_library,link_always "user32"; #system_library,link_always "shell32";
            #if  DEBUG { sokol_gfx_clib :: #library "sokol_gfx_windows_x64_gl_debug";   }
            else       { sokol_gfx_clib :: #library "sokol_gfx_windows_x64_gl_release"; }
        } else {
            #system_library,link_always "gdi32"; #system_library,link_always "dxguid"; #system_library,link_always "user32"; #system_library,link_always "shell32"; #system_library,link_always "d3d11";
            #if  DEBUG { sokol_gfx_clib :: #library "sokol_gfx_windows_x64_d3d11_debug";   }
            else       { sokol_gfx_clib :: #library "sokol_gfx_windows_x64_d3d11_release"; }
        }
    } else {
        #if USE_GL {
            #system_library,link_always "gdi32"; #system_library,link_always "dxguid"; #system_library,link_always "user32"; #system_library,link_always "shell32";
            #if  DEBUG { sokol_gfx_clib :: #library,no_dll "sokol_gfx_windows_x64_gl_debug";   }
            else       { sokol_gfx_clib :: #library,no_dll "sokol_gfx_windows_x64_gl_release"; }
        } else {
            #system_library,link_always "gdi32"; #system_library,link_always "dxguid"; #system_library,link_always "user32"; #system_library,link_always "shell32"; #system_library,link_always "d3d11";
            #if  DEBUG { sokol_gfx_clib :: #library,no_dll "sokol_gfx_windows_x64_d3d11_debug";   }
            else       { sokol_gfx_clib :: #library,no_dll "sokol_gfx_windows_x64_d3d11_release"; }
        }
    }
}
else #if OS == .MACOS {
    #if USE_DLL {
             #if  USE_GL && CPU == .ARM64 &&  DEBUG { sokol_gfx_clib :: #library "../dylib/sokol_dylib_macos_arm64_gl_debug.dylib"; }
        else #if  USE_GL && CPU == .ARM64 && !DEBUG { sokol_gfx_clib :: #library "../dylib/sokol_dylib_macos_arm64_gl_release.dylib"; }
        else #if  USE_GL && CPU == .X64   &&  DEBUG { sokol_gfx_clib :: #library "../dylib/sokol_dylib_macos_x64_gl_debug.dylib"; }
        else #if  USE_GL && CPU == .X64   && !DEBUG { sokol_gfx_clib :: #library "../dylib/sokol_dylib_macos_x64_gl_release.dylib"; }
        else #if !USE_GL && CPU == .ARM64 &&  DEBUG { sokol_gfx_clib :: #library "../dylib/sokol_dylib_macos_arm64_metal_debug.dylib"; }
        else #if !USE_GL && CPU == .ARM64 && !DEBUG { sokol_gfx_clib :: #library "../dylib/sokol_dylib_macos_arm64_metal_release.dylib"; }
        else #if !USE_GL && CPU == .X64   &&  DEBUG { sokol_gfx_clib :: #library "../dylib/sokol_dylib_macos_x64_metal_debug.dylib"; }
        else #if !USE_GL && CPU == .X64   && !DEBUG { sokol_gfx_clib :: #library "../dylib/sokol_dylib_macos_x64_metal_release.dylib"; }
    } else {
        #if USE_GL {
            #system_library,link_always "Cocoa"; #system_library,link_always "QuartzCore"; #system_library,link_always "OpenGL";
            #if CPU == .ARM64 {
                #if  DEBUG { sokol_gfx_clib :: #library,no_dll "sokol_gfx_macos_arm64_gl_debug";   }
                else       { sokol_gfx_clib :: #library,no_dll "sokol_gfx_macos_arm64_gl_release"; }
            } else {
                #if  DEBUG { sokol_gfx_clib :: #library,no_dll "sokol_gfx_macos_x64_gl_debug";   }
                else       { sokol_gfx_clib :: #library,no_dll "sokol_gfx_macos_x64_gl_release"; }
            }
        } else {
            #library,link_always "../../libclang_rt.osx"; #system_library,link_always "Cocoa"; #system_library,link_always "QuartzCore"; #system_library,link_always "Metal"; #system_library,link_always "MetalKit";
            #if CPU == .ARM64 {
                #if  DEBUG { sokol_gfx_clib :: #library,no_dll "sokol_gfx_macos_arm64_metal_debug";   }
                else       { sokol_gfx_clib :: #library,no_dll "sokol_gfx_macos_arm64_metal_release"; }
            } else {
                #if  DEBUG { sokol_gfx_clib :: #library,no_dll "sokol_gfx_macos_x64_metal_debug";   }
                else       { sokol_gfx_clib :: #library,no_dll "sokol_gfx_macos_x64_metal_release"; }
            }
        }
    }
} else #if OS == .LINUX {
    #system_library,link_always "libXcursor"; #system_library,link_always "libX11"; #system_library,link_always "libXi"; #system_library,link_always "libGL";
    #if  DEBUG { sokol_gfx_clib :: #library,no_dll "sokol_gfx_linux_x64_gl_debug";   }
    else       { sokol_gfx_clib :: #library,no_dll "sokol_gfx_linux_x64_gl_release"; }
} else #if OS == .WASM {
    #if  DEBUG { sokol_gfx_clib :: #library,no_dll "sokol_gfx_wasm_gl_debug";   }
    else       { sokol_gfx_clib :: #library,no_dll "sokol_gfx_wasm_gl_release"; }
} else {
    log_error("This OS is currently not supported");
}

// setup and misc functions
sg_setup :: (desc: *sg_desc) -> void #foreign sokol_gfx_clib;
sg_shutdown :: () -> void #foreign sokol_gfx_clib;
sg_isvalid :: () -> bool #foreign sokol_gfx_clib;
sg_reset_state_cache :: () -> void #foreign sokol_gfx_clib;
sg_install_trace_hooks :: (trace_hooks: *sg_trace_hooks) -> sg_trace_hooks #foreign sokol_gfx_clib;
sg_push_debug_group :: (name: *u8) -> void #foreign sokol_gfx_clib;
sg_pop_debug_group :: () -> void #foreign sokol_gfx_clib;
sg_add_commit_listener :: (listener: sg_commit_listener) -> bool #foreign sokol_gfx_clib;
sg_remove_commit_listener :: (listener: sg_commit_listener) -> bool #foreign sokol_gfx_clib;
// resource creation, destruction and updating
sg_make_buffer :: (desc: *sg_buffer_desc) -> sg_buffer #foreign sokol_gfx_clib;
sg_make_image :: (desc: *sg_image_desc) -> sg_image #foreign sokol_gfx_clib;
sg_make_sampler :: (desc: *sg_sampler_desc) -> sg_sampler #foreign sokol_gfx_clib;
sg_make_shader :: (desc: *sg_shader_desc) -> sg_shader #foreign sokol_gfx_clib;
sg_make_pipeline :: (desc: *sg_pipeline_desc) -> sg_pipeline #foreign sokol_gfx_clib;
sg_make_attachments :: (desc: *sg_attachments_desc) -> sg_attachments #foreign sokol_gfx_clib;
sg_destroy_buffer :: (buf: sg_buffer) -> void #foreign sokol_gfx_clib;
sg_destroy_image :: (img: sg_image) -> void #foreign sokol_gfx_clib;
sg_destroy_sampler :: (smp: sg_sampler) -> void #foreign sokol_gfx_clib;
sg_destroy_shader :: (shd: sg_shader) -> void #foreign sokol_gfx_clib;
sg_destroy_pipeline :: (pip: sg_pipeline) -> void #foreign sokol_gfx_clib;
sg_destroy_attachments :: (atts: sg_attachments) -> void #foreign sokol_gfx_clib;
sg_update_buffer :: (buf: sg_buffer, data: *sg_range) -> void #foreign sokol_gfx_clib;
sg_update_image :: (img: sg_image, data: *sg_image_data) -> void #foreign sokol_gfx_clib;
sg_append_buffer :: (buf: sg_buffer, data: *sg_range) -> s32 #foreign sokol_gfx_clib;
sg_query_buffer_overflow :: (buf: sg_buffer) -> bool #foreign sokol_gfx_clib;
sg_query_buffer_will_overflow :: (buf: sg_buffer, size: u64) -> bool #foreign sokol_gfx_clib;
// render and compute functions
sg_begin_pass :: (pass: *sg_pass) -> void #foreign sokol_gfx_clib;
sg_apply_viewport :: (x: s32, y: s32, width: s32, height: s32, origin_top_left: bool) -> void #foreign sokol_gfx_clib;
sg_apply_viewportf :: (x: float, y: float, width: float, height: float, origin_top_left: bool) -> void #foreign sokol_gfx_clib;
sg_apply_scissor_rect :: (x: s32, y: s32, width: s32, height: s32, origin_top_left: bool) -> void #foreign sokol_gfx_clib;
sg_apply_scissor_rectf :: (x: float, y: float, width: float, height: float, origin_top_left: bool) -> void #foreign sokol_gfx_clib;
sg_apply_pipeline :: (pip: sg_pipeline) -> void #foreign sokol_gfx_clib;
sg_apply_bindings :: (bindings: *sg_bindings) -> void #foreign sokol_gfx_clib;
sg_apply_uniforms :: (ub_slot: s32, data: *sg_range) -> void #foreign sokol_gfx_clib;
sg_draw :: (base_element: s32, num_elements: s32, num_instances: s32) -> void #foreign sokol_gfx_clib;
sg_dispatch :: (num_groups_x: s32, num_groups_y: s32, num_groups_z: s32) -> void #foreign sokol_gfx_clib;
sg_end_pass :: () -> void #foreign sokol_gfx_clib;
sg_commit :: () -> void #foreign sokol_gfx_clib;
// getting information
sg_query_desc :: () -> sg_desc #foreign sokol_gfx_clib;
sg_query_backend :: () -> sg_backend #foreign sokol_gfx_clib;
sg_query_features :: () -> sg_features #foreign sokol_gfx_clib;
sg_query_limits :: () -> sg_limits #foreign sokol_gfx_clib;
sg_query_pixelformat :: (fmt: sg_pixel_format) -> sg_pixelformat_info #foreign sokol_gfx_clib;
sg_query_row_pitch :: (fmt: sg_pixel_format, width: s32, row_align_bytes: s32) -> s32 #foreign sokol_gfx_clib;
sg_query_surface_pitch :: (fmt: sg_pixel_format, width: s32, height: s32, row_align_bytes: s32) -> s32 #foreign sokol_gfx_clib;
// get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID)
sg_query_buffer_state :: (buf: sg_buffer) -> sg_resource_state #foreign sokol_gfx_clib;
sg_query_image_state :: (img: sg_image) -> sg_resource_state #foreign sokol_gfx_clib;
sg_query_sampler_state :: (smp: sg_sampler) -> sg_resource_state #foreign sokol_gfx_clib;
sg_query_shader_state :: (shd: sg_shader) -> sg_resource_state #foreign sokol_gfx_clib;
sg_query_pipeline_state :: (pip: sg_pipeline) -> sg_resource_state #foreign sokol_gfx_clib;
sg_query_attachments_state :: (atts: sg_attachments) -> sg_resource_state #foreign sokol_gfx_clib;
// get runtime information about a resource
sg_query_buffer_info :: (buf: sg_buffer) -> sg_buffer_info #foreign sokol_gfx_clib;
sg_query_image_info :: (img: sg_image) -> sg_image_info #foreign sokol_gfx_clib;
sg_query_sampler_info :: (smp: sg_sampler) -> sg_sampler_info #foreign sokol_gfx_clib;
sg_query_shader_info :: (shd: sg_shader) -> sg_shader_info #foreign sokol_gfx_clib;
sg_query_pipeline_info :: (pip: sg_pipeline) -> sg_pipeline_info #foreign sokol_gfx_clib;
sg_query_attachments_info :: (atts: sg_attachments) -> sg_attachments_info #foreign sokol_gfx_clib;
// get desc structs matching a specific resource (NOTE that not all creation attributes may be provided)
sg_query_buffer_desc :: (buf: sg_buffer) -> sg_buffer_desc #foreign sokol_gfx_clib;
sg_query_image_desc :: (img: sg_image) -> sg_image_desc #foreign sokol_gfx_clib;
sg_query_sampler_desc :: (smp: sg_sampler) -> sg_sampler_desc #foreign sokol_gfx_clib;
sg_query_shader_desc :: (shd: sg_shader) -> sg_shader_desc #foreign sokol_gfx_clib;
sg_query_pipeline_desc :: (pip: sg_pipeline) -> sg_pipeline_desc #foreign sokol_gfx_clib;
sg_query_attachments_desc :: (atts: sg_attachments) -> sg_attachments_desc #foreign sokol_gfx_clib;
// get resource creation desc struct with their default values replaced
sg_query_buffer_defaults :: (desc: *sg_buffer_desc) -> sg_buffer_desc #foreign sokol_gfx_clib;
sg_query_image_defaults :: (desc: *sg_image_desc) -> sg_image_desc #foreign sokol_gfx_clib;
sg_query_sampler_defaults :: (desc: *sg_sampler_desc) -> sg_sampler_desc #foreign sokol_gfx_clib;
sg_query_shader_defaults :: (desc: *sg_shader_desc) -> sg_shader_desc #foreign sokol_gfx_clib;
sg_query_pipeline_defaults :: (desc: *sg_pipeline_desc) -> sg_pipeline_desc #foreign sokol_gfx_clib;
sg_query_attachments_defaults :: (desc: *sg_attachments_desc) -> sg_attachments_desc #foreign sokol_gfx_clib;
// assorted query functions
sg_query_buffer_size :: (buf: sg_buffer) -> u64 #foreign sokol_gfx_clib;
sg_query_buffer_type :: (buf: sg_buffer) -> sg_buffer_type #foreign sokol_gfx_clib;
sg_query_buffer_usage :: (buf: sg_buffer) -> sg_usage #foreign sokol_gfx_clib;
sg_query_image_type :: (img: sg_image) -> sg_image_type #foreign sokol_gfx_clib;
sg_query_image_width :: (img: sg_image) -> s32 #foreign sokol_gfx_clib;
sg_query_image_height :: (img: sg_image) -> s32 #foreign sokol_gfx_clib;
sg_query_image_num_slices :: (img: sg_image) -> s32 #foreign sokol_gfx_clib;
sg_query_image_num_mipmaps :: (img: sg_image) -> s32 #foreign sokol_gfx_clib;
sg_query_image_pixelformat :: (img: sg_image) -> sg_pixel_format #foreign sokol_gfx_clib;
sg_query_image_usage :: (img: sg_image) -> sg_usage #foreign sokol_gfx_clib;
sg_query_image_sample_count :: (img: sg_image) -> s32 #foreign sokol_gfx_clib;
// separate resource allocation and initialization (for async setup)
sg_alloc_buffer :: () -> sg_buffer #foreign sokol_gfx_clib;
sg_alloc_image :: () -> sg_image #foreign sokol_gfx_clib;
sg_alloc_sampler :: () -> sg_sampler #foreign sokol_gfx_clib;
sg_alloc_shader :: () -> sg_shader #foreign sokol_gfx_clib;
sg_alloc_pipeline :: () -> sg_pipeline #foreign sokol_gfx_clib;
sg_alloc_attachments :: () -> sg_attachments #foreign sokol_gfx_clib;
sg_dealloc_buffer :: (buf: sg_buffer) -> void #foreign sokol_gfx_clib;
sg_dealloc_image :: (img: sg_image) -> void #foreign sokol_gfx_clib;
sg_dealloc_sampler :: (smp: sg_sampler) -> void #foreign sokol_gfx_clib;
sg_dealloc_shader :: (shd: sg_shader) -> void #foreign sokol_gfx_clib;
sg_dealloc_pipeline :: (pip: sg_pipeline) -> void #foreign sokol_gfx_clib;
sg_dealloc_attachments :: (attachments: sg_attachments) -> void #foreign sokol_gfx_clib;
sg_init_buffer :: (buf: sg_buffer, desc: *sg_buffer_desc) -> void #foreign sokol_gfx_clib;
sg_init_image :: (img: sg_image, desc: *sg_image_desc) -> void #foreign sokol_gfx_clib;
sg_init_sampler :: (smg: sg_sampler, desc: *sg_sampler_desc) -> void #foreign sokol_gfx_clib;
sg_init_shader :: (shd: sg_shader, desc: *sg_shader_desc) -> void #foreign sokol_gfx_clib;
sg_init_pipeline :: (pip: sg_pipeline, desc: *sg_pipeline_desc) -> void #foreign sokol_gfx_clib;
sg_init_attachments :: (attachments: sg_attachments, desc: *sg_attachments_desc) -> void #foreign sokol_gfx_clib;
sg_uninit_buffer :: (buf: sg_buffer) -> void #foreign sokol_gfx_clib;
sg_uninit_image :: (img: sg_image) -> void #foreign sokol_gfx_clib;
sg_uninit_sampler :: (smp: sg_sampler) -> void #foreign sokol_gfx_clib;
sg_uninit_shader :: (shd: sg_shader) -> void #foreign sokol_gfx_clib;
sg_uninit_pipeline :: (pip: sg_pipeline) -> void #foreign sokol_gfx_clib;
sg_uninit_attachments :: (atts: sg_attachments) -> void #foreign sokol_gfx_clib;
sg_fail_buffer :: (buf: sg_buffer) -> void #foreign sokol_gfx_clib;
sg_fail_image :: (img: sg_image) -> void #foreign sokol_gfx_clib;
sg_fail_sampler :: (smp: sg_sampler) -> void #foreign sokol_gfx_clib;
sg_fail_shader :: (shd: sg_shader) -> void #foreign sokol_gfx_clib;
sg_fail_pipeline :: (pip: sg_pipeline) -> void #foreign sokol_gfx_clib;
sg_fail_attachments :: (atts: sg_attachments) -> void #foreign sokol_gfx_clib;
// frame stats
sg_enable_frame_stats :: () -> void #foreign sokol_gfx_clib;
sg_disable_frame_stats :: () -> void #foreign sokol_gfx_clib;
sg_frame_stats_enabled :: () -> bool #foreign sokol_gfx_clib;
sg_query_frame_stats :: () -> sg_frame_stats #foreign sokol_gfx_clib;
// D3D11: return ID3D11Device
sg_d3d11_device :: () -> *void #foreign sokol_gfx_clib;
// D3D11: return ID3D11DeviceContext
sg_d3d11_device_context :: () -> *void #foreign sokol_gfx_clib;
// D3D11: get internal buffer resource objects
sg_d3d11_query_buffer_info :: (buf: sg_buffer) -> sg_d3d11_buffer_info #foreign sokol_gfx_clib;
// D3D11: get internal image resource objects
sg_d3d11_query_image_info :: (img: sg_image) -> sg_d3d11_image_info #foreign sokol_gfx_clib;
// D3D11: get internal sampler resource objects
sg_d3d11_query_sampler_info :: (smp: sg_sampler) -> sg_d3d11_sampler_info #foreign sokol_gfx_clib;
// D3D11: get internal shader resource objects
sg_d3d11_query_shader_info :: (shd: sg_shader) -> sg_d3d11_shader_info #foreign sokol_gfx_clib;
// D3D11: get internal pipeline resource objects
sg_d3d11_query_pipeline_info :: (pip: sg_pipeline) -> sg_d3d11_pipeline_info #foreign sokol_gfx_clib;
// D3D11: get internal pass resource objects
sg_d3d11_query_attachments_info :: (atts: sg_attachments) -> sg_d3d11_attachments_info #foreign sokol_gfx_clib;
// Metal: return __bridge-casted MTLDevice
sg_mtl_device :: () -> *void #foreign sokol_gfx_clib;
// Metal: return __bridge-casted MTLRenderCommandEncoder when inside render pass (otherwise zero)
sg_mtl_render_command_encoder :: () -> *void #foreign sokol_gfx_clib;
// Metal: return __bridge-casted MTLComputeCommandEncoder when inside compute pass (otherwise zero)
sg_mtl_compute_command_encoder :: () -> *void #foreign sokol_gfx_clib;
// Metal: get internal __bridge-casted buffer resource objects
sg_mtl_query_buffer_info :: (buf: sg_buffer) -> sg_mtl_buffer_info #foreign sokol_gfx_clib;
// Metal: get internal __bridge-casted image resource objects
sg_mtl_query_image_info :: (img: sg_image) -> sg_mtl_image_info #foreign sokol_gfx_clib;
// Metal: get internal __bridge-casted sampler resource objects
sg_mtl_query_sampler_info :: (smp: sg_sampler) -> sg_mtl_sampler_info #foreign sokol_gfx_clib;
// Metal: get internal __bridge-casted shader resource objects
sg_mtl_query_shader_info :: (shd: sg_shader) -> sg_mtl_shader_info #foreign sokol_gfx_clib;
// Metal: get internal __bridge-casted pipeline resource objects
sg_mtl_query_pipeline_info :: (pip: sg_pipeline) -> sg_mtl_pipeline_info #foreign sokol_gfx_clib;
// WebGPU: return WGPUDevice object
sg_wgpu_device :: () -> *void #foreign sokol_gfx_clib;
// WebGPU: return WGPUQueue object
sg_wgpu_queue :: () -> *void #foreign sokol_gfx_clib;
// WebGPU: return this frame's WGPUCommandEncoder
sg_wgpu_command_encoder :: () -> *void #foreign sokol_gfx_clib;
// WebGPU: return WGPURenderPassEncoder of current pass (returns 0 when outside pass or in a compute pass)
sg_wgpu_render_pass_encoder :: () -> *void #foreign sokol_gfx_clib;
// WebGPU: return WGPUComputePassEncoder of current pass (returns 0 when outside pass or in a render pass)
sg_wgpu_compute_pass_encoder :: () -> *void #foreign sokol_gfx_clib;
// WebGPU: get internal buffer resource objects
sg_wgpu_query_buffer_info :: (buf: sg_buffer) -> sg_wgpu_buffer_info #foreign sokol_gfx_clib;
// WebGPU: get internal image resource objects
sg_wgpu_query_image_info :: (img: sg_image) -> sg_wgpu_image_info #foreign sokol_gfx_clib;
// WebGPU: get internal sampler resource objects
sg_wgpu_query_sampler_info :: (smp: sg_sampler) -> sg_wgpu_sampler_info #foreign sokol_gfx_clib;
// WebGPU: get internal shader resource objects
sg_wgpu_query_shader_info :: (shd: sg_shader) -> sg_wgpu_shader_info #foreign sokol_gfx_clib;
// WebGPU: get internal pipeline resource objects
sg_wgpu_query_pipeline_info :: (pip: sg_pipeline) -> sg_wgpu_pipeline_info #foreign sokol_gfx_clib;
// WebGPU: get internal pass resource objects
sg_wgpu_query_attachments_info :: (atts: sg_attachments) -> sg_wgpu_attachments_info #foreign sokol_gfx_clib;
// GL: get internal buffer resource objects
sg_gl_query_buffer_info :: (buf: sg_buffer) -> sg_gl_buffer_info #foreign sokol_gfx_clib;
// GL: get internal image resource objects
sg_gl_query_image_info :: (img: sg_image) -> sg_gl_image_info #foreign sokol_gfx_clib;
// GL: get internal sampler resource objects
sg_gl_query_sampler_info :: (smp: sg_sampler) -> sg_gl_sampler_info #foreign sokol_gfx_clib;
// GL: get internal shader resource objects
sg_gl_query_shader_info :: (shd: sg_shader) -> sg_gl_shader_info #foreign sokol_gfx_clib;
// GL: get internal pass resource objects
sg_gl_query_attachments_info :: (atts: sg_attachments) -> sg_gl_attachments_info #foreign sokol_gfx_clib;

sg_buffer :: struct {
    id : u32;
}

sg_image :: struct {
    id : u32;
}

sg_sampler :: struct {
    id : u32;
}

sg_shader :: struct {
    id : u32;
}

sg_pipeline :: struct {
    id : u32;
}

sg_attachments :: struct {
    id : u32;
}

sg_range :: struct {
    ptr : *void;
    size : u64;
}

INVALID_ID :: 0;
NUM_INFLIGHT_FRAMES :: 2;
MAX_COLOR_ATTACHMENTS :: 4;
MAX_UNIFORMBLOCK_MEMBERS :: 16;
MAX_VERTEX_ATTRIBUTES :: 16;
MAX_MIPMAPS :: 16;
MAX_TEXTUREARRAY_LAYERS :: 128;
MAX_UNIFORMBLOCK_BINDSLOTS :: 8;
MAX_VERTEXBUFFER_BINDSLOTS :: 8;
MAX_IMAGE_BINDSLOTS :: 16;
MAX_SAMPLER_BINDSLOTS :: 16;
MAX_STORAGEBUFFER_BINDSLOTS :: 8;
MAX_IMAGE_SAMPLER_PAIRS :: 16;

sg_color :: struct {
    r : float;
    g : float;
    b : float;
    a : float;
}

/*
    sg_backend

    The active 3D-API backend, use the function sg_query_backend()
    to get the currently active backend.
*/
sg_backend :: enum u32 {
    GLCORE;
    GLES3;
    D3D11;
    METAL_IOS;
    METAL_MACOS;
    METAL_SIMULATOR;
    WGPU;
    DUMMY;
}

/*
    sg_pixel_format

    sokol_gfx.h basically uses the same pixel formats as WebGPU, since these
    are supported on most newer GPUs.

    A pixelformat name consist of three parts:

        - components (R, RG, RGB or RGBA)
        - bit width per component (8, 16 or 32)
        - component data type:
            - unsigned normalized (no postfix)
            - signed normalized (SN postfix)
            - unsigned integer (UI postfix)
            - signed integer (SI postfix)
            - float (F postfix)

    Not all pixel formats can be used for everything, call sg_query_pixelformat()
    to inspect the capabilities of a given pixelformat. The function returns
    an sg_pixelformat_info struct with the following members:

        - sample: the pixelformat can be sampled as texture at least with
                  nearest filtering
        - filter: the pixelformat can be sampled as texture with linear
                  filtering
        - render: the pixelformat can be used as render-pass attachment
        - blend:  blending is supported when used as render-pass attachment
        - msaa:   multisample-antialiasing is supported when used
                  as render-pass attachment
        - depth:  the pixelformat can be used for depth-stencil attachments
        - compressed: this is a block-compressed format
        - bytes_per_pixel: the numbers of bytes in a pixel (0 for compressed formats)

    The default pixel format for texture images is SG_PIXELFORMAT_RGBA8.

    The default pixel format for render target images is platform-dependent
    and taken from the sg_environment struct passed into sg_setup(). Typically
    the default formats are:

        - for the Metal, D3D11 and WebGPU backends: SG_PIXELFORMAT_BGRA8
        - for GL backends: SG_PIXELFORMAT_RGBA8
*/
sg_pixel_format :: enum u32 {
    DEFAULT;
    NONE;
    R8;
    R8SN;
    R8UI;
    R8SI;
    R16;
    R16SN;
    R16UI;
    R16SI;
    R16F;
    RG8;
    RG8SN;
    RG8UI;
    RG8SI;
    R32UI;
    R32SI;
    R32F;
    RG16;
    RG16SN;
    RG16UI;
    RG16SI;
    RG16F;
    RGBA8;
    SRGB8A8;
    RGBA8SN;
    RGBA8UI;
    RGBA8SI;
    BGRA8;
    RGB10A2;
    RG11B10F;
    RGB9E5;
    RG32UI;
    RG32SI;
    RG32F;
    RGBA16;
    RGBA16SN;
    RGBA16UI;
    RGBA16SI;
    RGBA16F;
    RGBA32UI;
    RGBA32SI;
    RGBA32F;
    DEPTH;
    DEPTH_STENCIL;
    BC1_RGBA;
    BC2_RGBA;
    BC3_RGBA;
    BC3_SRGBA;
    BC4_R;
    BC4_RSN;
    BC5_RG;
    BC5_RGSN;
    BC6H_RGBF;
    BC6H_RGBUF;
    BC7_RGBA;
    BC7_SRGBA;
    ETC2_RGB8;
    ETC2_SRGB8;
    ETC2_RGB8A1;
    ETC2_RGBA8;
    ETC2_SRGB8A8;
    EAC_R11;
    EAC_R11SN;
    EAC_RG11;
    EAC_RG11SN;
    ASTC_4x4_RGBA;
    ASTC_4x4_SRGBA;
}

sg_pixelformat_info :: struct {
    sample : bool;
    filter : bool;
    render : bool;
    blend : bool;
    msaa : bool;
    depth : bool;
    compressed : bool;
    bytes_per_pixel : s32;
}

sg_features :: struct {
    origin_top_left : bool;
    image_clamp_to_border : bool;
    mrt_independent_blend_state : bool;
    mrt_independent_write_mask : bool;
    compute : bool;
    msaa_image_bindings : bool;
}

sg_limits :: struct {
    max_image_size_2d : s32;
    max_image_size_cube : s32;
    max_image_size_3d : s32;
    max_image_size_array : s32;
    max_image_array_layers : s32;
    max_vertex_attrs : s32;
    gl_max_vertex_uniform_components : s32;
    gl_max_combined_texture_image_units : s32;
}

/*
    sg_resource_state

    The current state of a resource in its resource pool.
    Resources start in the INITIAL state, which means the
    pool slot is unoccupied and can be allocated. When a resource is
    created, first an id is allocated, and the resource pool slot
    is set to state ALLOC. After allocation, the resource is
    initialized, which may result in the VALID or FAILED state. The
    reason why allocation and initialization are separate is because
    some resource types (e.g. buffers and images) might be asynchronously
    initialized by the user application. If a resource which is not
    in the VALID state is attempted to be used for rendering, rendering
    operations will silently be dropped.

    The special INVALID state is returned in sg_query_xxx_state() if no
    resource object exists for the provided resource id.
*/
sg_resource_state :: enum u32 {
    INITIAL;
    ALLOC;
    VALID;
    FAILED;
    INVALID;
}

/*
    sg_usage

    A resource usage hint describing the update strategy of
    buffers and images. This is used in the sg_buffer_desc.usage
    and sg_image_desc.usage members when creating buffers
    and images:

    SG_USAGE_IMMUTABLE:     the resource will never be updated with
                            new (CPU-side) data, instead the content of the
                            resource must be provided on creation
    SG_USAGE_DYNAMIC:       the resource will be updated infrequently
                            with new data (this could range from "once
                            after creation", to "quite often but not
                            every frame")
    SG_USAGE_STREAM:        the resource will be updated each frame
                            with new content

    The rendering backends use this hint to prevent that the
    CPU needs to wait for the GPU when attempting to update
    a resource that might be currently accessed by the GPU.

    Resource content is updated with the functions sg_update_buffer() or
    sg_append_buffer() for buffer objects, and sg_update_image() for image
    objects. For the sg_update_*() functions, only one update is allowed per
    frame and resource object, while sg_append_buffer() can be called
    multiple times per frame on the same buffer. The application must update
    all data required for rendering (this means that the update data can be
    smaller than the resource size, if only a part of the overall resource
    size is used for rendering, you only need to make sure that the data that
    *is* used is valid).

    The default usage is SG_USAGE_IMMUTABLE.
*/
sg_usage :: enum u32 {
    DEFAULT;
    IMMUTABLE;
    DYNAMIC;
    STREAM;
}

/*
    sg_buffer_type

    Indicates whether a buffer will be bound as vertex-,
    index- or storage-buffer.

    Used in the sg_buffer_desc.type member when creating a buffer.

    The default value is SG_BUFFERTYPE_VERTEXBUFFER.
*/
sg_buffer_type :: enum u32 {
    DEFAULT;
    VERTEXBUFFER;
    INDEXBUFFER;
    STORAGEBUFFER;
}

/*
    sg_index_type

    Indicates whether indexed rendering (fetching vertex-indices from an
    index buffer) is used, and if yes, the index data type (16- or 32-bits).

    This is used in the sg_pipeline_desc.index_type member when creating a
    pipeline object.

    The default index type is SG_INDEXTYPE_NONE.
*/
sg_index_type :: enum u32 {
    DEFAULT;
    NONE;
    UINT16;
    UINT32;
}

/*
    sg_image_type

    Indicates the basic type of an image object (2D-texture, cubemap,
    3D-texture or 2D-array-texture). Used in the sg_image_desc.type member when
    creating an image, and in sg_shader_image_desc to describe a sampled texture
    in the shader (both must match and will be checked in the validation layer
    when calling sg_apply_bindings).

    The default image type when creating an image is SG_IMAGETYPE_2D.
*/
sg_image_type :: enum u32 {
    DEFAULT;
    _2D;
    CUBE;
    _3D;
    ARRAY;
}

/*
    sg_image_sample_type

    The basic data type of a texture sample as expected by a shader.
    Must be provided in sg_shader_image and used by the validation
    layer in sg_apply_bindings() to check if the provided image object
    is compatible with what the shader expects. Apart from the sokol-gfx
    validation layer, WebGPU is the only backend API which actually requires
    matching texture and sampler type to be provided upfront for validation
    (other 3D APIs treat texture/sampler type mismatches as undefined behaviour).

    NOTE that the following texture pixel formats require the use
    of SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT, combined with a sampler
    of type SG_SAMPLERTYPE_NONFILTERING:

    - SG_PIXELFORMAT_R32F
    - SG_PIXELFORMAT_RG32F
    - SG_PIXELFORMAT_RGBA32F

    (when using sokol-shdc, also check out the meta tags `@image_sample_type`
    and `@sampler_type`)
*/
sg_image_sample_type :: enum u32 {
    DEFAULT;
    FLOAT;
    DEPTH;
    SINT;
    UINT;
    UNFILTERABLE_FLOAT;
}

/*
    sg_sampler_type

    The basic type of a texture sampler (sampling vs comparison) as
    defined in a shader. Must be provided in sg_shader_sampler_desc.

    sg_image_sample_type and sg_sampler_type for a texture/sampler
    pair must be compatible with each other, specifically only
    the following pairs are allowed:

    - SG_IMAGESAMPLETYPE_FLOAT => (SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING)
    - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT => SG_SAMPLERTYPE_NONFILTERING
    - SG_IMAGESAMPLETYPE_SINT => SG_SAMPLERTYPE_NONFILTERING
    - SG_IMAGESAMPLETYPE_UINT => SG_SAMPLERTYPE_NONFILTERING
    - SG_IMAGESAMPLETYPE_DEPTH => SG_SAMPLERTYPE_COMPARISON
*/
sg_sampler_type :: enum u32 {
    DEFAULT;
    FILTERING;
    NONFILTERING;
    COMPARISON;
}

/*
    sg_cube_face

    The cubemap faces. Use these as indices in the sg_image_desc.content
    array.
*/
sg_cube_face :: enum u32 {
    POS_X;
    NEG_X;
    POS_Y;
    NEG_Y;
    POS_Z;
    NEG_Z;
}

/*
    sg_primitive_type

    This is the common subset of 3D primitive types supported across all 3D
    APIs. This is used in the sg_pipeline_desc.primitive_type member when
    creating a pipeline object.

    The default primitive type is SG_PRIMITIVETYPE_TRIANGLES.
*/
sg_primitive_type :: enum u32 {
    DEFAULT;
    POINTS;
    LINES;
    LINE_STRIP;
    TRIANGLES;
    TRIANGLE_STRIP;
}

/*
    sg_filter

    The filtering mode when sampling a texture image. This is
    used in the sg_sampler_desc.min_filter, sg_sampler_desc.mag_filter
    and sg_sampler_desc.mipmap_filter members when creating a sampler object.

    For the default is SG_FILTER_NEAREST.
*/
sg_filter :: enum u32 {
    DEFAULT;
    NEAREST;
    LINEAR;
}

/*
    sg_wrap

    The texture coordinates wrapping mode when sampling a texture
    image. This is used in the sg_image_desc.wrap_u, .wrap_v
    and .wrap_w members when creating an image.

    The default wrap mode is SG_WRAP_REPEAT.

    NOTE: SG_WRAP_CLAMP_TO_BORDER is not supported on all backends
    and platforms. To check for support, call sg_query_features()
    and check the "clamp_to_border" boolean in the returned
    sg_features struct.

    Platforms which don't support SG_WRAP_CLAMP_TO_BORDER will silently fall back
    to SG_WRAP_CLAMP_TO_EDGE without a validation error.
*/
sg_wrap :: enum u32 {
    DEFAULT;
    REPEAT;
    CLAMP_TO_EDGE;
    CLAMP_TO_BORDER;
    MIRRORED_REPEAT;
}

/*
    sg_border_color

    The border color to use when sampling a texture, and the UV wrap
    mode is SG_WRAP_CLAMP_TO_BORDER.

    The default border color is SG_BORDERCOLOR_OPAQUE_BLACK
*/
sg_border_color :: enum u32 {
    DEFAULT;
    TRANSPARENT_BLACK;
    OPAQUE_BLACK;
    OPAQUE_WHITE;
}

/*
    sg_vertex_format

    The data type of a vertex component. This is used to describe
    the layout of input vertex data when creating a pipeline object.

    NOTE that specific mapping rules exist from the CPU-side vertex
    formats to the vertex attribute base type in the vertex shader code
    (see doc header section 'ON VERTEX FORMATS').
*/
sg_vertex_format :: enum u32 {
    INVALID;
    FLOAT;
    FLOAT2;
    FLOAT3;
    FLOAT4;
    INT;
    INT2;
    INT3;
    INT4;
    UINT;
    UINT2;
    UINT3;
    UINT4;
    BYTE4;
    BYTE4N;
    UBYTE4;
    UBYTE4N;
    SHORT2;
    SHORT2N;
    USHORT2;
    USHORT2N;
    SHORT4;
    SHORT4N;
    USHORT4;
    USHORT4N;
    UINT10_N2;
    HALF2;
    HALF4;
}

/*
    sg_vertex_step

    Defines whether the input pointer of a vertex input stream is advanced
    'per vertex' or 'per instance'. The default step-func is
    SG_VERTEXSTEP_PER_VERTEX. SG_VERTEXSTEP_PER_INSTANCE is used with
    instanced-rendering.

    The vertex-step is part of the vertex-layout definition
    when creating pipeline objects.
*/
sg_vertex_step :: enum u32 {
    DEFAULT;
    PER_VERTEX;
    PER_INSTANCE;
}

/*
    sg_uniform_type

    The data type of a uniform block member. This is used to
    describe the internal layout of uniform blocks when creating
    a shader object. This is only required for the GL backend, all
    other backends will ignore the interior layout of uniform blocks.
*/
sg_uniform_type :: enum u32 {
    INVALID;
    FLOAT;
    FLOAT2;
    FLOAT3;
    FLOAT4;
    INT;
    INT2;
    INT3;
    INT4;
    MAT4;
}

/*
    sg_uniform_layout

    A hint for the interior memory layout of uniform blocks. This is
    only relevant for the GL backend where the internal layout
    of uniform blocks must be known to sokol-gfx. For all other backends the
    internal memory layout of uniform blocks doesn't matter, sokol-gfx
    will just pass uniform data as an opaque memory blob to the
    3D backend.

    SG_UNIFORMLAYOUT_NATIVE (default)
        Native layout means that a 'backend-native' memory layout
        is used. For the GL backend this means that uniforms
        are packed tightly in memory (e.g. there are no padding
        bytes).

    SG_UNIFORMLAYOUT_STD140
        The memory layout is a subset of std140. Arrays are only
        allowed for the FLOAT4, INT4 and MAT4. Alignment is as
        is as follows:

            FLOAT, INT:         4 byte alignment
            FLOAT2, INT2:       8 byte alignment
            FLOAT3, INT3:       16 byte alignment(!)
            FLOAT4, INT4:       16 byte alignment
            MAT4:               16 byte alignment
            FLOAT4[], INT4[]:   16 byte alignment

        The overall size of the uniform block must be a multiple
        of 16.

    For more information search for 'UNIFORM DATA LAYOUT' in the documentation block
    at the start of the header.
*/
sg_uniform_layout :: enum u32 {
    DEFAULT;
    NATIVE;
    STD140;
}

/*
    sg_cull_mode

    The face-culling mode, this is used in the
    sg_pipeline_desc.cull_mode member when creating a
    pipeline object.

    The default cull mode is SG_CULLMODE_NONE
*/
sg_cull_mode :: enum u32 {
    DEFAULT;
    NONE;
    FRONT;
    BACK;
}

/*
    sg_face_winding

    The vertex-winding rule that determines a front-facing primitive. This
    is used in the member sg_pipeline_desc.face_winding
    when creating a pipeline object.

    The default winding is SG_FACEWINDING_CW (clockwise)
*/
sg_face_winding :: enum u32 {
    DEFAULT;
    CCW;
    CW;
}

/*
    sg_compare_func

    The compare-function for configuring depth- and stencil-ref tests
    in pipeline objects, and for texture samplers which perform a comparison
    instead of regular sampling operation.

    Used in the following structs:

    sg_pipeline_desc
        .depth
            .compare
        .stencil
            .front.compare
            .back.compare

    sg_sampler_desc
        .compare

    The default compare func for depth- and stencil-tests is
    SG_COMPAREFUNC_ALWAYS.

    The default compare func for samplers is SG_COMPAREFUNC_NEVER.
*/
sg_compare_func :: enum u32 {
    DEFAULT;
    NEVER;
    LESS;
    EQUAL;
    LESS_EQUAL;
    GREATER;
    NOT_EQUAL;
    GREATER_EQUAL;
    ALWAYS;
}

/*
    sg_stencil_op

    The operation performed on a currently stored stencil-value when a
    comparison test passes or fails. This is used when creating a pipeline
    object in the following sg_pipeline_desc struct items:

    sg_pipeline_desc
        .stencil
            .front
                .fail_op
                .depth_fail_op
                .pass_op
            .back
                .fail_op
                .depth_fail_op
                .pass_op

    The default value is SG_STENCILOP_KEEP.
*/
sg_stencil_op :: enum u32 {
    DEFAULT;
    KEEP;
    ZERO;
    REPLACE;
    INCR_CLAMP;
    DECR_CLAMP;
    INVERT;
    INCR_WRAP;
    DECR_WRAP;
}

/*
    sg_blend_factor

    The source and destination factors in blending operations.
    This is used in the following members when creating a pipeline object:

    sg_pipeline_desc
        .colors[i]
            .blend
                .src_factor_rgb
                .dst_factor_rgb
                .src_factor_alpha
                .dst_factor_alpha

    The default value is SG_BLENDFACTOR_ONE for source
    factors, and for the destination SG_BLENDFACTOR_ZERO if the associated
    blend-op is ADD, SUBTRACT or REVERSE_SUBTRACT or SG_BLENDFACTOR_ONE
    if the associated blend-op is MIN or MAX.
*/
sg_blend_factor :: enum u32 {
    DEFAULT;
    ZERO;
    ONE;
    SRC_COLOR;
    ONE_MINUS_SRC_COLOR;
    SRC_ALPHA;
    ONE_MINUS_SRC_ALPHA;
    DST_COLOR;
    ONE_MINUS_DST_COLOR;
    DST_ALPHA;
    ONE_MINUS_DST_ALPHA;
    SRC_ALPHA_SATURATED;
    BLEND_COLOR;
    ONE_MINUS_BLEND_COLOR;
    BLEND_ALPHA;
    ONE_MINUS_BLEND_ALPHA;
}

/*
    sg_blend_op

    Describes how the source and destination values are combined in the
    fragment blending operation. It is used in the following struct items
    when creating a pipeline object:

    sg_pipeline_desc
        .colors[i]
            .blend
                .op_rgb
                .op_alpha

    The default value is SG_BLENDOP_ADD.
*/
sg_blend_op :: enum u32 {
    DEFAULT;
    ADD;
    SUBTRACT;
    REVERSE_SUBTRACT;
    MIN;
    MAX;
}

/*
    sg_color_mask

    Selects the active color channels when writing a fragment color to the
    framebuffer. This is used in the members
    sg_pipeline_desc.colors[i].write_mask when creating a pipeline object.

    The default colormask is SG_COLORMASK_RGBA (write all colors channels)

    NOTE: since the color mask value 0 is reserved for the default value
    (SG_COLORMASK_RGBA), use SG_COLORMASK_NONE if all color channels
    should be disabled.
*/
sg_color_mask :: enum u32 {
    DEFAULT :: 0;
    NONE :: 16;
    R :: 1;
    G :: 2;
    RG :: 3;
    B :: 4;
    RB :: 5;
    GB :: 6;
    RGB :: 7;
    A :: 8;
    RA :: 9;
    GA :: 10;
    RGA :: 11;
    BA :: 12;
    RBA :: 13;
    GBA :: 14;
    RGBA :: 15;
}

/*
    sg_load_action

    Defines the load action that should be performed at the start of a render pass:

    SG_LOADACTION_CLEAR:        clear the render target
    SG_LOADACTION_LOAD:         load the previous content of the render target
    SG_LOADACTION_DONTCARE:     leave the render target in an undefined state

    This is used in the sg_pass_action structure.

    The default load action for all pass attachments is SG_LOADACTION_CLEAR,
    with the values rgba = { 0.5f, 0.5f, 0.5f, 1.0f }, depth=1.0f and stencil=0.

    If you want to override the default behaviour, it is important to not
    only set the clear color, but the 'action' field as well (as long as this
    is _SG_LOADACTION_DEFAULT, the value fields will be ignored).
*/
sg_load_action :: enum u32 {
    DEFAULT;
    CLEAR;
    LOAD;
    DONTCARE;
}

/*
    sg_store_action

    Defines the store action that should be performed at the end of a render pass:

    SG_STOREACTION_STORE:       store the rendered content to the color attachment image
    SG_STOREACTION_DONTCARE:    allows the GPU to discard the rendered content
*/
sg_store_action :: enum u32 {
    DEFAULT;
    STORE;
    DONTCARE;
}

sg_color_attachment_action :: struct {
    load_action : sg_load_action;
    store_action : sg_store_action;
    clear_value : sg_color;
}

sg_depth_attachment_action :: struct {
    load_action : sg_load_action;
    store_action : sg_store_action;
    clear_value : float;
}

sg_stencil_attachment_action :: struct {
    load_action : sg_load_action;
    store_action : sg_store_action;
    clear_value : u8;
}

sg_pass_action :: struct {
    colors : [4]sg_color_attachment_action;
    depth : sg_depth_attachment_action;
    stencil : sg_stencil_attachment_action;
}

sg_metal_swapchain :: struct {
    current_drawable : *void;
    depth_stencil_texture : *void;
    msaa_color_texture : *void;
}

sg_d3d11_swapchain :: struct {
    render_view : *void;
    resolve_view : *void;
    depth_stencil_view : *void;
}

sg_wgpu_swapchain :: struct {
    render_view : *void;
    resolve_view : *void;
    depth_stencil_view : *void;
}

sg_gl_swapchain :: struct {
    framebuffer : u32;
}

sg_swapchain :: struct {
    width : s32;
    height : s32;
    sample_count : s32;
    color_format : sg_pixel_format;
    depth_format : sg_pixel_format;
    metal : sg_metal_swapchain;
    d3d11 : sg_d3d11_swapchain;
    wgpu : sg_wgpu_swapchain;
    gl : sg_gl_swapchain;
}

sg_pass :: struct {
    _ : u32;
    compute : bool;
    action : sg_pass_action;
    attachments : sg_attachments;
    swapchain : sg_swapchain;
    label : *u8;
    _ : u32;
}

sg_bindings :: struct {
    _ : u32;
    vertex_buffers : [8]sg_buffer;
    vertex_buffer_offsets : [8]s32;
    index_buffer : sg_buffer;
    index_buffer_offset : s32;
    images : [16]sg_image;
    samplers : [16]sg_sampler;
    storage_buffers : [8]sg_buffer;
    _ : u32;
}

sg_buffer_desc :: struct {
    _ : u32;
    size : u64;
    type : sg_buffer_type;
    usage : sg_usage;
    data : sg_range;
    label : *u8;
    gl_buffers : [2]u32;
    mtl_buffers : [2]*void;
    d3d11_buffer : *void;
    wgpu_buffer : *void;
    _ : u32;
}

sg_image_data :: struct {
    subimage : [6][16]sg_range;
}

sg_image_desc :: struct {
    _ : u32;
    type : sg_image_type;
    render_target : bool;
    width : s32;
    height : s32;
    num_slices : s32;
    num_mipmaps : s32;
    usage : sg_usage;
    pixel_format : sg_pixel_format;
    sample_count : s32;
    data : sg_image_data;
    label : *u8;
    gl_textures : [2]u32;
    gl_texture_target : u32;
    mtl_textures : [2]*void;
    d3d11_texture : *void;
    d3d11_shader_resource_view : *void;
    wgpu_texture : *void;
    wgpu_texture_view : *void;
    _ : u32;
}

sg_sampler_desc :: struct {
    _ : u32;
    min_filter : sg_filter;
    mag_filter : sg_filter;
    mipmap_filter : sg_filter;
    wrap_u : sg_wrap;
    wrap_v : sg_wrap;
    wrap_w : sg_wrap;
    min_lod : float;
    max_lod : float;
    border_color : sg_border_color;
    compare : sg_compare_func;
    max_anisotropy : u32;
    label : *u8;
    gl_sampler : u32;
    mtl_sampler : *void;
    d3d11_sampler : *void;
    wgpu_sampler : *void;
    _ : u32;
}

/*
    sg_shader_desc

    Used as parameter of sg_make_shader() to create a shader object which
    communicates shader source or bytecode and shader interface
    reflection information to sokol-gfx.

    If you use sokol-shdc you can ignore the following information since
    the sg_shader_desc struct will be code generated.

    Otherwise you need to provide the following information to the
    sg_make_shader() call:

    - a vertex- and fragment-shader function:
        - the shader source or bytecode
        - an optional entry point name
        - for D3D11: an optional compile target when source code is provided
          (the defaults are "vs_4_0" and "ps_4_0")

    - ...or alternatively, a compute function:
        - the shader source or bytecode
        - an optional entry point name
        - for D3D11: an optional compile target when source code is provided
          (the default is "cs_5_0")

    - vertex attributes required by some backends (not for compute shaders):
        - the vertex attribute base type (undefined, float, signed int, unsigned int),
          this information is only used in the validation layer to check that the
          pipeline object vertex formats are compatible with the input vertex attribute
          type used in the vertex shader. NOTE that the default base type
          'undefined' skips the validation layer check.
        - for the GL backend: optional vertex attribute names used for name lookup
        - for the D3D11 backend: semantic names and indices

    - only for compute shaders on the Metal backend:
        - the workgroup size aka 'threads per thread-group'

          In other 3D APIs this is declared in the shader code:
            - GLSL: `layout(local_size_x=x, local_size_y=y, local_size_y=z) in;`
            - HLSL: `[numthreads(x, y, z)]`
            - WGSL: `@workgroup_size(x, y, z)`
          ...but in Metal the workgroup size is declared on the CPU side

    - reflection information for each uniform block used by the shader:
        - the shader stage the uniform block appears in (SG_SHADERSTAGE_*)
        - the size in bytes of the uniform block
        - backend-specific bindslots:
            - HLSL: the constant buffer register `register(b0..7)`
            - MSL: the buffer attribute `[[buffer(0..7)]]`
            - WGSL: the binding in `@group(0) @binding(0..15)`
        - GLSL only: a description of the uniform block interior
            - the memory layout standard (SG_UNIFORMLAYOUT_*)
            - for each member in the uniform block:
                - the member type (SG_UNIFORM_*)
                - if the member is an array, the array count
                - the member name

    - reflection information for each texture used by the shader:
        - the shader stage the texture appears in (SG_SHADERSTAGE_*)
        - the image type (SG_IMAGETYPE_*)
        - the image-sample type (SG_IMAGESAMPLETYPE_*)
        - whether the texture is multisampled
        - backend specific bindslots:
            - HLSL: the texture register `register(t0..23)`
            - MSL: the texture attribute `[[texture(0..15)]]`
            - WGSL: the binding in `@group(1) @binding(0..127)`

    - reflection information for each sampler used by the shader:
        - the shader stage the sampler appears in (SG_SHADERSTAGE_*)
        - the sampler type (SG_SAMPLERTYPE_*)
        - backend specific bindslots:
            - HLSL: the sampler register `register(s0..15)`
            - MSL: the sampler attribute `[[sampler(0..15)]]`
            - WGSL: the binding in `@group(0) @binding(0..127)`

    - reflection information for each storage buffer used by the shader:
        - the shader stage the storage buffer appears in (SG_SHADERSTAGE_*)
        - whether the storage buffer is readonly (currently this must
          always be true)
        - backend specific bindslots:
            - HLSL:
                - for readonly storage buffer bindings: `register(t0..23)`
                - for read/write storage buffer bindings: `register(u0..7)`
            - MSL: the buffer attribute `[[buffer(8..15)]]`
            - WGSL: the binding in `@group(1) @binding(0..127)`
            - GL: the binding in `layout(binding=0..7)`

    - reflection information for each combined image-sampler object
      used by the shader:
        - the shader stage (SG_SHADERSTAGE_*)
        - the texture's array index in the sg_shader_desc.images[] array
        - the sampler's array index in the sg_shader_desc.samplers[] array
        - GLSL only: the name of the combined image-sampler object

    The number and order of items in the sg_shader_desc.attrs[]
    array corresponds to the items in sg_pipeline_desc.layout.attrs.

        - sg_shader_desc.attrs[N] => sg_pipeline_desc.layout.attrs[N]

    NOTE that vertex attribute indices currently cannot have gaps.

    The items index in the sg_shader_desc.uniform_blocks[] array corresponds
    to the ub_slot arg in sg_apply_uniforms():

        - sg_shader_desc.uniform_blocks[N] => sg_apply_uniforms(N, ...)

    The items in the shader_desc images, samplers and storage_buffers
    arrays correspond to the same array items in the sg_bindings struct:

        - sg_shader_desc.images[N] => sg_bindings.images[N]
        - sg_shader_desc.samplers[N] => sg_bindings.samplers[N]
        - sg_shader_desc.storage_buffers[N] => sg_bindings.storage_buffers[N]

    For all GL backends, shader source-code must be provided. For D3D11 and Metal,
    either shader source-code or byte-code can be provided.

    NOTE that the uniform block, image, sampler and storage_buffer arrays
    can have gaps. This allows to use the same sg_bindings struct for
    different related shader variants.

    For D3D11, if source code is provided, the d3dcompiler_47.dll will be loaded
    on demand. If this fails, shader creation will fail. When compiling HLSL
    source code, you can provide an optional target string via
    sg_shader_stage_desc.d3d11_target, the default target is "vs_4_0" for the
    vertex shader stage and "ps_4_0" for the pixel shader stage.
*/
sg_shader_stage :: enum u32 {
    NONE;
    VERTEX;
    FRAGMENT;
    COMPUTE;
}

sg_shader_function :: struct {
    source : *u8;
    bytecode : sg_range;
    entry : *u8;
    d3d11_target : *u8;
}

sg_shader_attr_base_type :: enum u32 {
    UNDEFINED;
    FLOAT;
    SINT;
    UINT;
}

sg_shader_vertex_attr :: struct {
    base_type : sg_shader_attr_base_type;
    glsl_name : *u8;
    hlsl_sem_name : *u8;
    hlsl_sem_index : u8;
}

sg_glsl_shader_uniform :: struct {
    type : sg_uniform_type;
    array_count : u16;
    glsl_name : *u8;
}

sg_shader_uniform_block :: struct {
    stage : sg_shader_stage;
    size : u32;
    hlsl_register_b_n : u8;
    msl_buffer_n : u8;
    wgsl_group0_binding_n : u8;
    layout : sg_uniform_layout;
    glsl_uniforms : [16]sg_glsl_shader_uniform;
}

sg_shader_image :: struct {
    stage : sg_shader_stage;
    image_type : sg_image_type;
    sample_type : sg_image_sample_type;
    multisampled : bool;
    hlsl_register_t_n : u8;
    msl_texture_n : u8;
    wgsl_group1_binding_n : u8;
}

sg_shader_sampler :: struct {
    stage : sg_shader_stage;
    sampler_type : sg_sampler_type;
    hlsl_register_s_n : u8;
    msl_sampler_n : u8;
    wgsl_group1_binding_n : u8;
}

sg_shader_storage_buffer :: struct {
    stage : sg_shader_stage;
    readonly : bool;
    hlsl_register_t_n : u8;
    hlsl_register_u_n : u8;
    msl_buffer_n : u8;
    wgsl_group1_binding_n : u8;
    glsl_binding_n : u8;
}

sg_shader_image_sampler_pair :: struct {
    stage : sg_shader_stage;
    image_slot : u8;
    sampler_slot : u8;
    glsl_name : *u8;
}

sg_mtl_shader_threads_per_threadgroup :: struct {
    x : s32;
    y : s32;
    z : s32;
}

sg_shader_desc :: struct {
    _ : u32;
    vertex_func : sg_shader_function;
    fragment_func : sg_shader_function;
    compute_func : sg_shader_function;
    attrs : [16]sg_shader_vertex_attr;
    uniform_blocks : [8]sg_shader_uniform_block;
    storage_buffers : [8]sg_shader_storage_buffer;
    images : [16]sg_shader_image;
    samplers : [16]sg_shader_sampler;
    image_sampler_pairs : [16]sg_shader_image_sampler_pair;
    mtl_threads_per_threadgroup : sg_mtl_shader_threads_per_threadgroup;
    label : *u8;
    _ : u32;
}

sg_vertex_buffer_layout_state :: struct {
    stride : s32;
    step_func : sg_vertex_step;
    step_rate : s32;
}

sg_vertex_attr_state :: struct {
    buffer_index : s32;
    offset : s32;
    format : sg_vertex_format;
}

sg_vertex_layout_state :: struct {
    buffers : [8]sg_vertex_buffer_layout_state;
    attrs : [16]sg_vertex_attr_state;
}

sg_stencil_face_state :: struct {
    compare : sg_compare_func;
    fail_op : sg_stencil_op;
    depth_fail_op : sg_stencil_op;
    pass_op : sg_stencil_op;
}

sg_stencil_state :: struct {
    enabled : bool;
    front : sg_stencil_face_state;
    back : sg_stencil_face_state;
    read_mask : u8;
    write_mask : u8;
    ref : u8;
}

sg_depth_state :: struct {
    pixel_format : sg_pixel_format;
    compare : sg_compare_func;
    write_enabled : bool;
    bias : float;
    bias_slope_scale : float;
    bias_clamp : float;
}

sg_blend_state :: struct {
    enabled : bool;
    src_factor_rgb : sg_blend_factor;
    dst_factor_rgb : sg_blend_factor;
    op_rgb : sg_blend_op;
    src_factor_alpha : sg_blend_factor;
    dst_factor_alpha : sg_blend_factor;
    op_alpha : sg_blend_op;
}

sg_color_target_state :: struct {
    pixel_format : sg_pixel_format;
    write_mask : sg_color_mask;
    blend : sg_blend_state;
}

sg_pipeline_desc :: struct {
    _ : u32;
    compute : bool;
    shader : sg_shader;
    layout : sg_vertex_layout_state;
    depth : sg_depth_state;
    stencil : sg_stencil_state;
    color_count : s32;
    colors : [4]sg_color_target_state;
    primitive_type : sg_primitive_type;
    index_type : sg_index_type;
    cull_mode : sg_cull_mode;
    face_winding : sg_face_winding;
    sample_count : s32;
    blend_color : sg_color;
    alpha_to_coverage_enabled : bool;
    label : *u8;
    _ : u32;
}

sg_attachment_desc :: struct {
    image : sg_image;
    mip_level : s32;
    slice : s32;
}

sg_attachments_desc :: struct {
    _ : u32;
    colors : [4]sg_attachment_desc;
    resolves : [4]sg_attachment_desc;
    depth_stencil : sg_attachment_desc;
    label : *u8;
    _ : u32;
}

sg_trace_hooks :: struct {
    user_data : *void;
    reset_state_cache : (a0: *void) #c_call;
    make_buffer : (a0: *sg_buffer_desc, a1: sg_buffer, a2: *void) #c_call;
    make_image : (a0: *sg_image_desc, a1: sg_image, a2: *void) #c_call;
    make_sampler : (a0: *sg_sampler_desc, a1: sg_sampler, a2: *void) #c_call;
    make_shader : (a0: *sg_shader_desc, a1: sg_shader, a2: *void) #c_call;
    make_pipeline : (a0: *sg_pipeline_desc, a1: sg_pipeline, a2: *void) #c_call;
    make_attachments : (a0: *sg_attachments_desc, a1: sg_attachments, a2: *void) #c_call;
    destroy_buffer : (a0: sg_buffer, a1: *void) #c_call;
    destroy_image : (a0: sg_image, a1: *void) #c_call;
    destroy_sampler : (a0: sg_sampler, a1: *void) #c_call;
    destroy_shader : (a0: sg_shader, a1: *void) #c_call;
    destroy_pipeline : (a0: sg_pipeline, a1: *void) #c_call;
    destroy_attachments : (a0: sg_attachments, a1: *void) #c_call;
    update_buffer : (a0: sg_buffer, a1: *sg_range, a2: *void) #c_call;
    update_image : (a0: sg_image, a1: *sg_image_data, a2: *void) #c_call;
    append_buffer : (a0: sg_buffer, a1: *sg_range, a2: s32, a3: *void) #c_call;
    begin_pass : (a0: *sg_pass, a1: *void) #c_call;
    apply_viewport : (a0: s32, a1: s32, a2: s32, a3: s32, a4: bool, a5: *void) #c_call;
    apply_scissor_rect : (a0: s32, a1: s32, a2: s32, a3: s32, a4: bool, a5: *void) #c_call;
    apply_pipeline : (a0: sg_pipeline, a1: *void) #c_call;
    apply_bindings : (a0: *sg_bindings, a1: *void) #c_call;
    apply_uniforms : (a0: s32, a1: *sg_range, a2: *void) #c_call;
    draw : (a0: s32, a1: s32, a2: s32, a3: *void) #c_call;
    dispatch : (a0: s32, a1: s32, a2: s32, a3: *void) #c_call;
    end_pass : (a0: *void) #c_call;
    commit : (a0: *void) #c_call;
    alloc_buffer : (a0: sg_buffer, a1: *void) #c_call;
    alloc_image : (a0: sg_image, a1: *void) #c_call;
    alloc_sampler : (a0: sg_sampler, a1: *void) #c_call;
    alloc_shader : (a0: sg_shader, a1: *void) #c_call;
    alloc_pipeline : (a0: sg_pipeline, a1: *void) #c_call;
    alloc_attachments : (a0: sg_attachments, a1: *void) #c_call;
    dealloc_buffer : (a0: sg_buffer, a1: *void) #c_call;
    dealloc_image : (a0: sg_image, a1: *void) #c_call;
    dealloc_sampler : (a0: sg_sampler, a1: *void) #c_call;
    dealloc_shader : (a0: sg_shader, a1: *void) #c_call;
    dealloc_pipeline : (a0: sg_pipeline, a1: *void) #c_call;
    dealloc_attachments : (a0: sg_attachments, a1: *void) #c_call;
    init_buffer : (a0: sg_buffer, a1: *sg_buffer_desc, a2: *void) #c_call;
    init_image : (a0: sg_image, a1: *sg_image_desc, a2: *void) #c_call;
    init_sampler : (a0: sg_sampler, a1: *sg_sampler_desc, a2: *void) #c_call;
    init_shader : (a0: sg_shader, a1: *sg_shader_desc, a2: *void) #c_call;
    init_pipeline : (a0: sg_pipeline, a1: *sg_pipeline_desc, a2: *void) #c_call;
    init_attachments : (a0: sg_attachments, a1: *sg_attachments_desc, a2: *void) #c_call;
    uninit_buffer : (a0: sg_buffer, a1: *void) #c_call;
    uninit_image : (a0: sg_image, a1: *void) #c_call;
    uninit_sampler : (a0: sg_sampler, a1: *void) #c_call;
    uninit_shader : (a0: sg_shader, a1: *void) #c_call;
    uninit_pipeline : (a0: sg_pipeline, a1: *void) #c_call;
    uninit_attachments : (a0: sg_attachments, a1: *void) #c_call;
    fail_buffer : (a0: sg_buffer, a1: *void) #c_call;
    fail_image : (a0: sg_image, a1: *void) #c_call;
    fail_sampler : (a0: sg_sampler, a1: *void) #c_call;
    fail_shader : (a0: sg_shader, a1: *void) #c_call;
    fail_pipeline : (a0: sg_pipeline, a1: *void) #c_call;
    fail_attachments : (a0: sg_attachments, a1: *void) #c_call;
    push_debug_group : (a0: *u8, a1: *void) #c_call;
    pop_debug_group : (a0: *void) #c_call;
}

sg_slot_info :: struct {
    state : sg_resource_state;
    res_id : u32;
}

sg_buffer_info :: struct {
    slot : sg_slot_info;
    update_frame_index : u32;
    append_frame_index : u32;
    append_pos : s32;
    append_overflow : bool;
    num_slots : s32;
    active_slot : s32;
}

sg_image_info :: struct {
    slot : sg_slot_info;
    upd_frame_index : u32;
    num_slots : s32;
    active_slot : s32;
}

sg_sampler_info :: struct {
    slot : sg_slot_info;
}

sg_shader_info :: struct {
    slot : sg_slot_info;
}

sg_pipeline_info :: struct {
    slot : sg_slot_info;
}

sg_attachments_info :: struct {
    slot : sg_slot_info;
}

sg_frame_stats_gl :: struct {
    num_bind_buffer : u32;
    num_active_texture : u32;
    num_bind_texture : u32;
    num_bind_sampler : u32;
    num_use_program : u32;
    num_render_state : u32;
    num_vertex_attrib_pointer : u32;
    num_vertex_attrib_divisor : u32;
    num_enable_vertex_attrib_array : u32;
    num_disable_vertex_attrib_array : u32;
    num_uniform : u32;
    num_memory_barriers : u32;
}

sg_frame_stats_d3d11_pass :: struct {
    num_om_set_render_targets : u32;
    num_clear_render_target_view : u32;
    num_clear_depth_stencil_view : u32;
    num_resolve_subresource : u32;
}

sg_frame_stats_d3d11_pipeline :: struct {
    num_rs_set_state : u32;
    num_om_set_depth_stencil_state : u32;
    num_om_set_blend_state : u32;
    num_ia_set_primitive_topology : u32;
    num_ia_set_input_layout : u32;
    num_vs_set_shader : u32;
    num_vs_set_constant_buffers : u32;
    num_ps_set_shader : u32;
    num_ps_set_constant_buffers : u32;
    num_cs_set_shader : u32;
    num_cs_set_constant_buffers : u32;
}

sg_frame_stats_d3d11_bindings :: struct {
    num_ia_set_vertex_buffers : u32;
    num_ia_set_index_buffer : u32;
    num_vs_set_shader_resources : u32;
    num_vs_set_samplers : u32;
    num_ps_set_shader_resources : u32;
    num_ps_set_samplers : u32;
    num_cs_set_shader_resources : u32;
    num_cs_set_samplers : u32;
    num_cs_set_unordered_access_views : u32;
}

sg_frame_stats_d3d11_uniforms :: struct {
    num_update_subresource : u32;
}

sg_frame_stats_d3d11_draw :: struct {
    num_draw_indexed_instanced : u32;
    num_draw_indexed : u32;
    num_draw_instanced : u32;
    num_draw : u32;
}

sg_frame_stats_d3d11 :: struct {
    pass : sg_frame_stats_d3d11_pass;
    pipeline : sg_frame_stats_d3d11_pipeline;
    bindings : sg_frame_stats_d3d11_bindings;
    uniforms : sg_frame_stats_d3d11_uniforms;
    draw : sg_frame_stats_d3d11_draw;
    num_map : u32;
    num_unmap : u32;
}

sg_frame_stats_metal_idpool :: struct {
    num_added : u32;
    num_released : u32;
    num_garbage_collected : u32;
}

sg_frame_stats_metal_pipeline :: struct {
    num_set_blend_color : u32;
    num_set_cull_mode : u32;
    num_set_front_facing_winding : u32;
    num_set_stencil_reference_value : u32;
    num_set_depth_bias : u32;
    num_set_render_pipeline_state : u32;
    num_set_depth_stencil_state : u32;
}

sg_frame_stats_metal_bindings :: struct {
    num_set_vertex_buffer : u32;
    num_set_vertex_texture : u32;
    num_set_vertex_sampler_state : u32;
    num_set_fragment_buffer : u32;
    num_set_fragment_texture : u32;
    num_set_fragment_sampler_state : u32;
    num_set_compute_buffer : u32;
    num_set_compute_texture : u32;
    num_set_compute_sampler_state : u32;
}

sg_frame_stats_metal_uniforms :: struct {
    num_set_vertex_buffer_offset : u32;
    num_set_fragment_buffer_offset : u32;
    num_set_compute_buffer_offset : u32;
}

sg_frame_stats_metal :: struct {
    idpool : sg_frame_stats_metal_idpool;
    pipeline : sg_frame_stats_metal_pipeline;
    bindings : sg_frame_stats_metal_bindings;
    uniforms : sg_frame_stats_metal_uniforms;
}

sg_frame_stats_wgpu_uniforms :: struct {
    num_set_bindgroup : u32;
    size_write_buffer : u32;
}

sg_frame_stats_wgpu_bindings :: struct {
    num_set_vertex_buffer : u32;
    num_skip_redundant_vertex_buffer : u32;
    num_set_index_buffer : u32;
    num_skip_redundant_index_buffer : u32;
    num_create_bindgroup : u32;
    num_discard_bindgroup : u32;
    num_set_bindgroup : u32;
    num_skip_redundant_bindgroup : u32;
    num_bindgroup_cache_hits : u32;
    num_bindgroup_cache_misses : u32;
    num_bindgroup_cache_collisions : u32;
    num_bindgroup_cache_invalidates : u32;
    num_bindgroup_cache_hash_vs_key_mismatch : u32;
}

sg_frame_stats_wgpu :: struct {
    uniforms : sg_frame_stats_wgpu_uniforms;
    bindings : sg_frame_stats_wgpu_bindings;
}

sg_frame_stats :: struct {
    frame_index : u32;
    num_passes : u32;
    num_apply_viewport : u32;
    num_apply_scissor_rect : u32;
    num_apply_pipeline : u32;
    num_apply_bindings : u32;
    num_apply_uniforms : u32;
    num_draw : u32;
    num_dispatch : u32;
    num_update_buffer : u32;
    num_append_buffer : u32;
    num_update_image : u32;
    size_apply_uniforms : u32;
    size_update_buffer : u32;
    size_append_buffer : u32;
    size_update_image : u32;
    gl : sg_frame_stats_gl;
    d3d11 : sg_frame_stats_d3d11;
    metal : sg_frame_stats_metal;
    wgpu : sg_frame_stats_wgpu;
}

sg_log_item :: enum u32 {
    OK;
    MALLOC_FAILED;
    GL_TEXTURE_FORMAT_NOT_SUPPORTED;
    GL_3D_TEXTURES_NOT_SUPPORTED;
    GL_ARRAY_TEXTURES_NOT_SUPPORTED;
    GL_STORAGEBUFFER_GLSL_BINDING_OUT_OF_RANGE;
    GL_SHADER_COMPILATION_FAILED;
    GL_SHADER_LINKING_FAILED;
    GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER;
    GL_UNIFORMBLOCK_NAME_NOT_FOUND_IN_SHADER;
    GL_IMAGE_SAMPLER_NAME_NOT_FOUND_IN_SHADER;
    GL_FRAMEBUFFER_STATUS_UNDEFINED;
    GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT;
    GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT;
    GL_FRAMEBUFFER_STATUS_UNSUPPORTED;
    GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE;
    GL_FRAMEBUFFER_STATUS_UNKNOWN;
    D3D11_CREATE_BUFFER_FAILED;
    D3D11_CREATE_BUFFER_SRV_FAILED;
    D3D11_CREATE_BUFFER_UAV_FAILED;
    D3D11_CREATE_DEPTH_TEXTURE_UNSUPPORTED_PIXEL_FORMAT;
    D3D11_CREATE_DEPTH_TEXTURE_FAILED;
    D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT;
    D3D11_CREATE_2D_TEXTURE_FAILED;
    D3D11_CREATE_2D_SRV_FAILED;
    D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT;
    D3D11_CREATE_3D_TEXTURE_FAILED;
    D3D11_CREATE_3D_SRV_FAILED;
    D3D11_CREATE_MSAA_TEXTURE_FAILED;
    D3D11_CREATE_SAMPLER_STATE_FAILED;
    D3D11_UNIFORMBLOCK_HLSL_REGISTER_B_OUT_OF_RANGE;
    D3D11_STORAGEBUFFER_HLSL_REGISTER_T_OUT_OF_RANGE;
    D3D11_STORAGEBUFFER_HLSL_REGISTER_U_OUT_OF_RANGE;
    D3D11_IMAGE_HLSL_REGISTER_T_OUT_OF_RANGE;
    D3D11_SAMPLER_HLSL_REGISTER_S_OUT_OF_RANGE;
    D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED;
    D3D11_SHADER_COMPILATION_FAILED;
    D3D11_SHADER_COMPILATION_OUTPUT;
    D3D11_CREATE_CONSTANT_BUFFER_FAILED;
    D3D11_CREATE_INPUT_LAYOUT_FAILED;
    D3D11_CREATE_RASTERIZER_STATE_FAILED;
    D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED;
    D3D11_CREATE_BLEND_STATE_FAILED;
    D3D11_CREATE_RTV_FAILED;
    D3D11_CREATE_DSV_FAILED;
    D3D11_MAP_FOR_UPDATE_BUFFER_FAILED;
    D3D11_MAP_FOR_APPEND_BUFFER_FAILED;
    D3D11_MAP_FOR_UPDATE_IMAGE_FAILED;
    METAL_CREATE_BUFFER_FAILED;
    METAL_TEXTURE_FORMAT_NOT_SUPPORTED;
    METAL_CREATE_TEXTURE_FAILED;
    METAL_CREATE_SAMPLER_FAILED;
    METAL_SHADER_COMPILATION_FAILED;
    METAL_SHADER_CREATION_FAILED;
    METAL_SHADER_COMPILATION_OUTPUT;
    METAL_SHADER_ENTRY_NOT_FOUND;
    METAL_UNIFORMBLOCK_MSL_BUFFER_SLOT_OUT_OF_RANGE;
    METAL_STORAGEBUFFER_MSL_BUFFER_SLOT_OUT_OF_RANGE;
    METAL_IMAGE_MSL_TEXTURE_SLOT_OUT_OF_RANGE;
    METAL_SAMPLER_MSL_SAMPLER_SLOT_OUT_OF_RANGE;
    METAL_CREATE_CPS_FAILED;
    METAL_CREATE_CPS_OUTPUT;
    METAL_CREATE_RPS_FAILED;
    METAL_CREATE_RPS_OUTPUT;
    METAL_CREATE_DSS_FAILED;
    WGPU_BINDGROUPS_POOL_EXHAUSTED;
    WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE;
    WGPU_BINDGROUPSCACHE_SIZE_POW2;
    WGPU_CREATEBINDGROUP_FAILED;
    WGPU_CREATE_BUFFER_FAILED;
    WGPU_CREATE_TEXTURE_FAILED;
    WGPU_CREATE_TEXTURE_VIEW_FAILED;
    WGPU_CREATE_SAMPLER_FAILED;
    WGPU_CREATE_SHADER_MODULE_FAILED;
    WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED;
    WGPU_UNIFORMBLOCK_WGSL_GROUP0_BINDING_OUT_OF_RANGE;
    WGPU_STORAGEBUFFER_WGSL_GROUP1_BINDING_OUT_OF_RANGE;
    WGPU_IMAGE_WGSL_GROUP1_BINDING_OUT_OF_RANGE;
    WGPU_SAMPLER_WGSL_GROUP1_BINDING_OUT_OF_RANGE;
    WGPU_CREATE_PIPELINE_LAYOUT_FAILED;
    WGPU_CREATE_RENDER_PIPELINE_FAILED;
    WGPU_CREATE_COMPUTE_PIPELINE_FAILED;
    WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED;
    IDENTICAL_COMMIT_LISTENER;
    COMMIT_LISTENER_ARRAY_FULL;
    TRACE_HOOKS_NOT_ENABLED;
    DEALLOC_BUFFER_INVALID_STATE;
    DEALLOC_IMAGE_INVALID_STATE;
    DEALLOC_SAMPLER_INVALID_STATE;
    DEALLOC_SHADER_INVALID_STATE;
    DEALLOC_PIPELINE_INVALID_STATE;
    DEALLOC_ATTACHMENTS_INVALID_STATE;
    INIT_BUFFER_INVALID_STATE;
    INIT_IMAGE_INVALID_STATE;
    INIT_SAMPLER_INVALID_STATE;
    INIT_SHADER_INVALID_STATE;
    INIT_PIPELINE_INVALID_STATE;
    INIT_ATTACHMENTS_INVALID_STATE;
    UNINIT_BUFFER_INVALID_STATE;
    UNINIT_IMAGE_INVALID_STATE;
    UNINIT_SAMPLER_INVALID_STATE;
    UNINIT_SHADER_INVALID_STATE;
    UNINIT_PIPELINE_INVALID_STATE;
    UNINIT_ATTACHMENTS_INVALID_STATE;
    FAIL_BUFFER_INVALID_STATE;
    FAIL_IMAGE_INVALID_STATE;
    FAIL_SAMPLER_INVALID_STATE;
    FAIL_SHADER_INVALID_STATE;
    FAIL_PIPELINE_INVALID_STATE;
    FAIL_ATTACHMENTS_INVALID_STATE;
    BUFFER_POOL_EXHAUSTED;
    IMAGE_POOL_EXHAUSTED;
    SAMPLER_POOL_EXHAUSTED;
    SHADER_POOL_EXHAUSTED;
    PIPELINE_POOL_EXHAUSTED;
    PASS_POOL_EXHAUSTED;
    BEGINPASS_ATTACHMENT_INVALID;
    APPLY_BINDINGS_STORAGE_BUFFER_TRACKER_EXHAUSTED;
    DRAW_WITHOUT_BINDINGS;
    VALIDATE_BUFFERDESC_CANARY;
    VALIDATE_BUFFERDESC_EXPECT_NONZERO_SIZE;
    VALIDATE_BUFFERDESC_EXPECT_MATCHING_DATA_SIZE;
    VALIDATE_BUFFERDESC_EXPECT_ZERO_DATA_SIZE;
    VALIDATE_BUFFERDESC_EXPECT_NO_DATA;
    VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED;
    VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4;
    VALIDATE_IMAGEDATA_NODATA;
    VALIDATE_IMAGEDATA_DATA_SIZE;
    VALIDATE_IMAGEDESC_CANARY;
    VALIDATE_IMAGEDESC_WIDTH;
    VALIDATE_IMAGEDESC_HEIGHT;
    VALIDATE_IMAGEDESC_RT_PIXELFORMAT;
    VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT;
    VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT;
    VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT;
    VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS;
    VALIDATE_IMAGEDESC_MSAA_3D_IMAGE;
    VALIDATE_IMAGEDESC_MSAA_CUBE_IMAGE;
    VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE;
    VALIDATE_IMAGEDESC_RT_IMMUTABLE;
    VALIDATE_IMAGEDESC_RT_NO_DATA;
    VALIDATE_IMAGEDESC_INJECTED_NO_DATA;
    VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA;
    VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE;
    VALIDATE_SAMPLERDESC_CANARY;
    VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING;
    VALIDATE_SHADERDESC_CANARY;
    VALIDATE_SHADERDESC_VERTEX_SOURCE;
    VALIDATE_SHADERDESC_FRAGMENT_SOURCE;
    VALIDATE_SHADERDESC_COMPUTE_SOURCE;
    VALIDATE_SHADERDESC_VERTEX_SOURCE_OR_BYTECODE;
    VALIDATE_SHADERDESC_FRAGMENT_SOURCE_OR_BYTECODE;
    VALIDATE_SHADERDESC_COMPUTE_SOURCE_OR_BYTECODE;
    VALIDATE_SHADERDESC_INVALID_SHADER_COMBO;
    VALIDATE_SHADERDESC_NO_BYTECODE_SIZE;
    VALIDATE_SHADERDESC_METAL_THREADS_PER_THREADGROUP;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_NO_CONT_MEMBERS;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_SIZE_IS_ZERO;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_METAL_BUFFER_SLOT_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_METAL_BUFFER_SLOT_COLLISION;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_HLSL_REGISTER_B_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_HLSL_REGISTER_B_COLLISION;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_WGSL_GROUP0_BINDING_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_WGSL_GROUP0_BINDING_COLLISION;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_NO_MEMBERS;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_UNIFORM_GLSL_NAME;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_SIZE_MISMATCH;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_ARRAY_COUNT;
    VALIDATE_SHADERDESC_UNIFORMBLOCK_STD140_ARRAY_TYPE;
    VALIDATE_SHADERDESC_STORAGEBUFFER_METAL_BUFFER_SLOT_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_STORAGEBUFFER_METAL_BUFFER_SLOT_COLLISION;
    VALIDATE_SHADERDESC_STORAGEBUFFER_HLSL_REGISTER_T_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_STORAGEBUFFER_HLSL_REGISTER_T_COLLISION;
    VALIDATE_SHADERDESC_STORAGEBUFFER_HLSL_REGISTER_U_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_STORAGEBUFFER_HLSL_REGISTER_U_COLLISION;
    VALIDATE_SHADERDESC_STORAGEBUFFER_GLSL_BINDING_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_STORAGEBUFFER_GLSL_BINDING_COLLISION;
    VALIDATE_SHADERDESC_STORAGEBUFFER_WGSL_GROUP1_BINDING_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_STORAGEBUFFER_WGSL_GROUP1_BINDING_COLLISION;
    VALIDATE_SHADERDESC_IMAGE_METAL_TEXTURE_SLOT_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_IMAGE_METAL_TEXTURE_SLOT_COLLISION;
    VALIDATE_SHADERDESC_IMAGE_HLSL_REGISTER_T_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_IMAGE_HLSL_REGISTER_T_COLLISION;
    VALIDATE_SHADERDESC_IMAGE_WGSL_GROUP1_BINDING_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_IMAGE_WGSL_GROUP1_BINDING_COLLISION;
    VALIDATE_SHADERDESC_SAMPLER_METAL_SAMPLER_SLOT_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_SAMPLER_METAL_SAMPLER_SLOT_COLLISION;
    VALIDATE_SHADERDESC_SAMPLER_HLSL_REGISTER_S_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_SAMPLER_HLSL_REGISTER_S_COLLISION;
    VALIDATE_SHADERDESC_SAMPLER_WGSL_GROUP1_BINDING_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_SAMPLER_WGSL_GROUP1_BINDING_COLLISION;
    VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_SAMPLER_SLOT_OUT_OF_RANGE;
    VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_STAGE_MISMATCH;
    VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_SAMPLER_STAGE_MISMATCH;
    VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_GLSL_NAME;
    VALIDATE_SHADERDESC_NONFILTERING_SAMPLER_REQUIRED;
    VALIDATE_SHADERDESC_COMPARISON_SAMPLER_REQUIRED;
    VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS;
    VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS;
    VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG;
    VALIDATE_PIPELINEDESC_CANARY;
    VALIDATE_PIPELINEDESC_SHADER;
    VALIDATE_PIPELINEDESC_COMPUTE_SHADER_EXPECTED;
    VALIDATE_PIPELINEDESC_NO_COMPUTE_SHADER_EXPECTED;
    VALIDATE_PIPELINEDESC_NO_CONT_ATTRS;
    VALIDATE_PIPELINEDESC_ATTR_BASETYPE_MISMATCH;
    VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4;
    VALIDATE_PIPELINEDESC_ATTR_SEMANTICS;
    VALIDATE_PIPELINEDESC_SHADER_READONLY_STORAGEBUFFERS;
    VALIDATE_PIPELINEDESC_BLENDOP_MINMAX_REQUIRES_BLENDFACTOR_ONE;
    VALIDATE_ATTACHMENTSDESC_CANARY;
    VALIDATE_ATTACHMENTSDESC_NO_ATTACHMENTS;
    VALIDATE_ATTACHMENTSDESC_NO_CONT_COLOR_ATTS;
    VALIDATE_ATTACHMENTSDESC_IMAGE;
    VALIDATE_ATTACHMENTSDESC_MIPLEVEL;
    VALIDATE_ATTACHMENTSDESC_FACE;
    VALIDATE_ATTACHMENTSDESC_LAYER;
    VALIDATE_ATTACHMENTSDESC_SLICE;
    VALIDATE_ATTACHMENTSDESC_IMAGE_NO_RT;
    VALIDATE_ATTACHMENTSDESC_COLOR_INV_PIXELFORMAT;
    VALIDATE_ATTACHMENTSDESC_DEPTH_INV_PIXELFORMAT;
    VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES;
    VALIDATE_ATTACHMENTSDESC_IMAGE_SAMPLE_COUNTS;
    VALIDATE_ATTACHMENTSDESC_RESOLVE_COLOR_IMAGE_MSAA;
    VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE;
    VALIDATE_ATTACHMENTSDESC_RESOLVE_SAMPLE_COUNT;
    VALIDATE_ATTACHMENTSDESC_RESOLVE_MIPLEVEL;
    VALIDATE_ATTACHMENTSDESC_RESOLVE_FACE;
    VALIDATE_ATTACHMENTSDESC_RESOLVE_LAYER;
    VALIDATE_ATTACHMENTSDESC_RESOLVE_SLICE;
    VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_NO_RT;
    VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES;
    VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_FORMAT;
    VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE;
    VALIDATE_ATTACHMENTSDESC_DEPTH_MIPLEVEL;
    VALIDATE_ATTACHMENTSDESC_DEPTH_FACE;
    VALIDATE_ATTACHMENTSDESC_DEPTH_LAYER;
    VALIDATE_ATTACHMENTSDESC_DEPTH_SLICE;
    VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_NO_RT;
    VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES;
    VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SAMPLE_COUNT;
    VALIDATE_BEGINPASS_CANARY;
    VALIDATE_BEGINPASS_EXPECT_NO_ATTACHMENTS;
    VALIDATE_BEGINPASS_ATTACHMENTS_EXISTS;
    VALIDATE_BEGINPASS_ATTACHMENTS_VALID;
    VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE;
    VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE;
    VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE;
    VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH;
    VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT;
    VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT;
    VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT;
    VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_DEPTHFORMAT_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE;
    VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE;
    VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE;
    VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW;
    VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW;
    VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW;
    VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW;
    VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW;
    VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW;
    VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET;
    VALIDATE_BEGINPASS_SWAPCHAIN_GL_EXPECT_FRAMEBUFFER_NOTSET;
    VALIDATE_AVP_RENDERPASS_EXPECTED;
    VALIDATE_ASR_RENDERPASS_EXPECTED;
    VALIDATE_APIP_PIPELINE_VALID_ID;
    VALIDATE_APIP_PIPELINE_EXISTS;
    VALIDATE_APIP_PIPELINE_VALID;
    VALIDATE_APIP_PASS_EXPECTED;
    VALIDATE_APIP_SHADER_EXISTS;
    VALIDATE_APIP_SHADER_VALID;
    VALIDATE_APIP_COMPUTEPASS_EXPECTED;
    VALIDATE_APIP_RENDERPASS_EXPECTED;
    VALIDATE_APIP_CURPASS_ATTACHMENTS_EXISTS;
    VALIDATE_APIP_CURPASS_ATTACHMENTS_VALID;
    VALIDATE_APIP_ATT_COUNT;
    VALIDATE_APIP_COLOR_FORMAT;
    VALIDATE_APIP_DEPTH_FORMAT;
    VALIDATE_APIP_SAMPLE_COUNT;
    VALIDATE_ABND_PASS_EXPECTED;
    VALIDATE_ABND_EMPTY_BINDINGS;
    VALIDATE_ABND_PIPELINE;
    VALIDATE_ABND_PIPELINE_EXISTS;
    VALIDATE_ABND_PIPELINE_VALID;
    VALIDATE_ABND_COMPUTE_EXPECTED_NO_VBS;
    VALIDATE_ABND_COMPUTE_EXPECTED_NO_IB;
    VALIDATE_ABND_EXPECTED_VB;
    VALIDATE_ABND_VB_EXISTS;
    VALIDATE_ABND_VB_TYPE;
    VALIDATE_ABND_VB_OVERFLOW;
    VALIDATE_ABND_NO_IB;
    VALIDATE_ABND_IB;
    VALIDATE_ABND_IB_EXISTS;
    VALIDATE_ABND_IB_TYPE;
    VALIDATE_ABND_IB_OVERFLOW;
    VALIDATE_ABND_EXPECTED_IMAGE_BINDING;
    VALIDATE_ABND_IMG_EXISTS;
    VALIDATE_ABND_IMAGE_TYPE_MISMATCH;
    VALIDATE_ABND_EXPECTED_MULTISAMPLED_IMAGE;
    VALIDATE_ABND_IMAGE_MSAA;
    VALIDATE_ABND_EXPECTED_FILTERABLE_IMAGE;
    VALIDATE_ABND_EXPECTED_DEPTH_IMAGE;
    VALIDATE_ABND_EXPECTED_SAMPLER_BINDING;
    VALIDATE_ABND_UNEXPECTED_SAMPLER_COMPARE_NEVER;
    VALIDATE_ABND_EXPECTED_SAMPLER_COMPARE_NEVER;
    VALIDATE_ABND_EXPECTED_NONFILTERING_SAMPLER;
    VALIDATE_ABND_SMP_EXISTS;
    VALIDATE_ABND_EXPECTED_STORAGEBUFFER_BINDING;
    VALIDATE_ABND_STORAGEBUFFER_EXISTS;
    VALIDATE_ABND_STORAGEBUFFER_BINDING_BUFFERTYPE;
    VALIDATE_ABND_STORAGEBUFFER_READWRITE_IMMUTABLE;
    VALIDATE_AU_PASS_EXPECTED;
    VALIDATE_AU_NO_PIPELINE;
    VALIDATE_AU_NO_UNIFORMBLOCK_AT_SLOT;
    VALIDATE_AU_SIZE;
    VALIDATE_DRAW_RENDERPASS_EXPECTED;
    VALIDATE_DRAW_BASEELEMENT;
    VALIDATE_DRAW_NUMELEMENTS;
    VALIDATE_DRAW_NUMINSTANCES;
    VALIDATE_DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING;
    VALIDATE_DISPATCH_COMPUTEPASS_EXPECTED;
    VALIDATE_DISPATCH_NUMGROUPSX;
    VALIDATE_DISPATCH_NUMGROUPSY;
    VALIDATE_DISPATCH_NUMGROUPSZ;
    VALIDATE_DISPATCH_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING;
    VALIDATE_UPDATEBUF_USAGE;
    VALIDATE_UPDATEBUF_SIZE;
    VALIDATE_UPDATEBUF_ONCE;
    VALIDATE_UPDATEBUF_APPEND;
    VALIDATE_APPENDBUF_USAGE;
    VALIDATE_APPENDBUF_SIZE;
    VALIDATE_APPENDBUF_UPDATE;
    VALIDATE_UPDIMG_USAGE;
    VALIDATE_UPDIMG_ONCE;
    VALIDATION_FAILED;
}

sg_environment_defaults :: struct {
    color_format : sg_pixel_format;
    depth_format : sg_pixel_format;
    sample_count : s32;
}

sg_metal_environment :: struct {
    device : *void;
}

sg_d3d11_environment :: struct {
    device : *void;
    device_context : *void;
}

sg_wgpu_environment :: struct {
    device : *void;
}

sg_environment :: struct {
    defaults : sg_environment_defaults;
    metal : sg_metal_environment;
    d3d11 : sg_d3d11_environment;
    wgpu : sg_wgpu_environment;
}

sg_commit_listener :: struct {
    func : (a0: *void) #c_call;
    user_data : *void;
}

sg_allocator :: struct {
    alloc_fn : (a0: u64, a1: *void) -> *void #c_call;
    free_fn : (a0: *void, a1: *void) #c_call;
    user_data : *void;
}

sg_logger :: struct {
    func : (a0: *u8, a1: u32, a2: u32, a3: *u8, a4: u32, a5: *u8, a6: *void) #c_call;
    user_data : *void;
}

sg_desc :: struct {
    _ : u32;
    buffer_pool_size : s32;
    image_pool_size : s32;
    sampler_pool_size : s32;
    shader_pool_size : s32;
    pipeline_pool_size : s32;
    attachments_pool_size : s32;
    uniform_buffer_size : s32;
    max_dispatch_calls_per_pass : s32;
    max_commit_listeners : s32;
    disable_validation : bool;
    d3d11_shader_debugging : bool;
    mtl_force_managed_storage_mode : bool;
    mtl_use_command_buffer_with_retained_references : bool;
    wgpu_disable_bindgroups_cache : bool;
    wgpu_bindgroups_cache_size : s32;
    allocator : sg_allocator;
    logger : sg_logger;
    environment : sg_environment;
    _ : u32;
}

sg_d3d11_buffer_info :: struct {
    buf : *void;
}

sg_d3d11_image_info :: struct {
    tex2d : *void;
    tex3d : *void;
    res : *void;
    srv : *void;
}

sg_d3d11_sampler_info :: struct {
    smp : *void;
}

sg_d3d11_shader_info :: struct {
    cbufs : [8]*void;
    vs : *void;
    fs : *void;
}

sg_d3d11_pipeline_info :: struct {
    il : *void;
    rs : *void;
    dss : *void;
    bs : *void;
}

sg_d3d11_attachments_info :: struct {
    color_rtv : [4]*void;
    resolve_rtv : [4]*void;
    dsv : *void;
}

sg_mtl_buffer_info :: struct {
    buf : [2]*void;
    active_slot : s32;
}

sg_mtl_image_info :: struct {
    tex : [2]*void;
    active_slot : s32;
}

sg_mtl_sampler_info :: struct {
    smp : *void;
}

sg_mtl_shader_info :: struct {
    vertex_lib : *void;
    fragment_lib : *void;
    vertex_func : *void;
    fragment_func : *void;
}

sg_mtl_pipeline_info :: struct {
    rps : *void;
    dss : *void;
}

sg_wgpu_buffer_info :: struct {
    buf : *void;
}

sg_wgpu_image_info :: struct {
    tex : *void;
    view : *void;
}

sg_wgpu_sampler_info :: struct {
    smp : *void;
}

sg_wgpu_shader_info :: struct {
    vs_mod : *void;
    fs_mod : *void;
    bgl : *void;
}

sg_wgpu_pipeline_info :: struct {
    render_pipeline : *void;
    compute_pipeline : *void;
}

sg_wgpu_attachments_info :: struct {
    color_view : [4]*void;
    resolve_view : [4]*void;
    ds_view : *void;
}

sg_gl_buffer_info :: struct {
    buf : [2]u32;
    active_slot : s32;
}

sg_gl_image_info :: struct {
    tex : [2]u32;
    tex_target : u32;
    msaa_render_buffer : u32;
    active_slot : s32;
}

sg_gl_sampler_info :: struct {
    smp : u32;
}

sg_gl_shader_info :: struct {
    prog : u32;
}

sg_gl_attachments_info :: struct {
    framebuffer : u32;
    msaa_resolve_framebuffer : [4]u32;
}

