sokol-luajit/ffi/sokol-headers/sokol_gfx.h

4496 lines
207 KiB
Objective-C

/*
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, like a 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 pass:
sg_begin_pass(const sg_pass* pass);
Typically, 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 all the required information to render into the swapchain's surfaces.
To start an 'offscreen 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 });
--- set the render pipeline state for the next draw call with:
sg_apply_pipeline(sg_pipeline pip)
--- fill an sg_bindings struct with the resource bindings for the next
draw call (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
--- 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.
--- finish the current rendering 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
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
countermeasure to avoid 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
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_apply_pipeline
sg_apply_bindings
sg_apply_uniforms
sg_draw
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.
NOTE: currently multisample-images cannot be bound as textures.
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 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 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- shader-stage:
- for the desktop GL backend, source code can be provided in '#version 410' or
'#version 430', version 430 is required for storage buffer support, but note
that this is not available on macOS
- for the GLES3 backend, source code must be provided in '#version 300 es' syntax
- for the D3D11 backend, shaders can be provided as source or binary blobs, the
source code should be in HLSL4.0 (for best compatibility) or alternatively
in HLSL5.0 syntax (other versions may work but are not tested), NOTE: 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")
- Depending on backend, information about the input vertex attributes used by the
vertex shader:
- Metal: no information needed since vertex attributes are always bound
by their attribute location defined in the shader via '[[attribute(N)]]'
- WebGPU: no 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.
- Information about each uniform block used in the shader:
- the shader stage of the uniform block (vertex or fragment)
- 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, note that currently only
readonly storage buffers are supported
- a backend-specific bind slot:
- D3D11/HLSL: the texture register N (`register(tN)`) where N is 0..23
(in HLSL, storage buffers and texture share the same bind space)
- 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..16
- 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 or fragment)
- 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
(currently it's not supported to fetch data from multisampled
textures in shaders, but this is planned for a later time)
- a backend-specific bind slot:
- D3D11/HLSL: the texture register N (`register(tN)`) where N is 0..23
(in HLSL, 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 or fragment)
- 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 storage buffers: `register(t0..t23)`
- 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.
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
==================
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)
- all GLES3 platforms (WebGL2, iOS, Android - with the option that support on
Android may be added at a later point)
Currently only 'readonly' storage buffers are supported (meaning it's not possible
to write to storage buffers from shaders).
To use storage buffers, the following steps are required:
- write a shader which uses storage buffers (also see the example links below)
- create one or more storage buffers via sg_make_buffer() with the
buffer type SG_BUFFERTYPE_STORAGEBUFFER
- when creating a shader via sg_make_shader(), populate the sg_shader_desc
struct with binding info (when using sokol-shdc, this step will be taken care
of automatically)
- which storage buffer bind slots on the vertex- and fragment-stage
are occupied
- whether the storage buffer on that bind slot is readonly (this is currently required
to be true)
- when calling sg_apply_bindings(), apply the matching bind slots with the previously
created storage buffers
- ...and that's it.
For more details, see the following backend-agnostic sokol samples:
- simple vertex pulling from a storage buffer:
- C code: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.c
- shader: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.glsl
- instanced rendering via storage buffers (vertex- and instance-pulling):
- C code: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.c
- shader: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.glsl
- storage buffers both on the vertex- and fragment-stage:
- C code: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.c
- shader: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.glsl
- the Ozz animation sample rewritten to pull all rendering data from storage buffers:
- C code: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.cc
- shader: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.glsl
...also see the following backend-specific vertex pulling samples (those also don't use sokol-shdc):
- D3D11: https://github.com/floooh/sokol-samples/blob/master/d3d11/vertexpulling-d3d11.c
- desktop GL: https://github.com/floooh/sokol-samples/blob/master/glfw/vertexpulling-glfw.c
- Metal: https://github.com/floooh/sokol-samples/blob/master/metal/vertexpulling-metal.c
- WebGPU: https://github.com/floooh/sokol-samples/blob/master/wgpu/vertexpulling-wgpu.c
Storage buffer shader authoring caveats when using sokol-shdc:
- declare a storage buffer interface block with `layout(binding=N) readonly buffer [name] { ... }`
(where 'N' is the index in `sg_bindings.storage_buffers[N]`)
- declare a struct which describes a single array item in the storage buffer interface block
- only put a single flexible array member into the storage buffer interface block
E.g. a complete example in 'sokol-shdc GLSL':
```glsl
// declare a struct:
struct sb_vertex {
vec3 pos;
vec4 color;
}
// declare a buffer interface block with a single flexible struct array:
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;
...
}
```
Backend-specific storage-buffer caveats (not relevant when using sokol-shdc):
D3D11:
- storage buffers are created as 'raw' Byte Address Buffers
(https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-intro#raw-views-of-buffers)
- in HLSL, use a ByteAddressBuffer to access the buffer content
(https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-byteaddressbuffer)
- in D3D11, storage buffers and textures share the same bind slots (declared as
`register(tN)` in HLSL), where N must be in the range 0..23)
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.
A NOTE ON PORTABLE PACKED VERTEX FORMATS:
=========================================
There are two things to consider when using packed
vertex formats like UBYTE4, SHORT2, etc which need to work
across all backends:
- D3D11 can only convert *normalized* vertex formats to
floating point during vertex fetch, normalized formats
have a trailing 'N', and are "normalized" to a range
-1.0..+1.0 (for the signed formats) or 0.0..1.0 (for the
unsigned formats):
- SG_VERTEXFORMAT_BYTE4N
- SG_VERTEXFORMAT_UBYTE4N
- SG_VERTEXFORMAT_SHORT2N
- SG_VERTEXFORMAT_USHORT2N
- SG_VERTEXFORMAT_SHORT4N
- SG_VERTEXFORMAT_USHORT4N
D3D11 will not convert *non-normalized* vertex formats to floating point
vertex shader inputs, those can only be uses with the *ivecn* vertex shader
input types when D3D11 is used as backend (GL and Metal can use both formats)
- SG_VERTEXFORMAT_BYTE4,
- SG_VERTEXFORMAT_UBYTE4
- SG_VERTEXFORMAT_SHORT2
- SG_VERTEXFORMAT_SHORT4
For a vertex input layout which works on all platforms, only use the following
vertex formats, and if needed "expand" the normalized vertex shader
inputs in the vertex shader by multiplying with 127.0, 255.0, 32767.0 or
65535.0:
- SG_VERTEXFORMAT_FLOAT,
- SG_VERTEXFORMAT_FLOAT2,
- SG_VERTEXFORMAT_FLOAT3,
- SG_VERTEXFORMAT_FLOAT4,
- SG_VERTEXFORMAT_BYTE4N,
- SG_VERTEXFORMAT_UBYTE4N,
- SG_VERTEXFORMAT_SHORT2N,
- SG_VERTEXFORMAT_USHORT2N
- SG_VERTEXFORMAT_SHORT4N,
- SG_VERTEXFORMAT_USHORT4N
- SG_VERTEXFORMAT_UINT10_N2
- SG_VERTEXFORMAT_HALF2
- SG_VERTEXFORMAT_HALF4
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..127:
@group(0) @binding(0..7)
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.
- The sokol shader compiler generally adds `diagnostic(off, derivative_uniformity);`
into the WGSL output. Currently only the Chrome WebGPU implementation seems
to recognize this.
- The vertex format SG_VERTEXFORMAT_UINT10_N2 is currently not supported because
WebGPU lacks a matching vertex format (this is currently being worked on though,
as soon as the vertex format shows up in webgpu.h, sokol_gfx.h will add support.
- Likewise, the following sokol-gfx vertex formats are not supported in WebGPU:
R16, R16SN, RG16, RG16SN, RGBA16, RGBA16SN and all PVRTC compressed format.
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.
*/
#define SOKOL_GFX_INCLUDED (1)
#define SOKOL_API_DECL
#define SOKOL_GFX_API_DECL SOKOL_API_DECL
/*
Resource id typedefs:
sg_buffer: vertex- and index-buffers
sg_image: images used as textures and render-pass attachments
sg_sampler sampler objects describing how a texture is sampled in a shader
sg_shader: vertex- and fragment-shaders and shader interface information
sg_pipeline: associated shader and vertex-layouts, and render states
sg_attachments: a baked collection of render pass attachment images
Instead of pointers, resource creation functions return a 32-bit
handle which uniquely identifies the resource object.
The 32-bit resource id is split into a 16-bit pool index in the lower bits,
and a 16-bit 'generation counter' in the upper bits. The index allows fast
pool lookups, and combined with the generation-counter it allows to detect
'dangling accesses' (trying to use an object which no longer exists, and
its pool slot has been reused for a new object)
The resource ids are wrapped into a strongly-typed struct so that
trying to pass an incompatible resource id is a compile error.
*/
typedef struct sg_buffer { uint32_t id; } sg_buffer;
typedef struct sg_image { uint32_t id; } sg_image;
typedef struct sg_sampler { uint32_t id; } sg_sampler;
typedef struct sg_shader { uint32_t id; } sg_shader;
typedef struct sg_pipeline { uint32_t id; } sg_pipeline;
typedef struct sg_attachments { uint32_t id; } sg_attachments;
/*
sg_range is a pointer-size-pair struct used to pass memory blobs into
sokol-gfx. When initialized from a value type (array or struct), you can
use the SG_RANGE() macro to build an sg_range struct. For functions which
take either a sg_range pointer, or a (C++) sg_range reference, use the
SG_RANGE_REF macro as a solution which compiles both in C and C++.
*/
typedef struct sg_range {
const void* ptr;
size_t size;
} sg_range;
// disabling this for every includer isn't great, but the warnings are also quite pointless
#if defined(_MSC_VER)
#pragma warning(disable:4221) // /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y'
#pragma warning(disable:4204) // VS2015: nonstandard extension used: non-constant aggregate initializer
#endif
#if defined(__cplusplus)
#define SG_RANGE(x) sg_range{ &x, sizeof(x) }
#define SG_RANGE_REF(x) sg_range{ &x, sizeof(x) }
#else
#define SG_RANGE(x) (sg_range){ &x, sizeof(x) }
#define SG_RANGE_REF(x) &(sg_range){ &x, sizeof(x) }
#endif
// various compile-time constants in the public API
enum {
SG_INVALID_ID = 0,
SG_NUM_INFLIGHT_FRAMES = 2,
SG_MAX_COLOR_ATTACHMENTS = 4,
SG_MAX_UNIFORMBLOCK_MEMBERS = 16,
SG_MAX_VERTEX_ATTRIBUTES = 16,
SG_MAX_MIPMAPS = 16,
SG_MAX_TEXTUREARRAY_LAYERS = 128,
SG_MAX_UNIFORMBLOCK_BINDSLOTS = 8,
SG_MAX_VERTEXBUFFER_BINDSLOTS = 8,
SG_MAX_IMAGE_BINDSLOTS = 16,
SG_MAX_SAMPLER_BINDSLOTS = 16,
SG_MAX_STORAGEBUFFER_BINDSLOTS = 8,
SG_MAX_IMAGE_SAMPLER_PAIRS = 16,
};
/*
sg_color
An RGBA color value.
*/
typedef struct sg_color { float r, g, b, a; } sg_color;
/*
sg_backend
The active 3D-API backend, use the function sg_query_backend()
to get the currently active backend.
*/
typedef enum sg_backend {
SG_BACKEND_GLCORE,
SG_BACKEND_GLES3,
SG_BACKEND_D3D11,
SG_BACKEND_METAL_IOS,
SG_BACKEND_METAL_MACOS,
SG_BACKEND_METAL_SIMULATOR,
SG_BACKEND_WGPU,
SG_BACKEND_DUMMY,
} sg_backend;
/*
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
*/
typedef enum sg_pixel_format {
_SG_PIXELFORMAT_DEFAULT, // value 0 reserved for default-init
SG_PIXELFORMAT_NONE,
SG_PIXELFORMAT_R8,
SG_PIXELFORMAT_R8SN,
SG_PIXELFORMAT_R8UI,
SG_PIXELFORMAT_R8SI,
SG_PIXELFORMAT_R16,
SG_PIXELFORMAT_R16SN,
SG_PIXELFORMAT_R16UI,
SG_PIXELFORMAT_R16SI,
SG_PIXELFORMAT_R16F,
SG_PIXELFORMAT_RG8,
SG_PIXELFORMAT_RG8SN,
SG_PIXELFORMAT_RG8UI,
SG_PIXELFORMAT_RG8SI,
SG_PIXELFORMAT_R32UI,
SG_PIXELFORMAT_R32SI,
SG_PIXELFORMAT_R32F,
SG_PIXELFORMAT_RG16,
SG_PIXELFORMAT_RG16SN,
SG_PIXELFORMAT_RG16UI,
SG_PIXELFORMAT_RG16SI,
SG_PIXELFORMAT_RG16F,
SG_PIXELFORMAT_RGBA8,
SG_PIXELFORMAT_SRGB8A8,
SG_PIXELFORMAT_RGBA8SN,
SG_PIXELFORMAT_RGBA8UI,
SG_PIXELFORMAT_RGBA8SI,
SG_PIXELFORMAT_BGRA8,
SG_PIXELFORMAT_RGB10A2,
SG_PIXELFORMAT_RG11B10F,
SG_PIXELFORMAT_RGB9E5,
SG_PIXELFORMAT_RG32UI,
SG_PIXELFORMAT_RG32SI,
SG_PIXELFORMAT_RG32F,
SG_PIXELFORMAT_RGBA16,
SG_PIXELFORMAT_RGBA16SN,
SG_PIXELFORMAT_RGBA16UI,
SG_PIXELFORMAT_RGBA16SI,
SG_PIXELFORMAT_RGBA16F,
SG_PIXELFORMAT_RGBA32UI,
SG_PIXELFORMAT_RGBA32SI,
SG_PIXELFORMAT_RGBA32F,
// NOTE: when adding/removing pixel formats before DEPTH, also update sokol_app.h/_SAPP_PIXELFORMAT_*
SG_PIXELFORMAT_DEPTH,
SG_PIXELFORMAT_DEPTH_STENCIL,
// NOTE: don't put any new compressed format in front of here
SG_PIXELFORMAT_BC1_RGBA,
SG_PIXELFORMAT_BC2_RGBA,
SG_PIXELFORMAT_BC3_RGBA,
SG_PIXELFORMAT_BC3_SRGBA,
SG_PIXELFORMAT_BC4_R,
SG_PIXELFORMAT_BC4_RSN,
SG_PIXELFORMAT_BC5_RG,
SG_PIXELFORMAT_BC5_RGSN,
SG_PIXELFORMAT_BC6H_RGBF,
SG_PIXELFORMAT_BC6H_RGBUF,
SG_PIXELFORMAT_BC7_RGBA,
SG_PIXELFORMAT_BC7_SRGBA,
SG_PIXELFORMAT_PVRTC_RGB_2BPP, // FIXME: deprecated
SG_PIXELFORMAT_PVRTC_RGB_4BPP, // FIXME: deprecated
SG_PIXELFORMAT_PVRTC_RGBA_2BPP, // FIXME: deprecated
SG_PIXELFORMAT_PVRTC_RGBA_4BPP, // FIXME: deprecated
SG_PIXELFORMAT_ETC2_RGB8,
SG_PIXELFORMAT_ETC2_SRGB8,
SG_PIXELFORMAT_ETC2_RGB8A1,
SG_PIXELFORMAT_ETC2_RGBA8,
SG_PIXELFORMAT_ETC2_SRGB8A8,
SG_PIXELFORMAT_EAC_R11,
SG_PIXELFORMAT_EAC_R11SN,
SG_PIXELFORMAT_EAC_RG11,
SG_PIXELFORMAT_EAC_RG11SN,
SG_PIXELFORMAT_ASTC_4x4_RGBA,
SG_PIXELFORMAT_ASTC_4x4_SRGBA,
_SG_PIXELFORMAT_NUM,
_SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF
} sg_pixel_format;
/*
Runtime information about a pixel format, returned by sg_query_pixelformat().
*/
typedef struct sg_pixelformat_info {
bool sample; // pixel format can be sampled in shaders at least with nearest filtering
bool filter; // pixel format can be sampled with linear filtering
bool render; // pixel format can be used as render-pass attachment
bool blend; // pixel format supports alpha-blending when used as render-pass attachment
bool msaa; // pixel format supports MSAA when used as render-pass attachment
bool depth; // pixel format is a depth format
bool compressed; // true if this is a hardware-compressed format
int bytes_per_pixel; // NOTE: this is 0 for compressed formats, use sg_query_row_pitch() / sg_query_surface_pitch() as alternative
} sg_pixelformat_info;
/*
Runtime information about available optional features, returned by sg_query_features()
*/
typedef struct sg_features {
bool origin_top_left; // framebuffer- and texture-origin is in top left corner
bool image_clamp_to_border; // border color and clamp-to-border uv-wrap mode is supported
bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state
bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks
bool storage_buffer; // storage buffers are supported
bool msaa_image_bindings; // if true, multisampled images can be bound as texture resources
} sg_features;
/*
Runtime information about resource limits, returned by sg_query_limit()
*/
typedef struct sg_limits {
int max_image_size_2d; // max width/height of SG_IMAGETYPE_2D images
int max_image_size_cube; // max width/height of SG_IMAGETYPE_CUBE images
int max_image_size_3d; // max width/height/depth of SG_IMAGETYPE_3D images
int max_image_size_array; // max width/height of SG_IMAGETYPE_ARRAY images
int max_image_array_layers; // max number of layers in SG_IMAGETYPE_ARRAY images
int max_vertex_attrs; // max number of vertex attributes, clamped to SG_MAX_VERTEX_ATTRIBUTES
int gl_max_vertex_uniform_components; // <= GL_MAX_VERTEX_UNIFORM_COMPONENTS (only on GL backends)
int gl_max_combined_texture_image_units; // <= GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS (only on GL backends)
} sg_limits;
/*
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.
*/
typedef enum sg_resource_state {
SG_RESOURCESTATE_INITIAL,
SG_RESOURCESTATE_ALLOC,
SG_RESOURCESTATE_VALID,
SG_RESOURCESTATE_FAILED,
SG_RESOURCESTATE_INVALID,
_SG_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF
} sg_resource_state;
/*
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 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.
*/
typedef enum sg_usage {
_SG_USAGE_DEFAULT, // value 0 reserved for default-init
SG_USAGE_IMMUTABLE,
SG_USAGE_DYNAMIC,
SG_USAGE_STREAM,
_SG_USAGE_NUM,
_SG_USAGE_FORCE_U32 = 0x7FFFFFFF
} sg_usage;
/*
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.
*/
typedef enum sg_buffer_type {
_SG_BUFFERTYPE_DEFAULT, // value 0 reserved for default-init
SG_BUFFERTYPE_VERTEXBUFFER,
SG_BUFFERTYPE_INDEXBUFFER,
SG_BUFFERTYPE_STORAGEBUFFER,
_SG_BUFFERTYPE_NUM,
_SG_BUFFERTYPE_FORCE_U32 = 0x7FFFFFFF
} sg_buffer_type;
/*
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.
*/
typedef enum sg_index_type {
_SG_INDEXTYPE_DEFAULT, // value 0 reserved for default-init
SG_INDEXTYPE_NONE,
SG_INDEXTYPE_UINT16,
SG_INDEXTYPE_UINT32,
_SG_INDEXTYPE_NUM,
_SG_INDEXTYPE_FORCE_U32 = 0x7FFFFFFF
} sg_index_type;
/*
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.
*/
typedef enum sg_image_type {
_SG_IMAGETYPE_DEFAULT, // value 0 reserved for default-init
SG_IMAGETYPE_2D,
SG_IMAGETYPE_CUBE,
SG_IMAGETYPE_3D,
SG_IMAGETYPE_ARRAY,
_SG_IMAGETYPE_NUM,
_SG_IMAGETYPE_FORCE_U32 = 0x7FFFFFFF
} sg_image_type;
/*
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`)
*/
typedef enum sg_image_sample_type {
_SG_IMAGESAMPLETYPE_DEFAULT, // value 0 reserved for default-init
SG_IMAGESAMPLETYPE_FLOAT,
SG_IMAGESAMPLETYPE_DEPTH,
SG_IMAGESAMPLETYPE_SINT,
SG_IMAGESAMPLETYPE_UINT,
SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT,
_SG_IMAGESAMPLETYPE_NUM,
_SG_IMAGESAMPLETYPE_FORCE_U32 = 0x7FFFFFFF
} sg_image_sample_type;
/*
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
*/
typedef enum sg_sampler_type {
_SG_SAMPLERTYPE_DEFAULT,
SG_SAMPLERTYPE_FILTERING,
SG_SAMPLERTYPE_NONFILTERING,
SG_SAMPLERTYPE_COMPARISON,
_SG_SAMPLERTYPE_NUM,
_SG_SAMPLERTYPE_FORCE_U32,
} sg_sampler_type;
/*
sg_cube_face
The cubemap faces. Use these as indices in the sg_image_desc.content
array.
*/
typedef enum sg_cube_face {
SG_CUBEFACE_POS_X,
SG_CUBEFACE_NEG_X,
SG_CUBEFACE_POS_Y,
SG_CUBEFACE_NEG_Y,
SG_CUBEFACE_POS_Z,
SG_CUBEFACE_NEG_Z,
SG_CUBEFACE_NUM,
_SG_CUBEFACE_FORCE_U32 = 0x7FFFFFFF
} sg_cube_face;
/*
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.
*/
typedef enum sg_primitive_type {
_SG_PRIMITIVETYPE_DEFAULT, // value 0 reserved for default-init
SG_PRIMITIVETYPE_POINTS,
SG_PRIMITIVETYPE_LINES,
SG_PRIMITIVETYPE_LINE_STRIP,
SG_PRIMITIVETYPE_TRIANGLES,
SG_PRIMITIVETYPE_TRIANGLE_STRIP,
_SG_PRIMITIVETYPE_NUM,
_SG_PRIMITIVETYPE_FORCE_U32 = 0x7FFFFFFF
} sg_primitive_type;
/*
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.
*/
typedef enum sg_filter {
_SG_FILTER_DEFAULT, // value 0 reserved for default-init
SG_FILTER_NEAREST,
SG_FILTER_LINEAR,
_SG_FILTER_NUM,
_SG_FILTER_FORCE_U32 = 0x7FFFFFFF
} sg_filter;
/*
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.
*/
typedef enum sg_wrap {
_SG_WRAP_DEFAULT, // value 0 reserved for default-init
SG_WRAP_REPEAT,
SG_WRAP_CLAMP_TO_EDGE,
SG_WRAP_CLAMP_TO_BORDER,
SG_WRAP_MIRRORED_REPEAT,
_SG_WRAP_NUM,
_SG_WRAP_FORCE_U32 = 0x7FFFFFFF
} sg_wrap;
/*
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
*/
typedef enum sg_border_color {
_SG_BORDERCOLOR_DEFAULT, // value 0 reserved for default-init
SG_BORDERCOLOR_TRANSPARENT_BLACK,
SG_BORDERCOLOR_OPAQUE_BLACK,
SG_BORDERCOLOR_OPAQUE_WHITE,
_SG_BORDERCOLOR_NUM,
_SG_BORDERCOLOR_FORCE_U32 = 0x7FFFFFFF
} sg_border_color;
/*
sg_vertex_format
The data type of a vertex component. This is used to describe
the layout of vertex data when creating a pipeline object.
*/
typedef enum sg_vertex_format {
SG_VERTEXFORMAT_INVALID,
SG_VERTEXFORMAT_FLOAT,
SG_VERTEXFORMAT_FLOAT2,
SG_VERTEXFORMAT_FLOAT3,
SG_VERTEXFORMAT_FLOAT4,
SG_VERTEXFORMAT_BYTE4,
SG_VERTEXFORMAT_BYTE4N,
SG_VERTEXFORMAT_UBYTE4,
SG_VERTEXFORMAT_UBYTE4N,
SG_VERTEXFORMAT_SHORT2,
SG_VERTEXFORMAT_SHORT2N,
SG_VERTEXFORMAT_USHORT2N,
SG_VERTEXFORMAT_SHORT4,
SG_VERTEXFORMAT_SHORT4N,
SG_VERTEXFORMAT_USHORT4N,
SG_VERTEXFORMAT_UINT10_N2,
SG_VERTEXFORMAT_HALF2,
SG_VERTEXFORMAT_HALF4,
_SG_VERTEXFORMAT_NUM,
_SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF
} sg_vertex_format;
/*
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.
*/
typedef enum sg_vertex_step {
_SG_VERTEXSTEP_DEFAULT, // value 0 reserved for default-init
SG_VERTEXSTEP_PER_VERTEX,
SG_VERTEXSTEP_PER_INSTANCE,
_SG_VERTEXSTEP_NUM,
_SG_VERTEXSTEP_FORCE_U32 = 0x7FFFFFFF
} sg_vertex_step;
/*
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.
*/
typedef enum sg_uniform_type {
SG_UNIFORMTYPE_INVALID,
SG_UNIFORMTYPE_FLOAT,
SG_UNIFORMTYPE_FLOAT2,
SG_UNIFORMTYPE_FLOAT3,
SG_UNIFORMTYPE_FLOAT4,
SG_UNIFORMTYPE_INT,
SG_UNIFORMTYPE_INT2,
SG_UNIFORMTYPE_INT3,
SG_UNIFORMTYPE_INT4,
SG_UNIFORMTYPE_MAT4,
_SG_UNIFORMTYPE_NUM,
_SG_UNIFORMTYPE_FORCE_U32 = 0x7FFFFFFF
} sg_uniform_type;
/*
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 a single 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.
*/
typedef enum sg_uniform_layout {
_SG_UNIFORMLAYOUT_DEFAULT, // value 0 reserved for default-init
SG_UNIFORMLAYOUT_NATIVE, // default: layout depends on currently active backend
SG_UNIFORMLAYOUT_STD140, // std140: memory layout according to std140
_SG_UNIFORMLAYOUT_NUM,
_SG_UNIFORMLAYOUT_FORCE_U32 = 0x7FFFFFFF
} sg_uniform_layout;
/*
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
*/
typedef enum sg_cull_mode {
_SG_CULLMODE_DEFAULT, // value 0 reserved for default-init
SG_CULLMODE_NONE,
SG_CULLMODE_FRONT,
SG_CULLMODE_BACK,
_SG_CULLMODE_NUM,
_SG_CULLMODE_FORCE_U32 = 0x7FFFFFFF
} sg_cull_mode;
/*
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)
*/
typedef enum sg_face_winding {
_SG_FACEWINDING_DEFAULT, // value 0 reserved for default-init
SG_FACEWINDING_CCW,
SG_FACEWINDING_CW,
_SG_FACEWINDING_NUM,
_SG_FACEWINDING_FORCE_U32 = 0x7FFFFFFF
} sg_face_winding;
/*
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.compar
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.
*/
typedef enum sg_compare_func {
_SG_COMPAREFUNC_DEFAULT, // value 0 reserved for default-init
SG_COMPAREFUNC_NEVER,
SG_COMPAREFUNC_LESS,
SG_COMPAREFUNC_EQUAL,
SG_COMPAREFUNC_LESS_EQUAL,
SG_COMPAREFUNC_GREATER,
SG_COMPAREFUNC_NOT_EQUAL,
SG_COMPAREFUNC_GREATER_EQUAL,
SG_COMPAREFUNC_ALWAYS,
_SG_COMPAREFUNC_NUM,
_SG_COMPAREFUNC_FORCE_U32 = 0x7FFFFFFF
} sg_compare_func;
/*
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.
*/
typedef enum sg_stencil_op {
_SG_STENCILOP_DEFAULT, // value 0 reserved for default-init
SG_STENCILOP_KEEP,
SG_STENCILOP_ZERO,
SG_STENCILOP_REPLACE,
SG_STENCILOP_INCR_CLAMP,
SG_STENCILOP_DECR_CLAMP,
SG_STENCILOP_INVERT,
SG_STENCILOP_INCR_WRAP,
SG_STENCILOP_DECR_WRAP,
_SG_STENCILOP_NUM,
_SG_STENCILOP_FORCE_U32 = 0x7FFFFFFF
} sg_stencil_op;
/*
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 SG_BLENDFACTOR_ZERO for destination factors.
*/
typedef enum sg_blend_factor {
_SG_BLENDFACTOR_DEFAULT, // value 0 reserved for default-init
SG_BLENDFACTOR_ZERO,
SG_BLENDFACTOR_ONE,
SG_BLENDFACTOR_SRC_COLOR,
SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
SG_BLENDFACTOR_SRC_ALPHA,
SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
SG_BLENDFACTOR_DST_COLOR,
SG_BLENDFACTOR_ONE_MINUS_DST_COLOR,
SG_BLENDFACTOR_DST_ALPHA,
SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA,
SG_BLENDFACTOR_SRC_ALPHA_SATURATED,
SG_BLENDFACTOR_BLEND_COLOR,
SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR,
SG_BLENDFACTOR_BLEND_ALPHA,
SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA,
_SG_BLENDFACTOR_NUM,
_SG_BLENDFACTOR_FORCE_U32 = 0x7FFFFFFF
} sg_blend_factor;
/*
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.
*/
typedef enum sg_blend_op {
_SG_BLENDOP_DEFAULT, // value 0 reserved for default-init
SG_BLENDOP_ADD,
SG_BLENDOP_SUBTRACT,
SG_BLENDOP_REVERSE_SUBTRACT,
_SG_BLENDOP_NUM,
_SG_BLENDOP_FORCE_U32 = 0x7FFFFFFF
} sg_blend_op;
/*
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.
*/
typedef enum sg_color_mask {
_SG_COLORMASK_DEFAULT = 0, // value 0 reserved for default-init
SG_COLORMASK_NONE = 0x10, // special value for 'all channels disabled
SG_COLORMASK_R = 0x1,
SG_COLORMASK_G = 0x2,
SG_COLORMASK_RG = 0x3,
SG_COLORMASK_B = 0x4,
SG_COLORMASK_RB = 0x5,
SG_COLORMASK_GB = 0x6,
SG_COLORMASK_RGB = 0x7,
SG_COLORMASK_A = 0x8,
SG_COLORMASK_RA = 0x9,
SG_COLORMASK_GA = 0xA,
SG_COLORMASK_RGA = 0xB,
SG_COLORMASK_BA = 0xC,
SG_COLORMASK_RBA = 0xD,
SG_COLORMASK_GBA = 0xE,
SG_COLORMASK_RGBA = 0xF,
_SG_COLORMASK_FORCE_U32 = 0x7FFFFFFF
} sg_color_mask;
/*
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).
*/
typedef enum sg_load_action {
_SG_LOADACTION_DEFAULT,
SG_LOADACTION_CLEAR,
SG_LOADACTION_LOAD,
SG_LOADACTION_DONTCARE,
_SG_LOADACTION_FORCE_U32 = 0x7FFFFFFF
} sg_load_action;
/*
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
*/
typedef enum sg_store_action {
_SG_STOREACTION_DEFAULT,
SG_STOREACTION_STORE,
SG_STOREACTION_DONTCARE,
_SG_STOREACTION_FORCE_U32 = 0x7FFFFFFF
} sg_store_action;
/*
sg_pass_action
The sg_pass_action struct defines the actions to be performed
at the start and end of a render pass.
- at the start of the pass: whether the render attachments should be cleared,
loaded with their previous content, or start in an undefined state
- for clear operations: the clear value (color, depth, or stencil values)
- at the end of the pass: whether the rendering result should be
stored back into the render attachment or discarded
*/
typedef struct sg_color_attachment_action {
sg_load_action load_action; // default: SG_LOADACTION_CLEAR
sg_store_action store_action; // default: SG_STOREACTION_STORE
sg_color clear_value; // default: { 0.5f, 0.5f, 0.5f, 1.0f }
} sg_color_attachment_action;
typedef struct sg_depth_attachment_action {
sg_load_action load_action; // default: SG_LOADACTION_CLEAR
sg_store_action store_action; // default: SG_STOREACTION_DONTCARE
float clear_value; // default: 1.0
} sg_depth_attachment_action;
typedef struct sg_stencil_attachment_action {
sg_load_action load_action; // default: SG_LOADACTION_CLEAR
sg_store_action store_action; // default: SG_STOREACTION_DONTCARE
uint8_t clear_value; // default: 0
} sg_stencil_attachment_action;
typedef struct sg_pass_action {
sg_color_attachment_action colors[SG_MAX_COLOR_ATTACHMENTS];
sg_depth_attachment_action depth;
sg_stencil_attachment_action stencil;
} sg_pass_action;
/*
sg_swapchain
Used in sg_begin_pass() to provide details about an external swapchain
(pixel formats, sample count and backend-API specific render surface objects).
The following information must be provided:
- the width and height of the swapchain surfaces in number of pixels,
- the pixel format of the render- and optional msaa-resolve-surface
- the pixel format of the optional depth- or depth-stencil-surface
- the MSAA sample count for the render and depth-stencil surface
If the pixel formats and MSAA sample counts are left zero-initialized,
their defaults are taken from the sg_environment struct provided in the
sg_setup() call.
The width and height *must* be > 0.
Additionally the following backend API specific objects must be passed in
as 'type erased' void pointers:
GL:
- on all GL backends, a GL framebuffer object must be provided. This
can be zero for the default framebuffer.
D3D11:
- an ID3D11RenderTargetView for the rendering surface, without
MSAA rendering this surface will also be displayed
- an optional ID3D11DepthStencilView for the depth- or depth/stencil
buffer surface
- when MSAA rendering is used, another ID3D11RenderTargetView
which serves as MSAA resolve target and will be displayed
WebGPU (same as D3D11, except different types)
- a WGPUTextureView for the rendering surface, without
MSAA rendering this surface will also be displayed
- an optional WGPUTextureView for the depth- or depth/stencil
buffer surface
- when MSAA rendering is used, another WGPUTextureView
which serves as MSAA resolve target and will be displayed
Metal (NOTE that the roles of provided surfaces is slightly different
than on D3D11 or WebGPU in case of MSAA vs non-MSAA rendering):
- A current CAMetalDrawable (NOT an MTLDrawable!) which will be presented.
This will either be rendered to directly (if no MSAA is used), or serve
as MSAA-resolve target.
- an optional MTLTexture for the depth- or depth-stencil buffer
- an optional multisampled MTLTexture which serves as intermediate
rendering surface which will then be resolved into the
CAMetalDrawable.
NOTE that for Metal you must use an ObjC __bridge cast to
properly tunnel the ObjC object id through a C void*, e.g.:
swapchain.metal.current_drawable = (__bridge const void*) [mtkView currentDrawable];
On all other backends you shouldn't need to mess with the reference count.
It's a good practice to write a helper function which returns an initialized
sg_swapchain structs, which can then be plugged directly into
sg_pass.swapchain. Look at the function sglue_swapchain() in the sokol_glue.h
as an example.
*/
typedef struct sg_metal_swapchain {
const void* current_drawable; // CAMetalDrawable (NOT MTLDrawable!!!)
const void* depth_stencil_texture; // MTLTexture
const void* msaa_color_texture; // MTLTexture
} sg_metal_swapchain;
typedef struct sg_d3d11_swapchain {
const void* render_view; // ID3D11RenderTargetView
const void* resolve_view; // ID3D11RenderTargetView
const void* depth_stencil_view; // ID3D11DepthStencilView
} sg_d3d11_swapchain;
typedef struct sg_wgpu_swapchain {
const void* render_view; // WGPUTextureView
const void* resolve_view; // WGPUTextureView
const void* depth_stencil_view; // WGPUTextureView
} sg_wgpu_swapchain;
typedef struct sg_gl_swapchain {
uint32_t framebuffer; // GL framebuffer object
} sg_gl_swapchain;
typedef struct sg_swapchain {
int width;
int height;
int sample_count;
sg_pixel_format color_format;
sg_pixel_format depth_format;
sg_metal_swapchain metal;
sg_d3d11_swapchain d3d11;
sg_wgpu_swapchain wgpu;
sg_gl_swapchain gl;
} sg_swapchain;
/*
sg_pass
The sg_pass structure is passed as argument into the sg_begin_pass()
function.
For an offscreen rendering pass, an sg_pass_action struct and sg_attachments
object must be provided, and for swapchain passes, an sg_pass_action and
an sg_swapchain struct. It is an error to provide both an sg_attachments
handle and an initialized sg_swapchain struct in the same sg_begin_pass().
An sg_begin_pass() call for an offscreen pass would look like this (where
`attachments` is an sg_attachments handle):
sg_begin_pass(&(sg_pass){
.action = { ... },
.attachments = attachments,
});
...and a swapchain render pass would look like this (using the sokol_glue.h
helper function sglue_swapchain() which gets the swapchain properties from
sokol_app.h):
sg_begin_pass(&(sg_pass){
.action = { ... },
.swapchain = sglue_swapchain(),
});
You can also omit the .action object to get default pass action behaviour
(clear to color=grey, depth=1 and stencil=0).
*/
typedef struct sg_pass {
uint32_t _start_canary;
sg_pass_action action;
sg_attachments attachments;
sg_swapchain swapchain;
const char* label;
uint32_t _end_canary;
} sg_pass;
/*
sg_bindings
The sg_bindings structure defines the buffers, images and
samplers resource bindings for the next draw call.
To update the resource bindings, call sg_apply_bindings() with
a pointer to a populated sg_bindings struct. Note that
sg_apply_bindings() must be called after sg_apply_pipeline()
and that bindings are not preserved across sg_apply_pipeline()
calls, even when the new pipeline uses the same 'bindings layout'.
A resource binding struct contains:
- 1..N vertex buffers
- 0..N vertex buffer offsets
- 0..1 index buffers
- 0..1 index buffer offsets
- 0..N images
- 0..N samplers
- 0..N storage buffers
Where 'N' is defined in the following constants:
- SG_MAX_VERTEXBUFFER_BINDSLOTS
- SG_MAX_IMAGE_BINDLOTS
- SG_MAX_SAMPLER_BINDSLOTS
- SG_MAX_STORAGEBUFFER_BINDGLOTS
When using sokol-shdc for shader authoring, the `layout(binding=N)`
annotation in the shader code directly maps to the slot index for that
resource type in the bindings struct, for instance the following vertex-
and fragment-shader interface for sokol-shdc:
@vs vs
layout(binding=0) uniform vs_params { ... };
layout(binding=0) readonly buffer ssbo { ... };
layout(binding=0) uniform texture2D vs_tex;
layout(binding=0) uniform sampler vs_smp;
...
@end
@fs fs
layout(binding=1) uniform fs_params { ... };
layout(binding=1) uniform texture2D fs_tex;
layout(binding=1) uniform sampler fs_smp;
...
@end
...would map to the following sg_bindings struct:
const sg_bindings bnd = {
.vertex_buffers[0] = ...,
.images[0] = vs_tex,
.images[1] = fs_tex,
.samplers[0] = vs_smp,
.samplers[1] = fs_smp,
.storage_buffers[0] = ssbo,
};
...alternatively you can use code-generated slot indices:
const sg_bindings bnd = {
.vertex_buffers[0] = ...,
.images[IMG_vs_tex] = vs_tex,
.images[IMG_fs_tex] = fs_tex,
.samplers[SMP_vs_smp] = vs_smp,
.samplers[SMP_fs_smp] = fs_smp,
.storage_buffers[SBUF_ssbo] = ssbo,
};
Resource bindslots for a specific shader/pipeline may have gaps, and an
sg_bindings struct may have populated bind slots which are not used by a
specific shader. This allows to use the same sg_bindings struct across
different shader variants.
When not using sokol-shdc, the bindslot indices in the sg_bindings
struct need to match the per-resource reflection info slot indices
in the sg_shader_desc struct (for details about that see the
sg_shader_desc struct documentation).
The optional buffer offsets can be used to put different unrelated
chunks of vertex- and/or index-data into the same buffer objects.
*/
typedef struct sg_bindings {
uint32_t _start_canary;
sg_buffer vertex_buffers[SG_MAX_VERTEXBUFFER_BINDSLOTS];
int vertex_buffer_offsets[SG_MAX_VERTEXBUFFER_BINDSLOTS];
sg_buffer index_buffer;
int index_buffer_offset;
sg_image images[SG_MAX_IMAGE_BINDSLOTS];
sg_sampler samplers[SG_MAX_SAMPLER_BINDSLOTS];
sg_buffer storage_buffers[SG_MAX_STORAGEBUFFER_BINDSLOTS];
uint32_t _end_canary;
} sg_bindings;
/*
sg_buffer_desc
Creation parameters for sg_buffer objects, used in the
sg_make_buffer() call.
The default configuration is:
.size: 0 (*must* be >0 for buffers without data)
.type: SG_BUFFERTYPE_VERTEXBUFFER
.usage: SG_USAGE_IMMUTABLE
.data.ptr 0 (*must* be valid for immutable buffers)
.data.size 0 (*must* be > 0 for immutable buffers)
.label 0 (optional string label)
For immutable buffers which are initialized with initial data,
keep the .size item zero-initialized, and set the size together with the
pointer to the initial data in the .data item.
For mutable buffers without initial data, keep the .data item
zero-initialized, and set the buffer size in the .size item instead.
You can also set both size values, but currently both size values must
be identical (this may change in the future when the dynamic resource
management may become more flexible).
ADVANCED TOPIC: Injecting native 3D-API buffers:
The following struct members allow to inject your own GL, Metal
or D3D11 buffers into sokol_gfx:
.gl_buffers[SG_NUM_INFLIGHT_FRAMES]
.mtl_buffers[SG_NUM_INFLIGHT_FRAMES]
.d3d11_buffer
You must still provide all other struct items except the .data item, and
these must match the creation parameters of the native buffers you
provide. For SG_USAGE_IMMUTABLE, only provide a single native 3D-API
buffer, otherwise you need to provide SG_NUM_INFLIGHT_FRAMES buffers
(only for GL and Metal, not D3D11). Providing multiple buffers for GL and
Metal is necessary because sokol_gfx will rotate through them when
calling sg_update_buffer() to prevent lock-stalls.
Note that it is expected that immutable injected buffer have already been
initialized with content, and the .content member must be 0!
Also you need to call sg_reset_state_cache() after calling native 3D-API
functions, and before calling any sokol_gfx function.
*/
typedef struct sg_buffer_desc {
uint32_t _start_canary;
size_t size;
sg_buffer_type type;
sg_usage usage;
sg_range data;
const char* label;
// optionally inject backend-specific resources
uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES];
const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES];
const void* d3d11_buffer;
const void* wgpu_buffer;
uint32_t _end_canary;
} sg_buffer_desc;
/*
sg_image_data
Defines the content of an image through a 2D array of sg_range structs.
The first array dimension is the cubemap face, and the second array
dimension the mipmap level.
*/
typedef struct sg_image_data {
sg_range subimage[SG_CUBEFACE_NUM][SG_MAX_MIPMAPS];
} sg_image_data;
/*
sg_image_desc
Creation parameters for sg_image objects, used in the sg_make_image() call.
The default configuration is:
.type: SG_IMAGETYPE_2D
.render_target: false
.width 0 (must be set to >0)
.height 0 (must be set to >0)
.num_slices 1 (3D textures: depth; array textures: number of layers)
.num_mipmaps: 1
.usage: SG_USAGE_IMMUTABLE
.pixel_format: SG_PIXELFORMAT_RGBA8 for textures, or sg_desc.environment.defaults.color_format for render targets
.sample_count: 1 for textures, or sg_desc.environment.defaults.sample_count for render targets
.data an sg_image_data struct to define the initial content
.label 0 (optional string label for trace hooks)
Q: Why is the default sample_count for render targets identical with the
"default sample count" from sg_desc.environment.defaults.sample_count?
A: So that it matches the default sample count in pipeline objects. Even
though it is a bit strange/confusing that offscreen render targets by default
get the same sample count as 'default swapchains', but it's better that
an offscreen render target created with default parameters matches
a pipeline object created with default parameters.
NOTE:
Images with usage SG_USAGE_IMMUTABLE must be fully initialized by
providing a valid .data member which points to initialization data.
ADVANCED TOPIC: Injecting native 3D-API textures:
The following struct members allow to inject your own GL, Metal or D3D11
textures into sokol_gfx:
.gl_textures[SG_NUM_INFLIGHT_FRAMES]
.mtl_textures[SG_NUM_INFLIGHT_FRAMES]
.d3d11_texture
.d3d11_shader_resource_view
.wgpu_texture
.wgpu_texture_view
For GL, you can also specify the texture target or leave it empty to use
the default texture target for the image type (GL_TEXTURE_2D for
SG_IMAGETYPE_2D etc)
For D3D11 and WebGPU, either only provide a texture, or both a texture and
shader-resource-view / texture-view object. If you want to use access the
injected texture in a shader you *must* provide a shader-resource-view.
The same rules apply as for injecting native buffers (see sg_buffer_desc
documentation for more details).
*/
typedef struct sg_image_desc {
uint32_t _start_canary;
sg_image_type type;
bool render_target;
int width;
int height;
int num_slices;
int num_mipmaps;
sg_usage usage;
sg_pixel_format pixel_format;
int sample_count;
sg_image_data data;
const char* label;
// optionally inject backend-specific resources
uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES];
uint32_t gl_texture_target;
const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES];
const void* d3d11_texture;
const void* d3d11_shader_resource_view;
const void* wgpu_texture;
const void* wgpu_texture_view;
uint32_t _end_canary;
} sg_image_desc;
/*
sg_sampler_desc
Creation parameters for sg_sampler objects, used in the sg_make_sampler() call
.min_filter: SG_FILTER_NEAREST
.mag_filter: SG_FILTER_NEAREST
.mipmap_filter SG_FILTER_NEAREST
.wrap_u: SG_WRAP_REPEAT
.wrap_v: SG_WRAP_REPEAT
.wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D)
.min_lod 0.0f
.max_lod FLT_MAX
.border_color SG_BORDERCOLOR_OPAQUE_BLACK
.compare SG_COMPAREFUNC_NEVER
.max_anisotropy 1 (must be 1..16)
*/
typedef struct sg_sampler_desc {
uint32_t _start_canary;
sg_filter min_filter;
sg_filter mag_filter;
sg_filter mipmap_filter;
sg_wrap wrap_u;
sg_wrap wrap_v;
sg_wrap wrap_w;
float min_lod;
float max_lod;
sg_border_color border_color;
sg_compare_func compare;
uint32_t max_anisotropy;
const char* label;
// optionally inject backend-specific resources
uint32_t gl_sampler;
const void* mtl_sampler;
const void* d3d11_sampler;
const void* wgpu_sampler;
uint32_t _end_canary;
} sg_sampler_desc;
/*
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")
- vertex attributes required by some backends:
- for the GL backend: optional vertex attribute names
used for name lookup
- for the D3D11 backend: semantic names and indices
- 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: the texture(sic) register `register(t0..23)`
- MSL: the buffer attribute `[[buffer(8..15)]]`
- WGSL: the binding in `@group(1) @binding(0..127)`
- GL: the binding in `layout(binding=0..16)`
- 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.
*/
typedef enum sg_shader_stage {
SG_SHADERSTAGE_NONE,
SG_SHADERSTAGE_VERTEX,
SG_SHADERSTAGE_FRAGMENT,
} sg_shader_stage;
typedef struct sg_shader_function {
const char* source;
sg_range bytecode;
const char* entry;
const char* d3d11_target; // default: "vs_4_0" or "ps_4_0"
} sg_shader_function;
typedef struct sg_shader_vertex_attr {
const char* glsl_name; // [optional] GLSL attribute name
const char* hlsl_sem_name; // HLSL semantic name
uint8_t hlsl_sem_index; // HLSL semantic index
} sg_shader_vertex_attr;
typedef struct sg_glsl_shader_uniform {
sg_uniform_type type;
uint16_t array_count; // 0 or 1 for scalars, >1 for arrays
const char* glsl_name; // glsl name binding is required on GL 4.1 and WebGL2
} sg_glsl_shader_uniform;
typedef struct sg_shader_uniform_block {
sg_shader_stage stage;
uint32_t size;
uint8_t hlsl_register_b_n; // HLSL register(bn)
uint8_t msl_buffer_n; // MSL [[buffer(n)]]
uint8_t wgsl_group0_binding_n; // WGSL @group(0) @binding(n)
sg_uniform_layout layout;
sg_glsl_shader_uniform glsl_uniforms[SG_MAX_UNIFORMBLOCK_MEMBERS];
} sg_shader_uniform_block;
typedef struct sg_shader_image {
sg_shader_stage stage;
sg_image_type image_type;
sg_image_sample_type sample_type;
bool multisampled;
uint8_t hlsl_register_t_n; // HLSL register(tn) bind slot
uint8_t msl_texture_n; // MSL [[texture(n)]] bind slot
uint8_t wgsl_group1_binding_n; // WGSL @group(1) @binding(n) bind slot
} sg_shader_image;
typedef struct sg_shader_sampler {
sg_shader_stage stage;
sg_sampler_type sampler_type;
uint8_t hlsl_register_s_n; // HLSL register(sn) bind slot
uint8_t msl_sampler_n; // MSL [[sampler(n)]] bind slot
uint8_t wgsl_group1_binding_n; // WGSL @group(1) @binding(n) bind slot
} sg_shader_sampler;
typedef struct sg_shader_storage_buffer {
sg_shader_stage stage;
bool readonly;
uint8_t hlsl_register_t_n; // HLSL register(tn) bind slot
uint8_t msl_buffer_n; // MSL [[buffer(n)]] bind slot
uint8_t wgsl_group1_binding_n; // WGSL @group(1) @binding(n) bind slot
uint8_t glsl_binding_n; // GLSL layout(binding=n)
} sg_shader_storage_buffer;
typedef struct sg_shader_image_sampler_pair {
sg_shader_stage stage;
uint8_t image_slot;
uint8_t sampler_slot;
const char* glsl_name; // glsl name binding required because of GL 4.1 and WebGL2
} sg_shader_image_sampler_pair;
typedef struct sg_shader_desc {
uint32_t _start_canary;
sg_shader_function vertex_func;
sg_shader_function fragment_func;
sg_shader_vertex_attr attrs[SG_MAX_VERTEX_ATTRIBUTES];
sg_shader_uniform_block uniform_blocks[SG_MAX_UNIFORMBLOCK_BINDSLOTS];
sg_shader_storage_buffer storage_buffers[SG_MAX_STORAGEBUFFER_BINDSLOTS];
sg_shader_image images[SG_MAX_IMAGE_BINDSLOTS];
sg_shader_sampler samplers[SG_MAX_SAMPLER_BINDSLOTS];
sg_shader_image_sampler_pair image_sampler_pairs[SG_MAX_IMAGE_SAMPLER_PAIRS];
const char* label;
uint32_t _end_canary;
} sg_shader_desc;
/*
sg_pipeline_desc
The sg_pipeline_desc struct defines all creation parameters for an
sg_pipeline object, used as argument to the sg_make_pipeline() function:
- the vertex layout for all input vertex buffers
- a shader object
- the 3D primitive type (points, lines, triangles, ...)
- the index type (none, 16- or 32-bit)
- all the fixed-function-pipeline state (depth-, stencil-, blend-state, etc...)
If the vertex data has no gaps between vertex components, you can omit
the .layout.buffers[].stride and layout.attrs[].offset items (leave them
default-initialized to 0), sokol-gfx will then compute the offsets and
strides from the vertex component formats (.layout.attrs[].format).
Please note that ALL vertex attribute offsets must be 0 in order for the
automatic offset computation to kick in.
The default configuration is as follows:
.shader: 0 (must be initialized with a valid sg_shader id!)
.layout:
.buffers[]: vertex buffer layouts
.stride: 0 (if no stride is given it will be computed)
.step_func SG_VERTEXSTEP_PER_VERTEX
.step_rate 1
.attrs[]: vertex attribute declarations
.buffer_index 0 the vertex buffer bind slot
.offset 0 (offsets can be omitted if the vertex layout has no gaps)
.format SG_VERTEXFORMAT_INVALID (must be initialized!)
.depth:
.pixel_format: sg_desc.context.depth_format
.compare: SG_COMPAREFUNC_ALWAYS
.write_enabled: false
.bias: 0.0f
.bias_slope_scale: 0.0f
.bias_clamp: 0.0f
.stencil:
.enabled: false
.front/back:
.compare: SG_COMPAREFUNC_ALWAYS
.fail_op: SG_STENCILOP_KEEP
.depth_fail_op: SG_STENCILOP_KEEP
.pass_op: SG_STENCILOP_KEEP
.read_mask: 0
.write_mask: 0
.ref: 0
.color_count 1
.colors[0..color_count]
.pixel_format sg_desc.context.color_format
.write_mask: SG_COLORMASK_RGBA
.blend:
.enabled: false
.src_factor_rgb: SG_BLENDFACTOR_ONE
.dst_factor_rgb: SG_BLENDFACTOR_ZERO
.op_rgb: SG_BLENDOP_ADD
.src_factor_alpha: SG_BLENDFACTOR_ONE
.dst_factor_alpha: SG_BLENDFACTOR_ZERO
.op_alpha: SG_BLENDOP_ADD
.primitive_type: SG_PRIMITIVETYPE_TRIANGLES
.index_type: SG_INDEXTYPE_NONE
.cull_mode: SG_CULLMODE_NONE
.face_winding: SG_FACEWINDING_CW
.sample_count: sg_desc.context.sample_count
.blend_color: (sg_color) { 0.0f, 0.0f, 0.0f, 0.0f }
.alpha_to_coverage_enabled: false
.label 0 (optional string label for trace hooks)
*/
typedef struct sg_vertex_buffer_layout_state {
int stride;
sg_vertex_step step_func;
int step_rate;
} sg_vertex_buffer_layout_state;
typedef struct sg_vertex_attr_state {
int buffer_index;
int offset;
sg_vertex_format format;
} sg_vertex_attr_state;
typedef struct sg_vertex_layout_state {
sg_vertex_buffer_layout_state buffers[SG_MAX_VERTEXBUFFER_BINDSLOTS];
sg_vertex_attr_state attrs[SG_MAX_VERTEX_ATTRIBUTES];
} sg_vertex_layout_state;
typedef struct sg_stencil_face_state {
sg_compare_func compare;
sg_stencil_op fail_op;
sg_stencil_op depth_fail_op;
sg_stencil_op pass_op;
} sg_stencil_face_state;
typedef struct sg_stencil_state {
bool enabled;
sg_stencil_face_state front;
sg_stencil_face_state back;
uint8_t read_mask;
uint8_t write_mask;
uint8_t ref;
} sg_stencil_state;
typedef struct sg_depth_state {
sg_pixel_format pixel_format;
sg_compare_func compare;
bool write_enabled;
float bias;
float bias_slope_scale;
float bias_clamp;
} sg_depth_state;
typedef struct sg_blend_state {
bool enabled;
sg_blend_factor src_factor_rgb;
sg_blend_factor dst_factor_rgb;
sg_blend_op op_rgb;
sg_blend_factor src_factor_alpha;
sg_blend_factor dst_factor_alpha;
sg_blend_op op_alpha;
} sg_blend_state;
typedef struct sg_color_target_state {
sg_pixel_format pixel_format;
sg_color_mask write_mask;
sg_blend_state blend;
} sg_color_target_state;
typedef struct sg_pipeline_desc {
uint32_t _start_canary;
sg_shader shader;
sg_vertex_layout_state layout;
sg_depth_state depth;
sg_stencil_state stencil;
int color_count;
sg_color_target_state colors[SG_MAX_COLOR_ATTACHMENTS];
sg_primitive_type primitive_type;
sg_index_type index_type;
sg_cull_mode cull_mode;
sg_face_winding face_winding;
int sample_count;
sg_color blend_color;
bool alpha_to_coverage_enabled;
const char* label;
uint32_t _end_canary;
} sg_pipeline_desc;
/*
sg_attachments_desc
Creation parameters for an sg_attachments object, used as argument to the
sg_make_attachments() function.
An attachments object bundles 0..4 color attachments, 0..4 msaa-resolve
attachments, and none or one depth-stencil attachmente for use
in a render pass. At least one color attachment or one depth-stencil
attachment must be provided (no color attachment and a depth-stencil
attachment is useful for a depth-only render pass).
Each attachment definition consists of an image object, and two additional indices
describing which subimage the pass will render into: one mipmap index, and if the image
is a cubemap, array-texture or 3D-texture, the face-index, array-layer or
depth-slice.
All attachments must have the same width and height.
All color attachments and the depth-stencil attachment must have the
same sample count.
If a resolve attachment is set, an MSAA-resolve operation from the
associated color attachment image into the resolve attachment image will take
place in the sg_end_pass() function. In this case, the color attachment
must have a (sample_count>1), and the resolve attachment a
(sample_count==1). The resolve attachment also must have the same pixel
format as the color attachment.
NOTE that MSAA depth-stencil attachments cannot be msaa-resolved!
*/
typedef struct sg_attachment_desc {
sg_image image;
int mip_level;
int slice; // cube texture: face; array texture: layer; 3D texture: slice
} sg_attachment_desc;
typedef struct sg_attachments_desc {
uint32_t _start_canary;
sg_attachment_desc colors[SG_MAX_COLOR_ATTACHMENTS];
sg_attachment_desc resolves[SG_MAX_COLOR_ATTACHMENTS];
sg_attachment_desc depth_stencil;
const char* label;
uint32_t _end_canary;
} sg_attachments_desc;
/*
sg_trace_hooks
Installable callback functions to keep track of the sokol-gfx calls,
this is useful for debugging, or keeping track of resource creation
and destruction.
Trace hooks are installed with sg_install_trace_hooks(), this returns
another sg_trace_hooks struct with the previous set of
trace hook function pointers. These should be invoked by the
new trace hooks to form a proper call chain.
*/
typedef struct sg_trace_hooks {
void* user_data;
void (*reset_state_cache)(void* user_data);
void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data);
void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data);
void (*make_sampler)(const sg_sampler_desc* desc, sg_sampler result, void* user_data);
void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data);
void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data);
void (*make_attachments)(const sg_attachments_desc* desc, sg_attachments result, void* user_data);
void (*destroy_buffer)(sg_buffer buf, void* user_data);
void (*destroy_image)(sg_image img, void* user_data);
void (*destroy_sampler)(sg_sampler smp, void* user_data);
void (*destroy_shader)(sg_shader shd, void* user_data);
void (*destroy_pipeline)(sg_pipeline pip, void* user_data);
void (*destroy_attachments)(sg_attachments atts, void* user_data);
void (*update_buffer)(sg_buffer buf, const sg_range* data, void* user_data);
void (*update_image)(sg_image img, const sg_image_data* data, void* user_data);
void (*append_buffer)(sg_buffer buf, const sg_range* data, int result, void* user_data);
void (*begin_pass)(const sg_pass* pass, void* user_data);
void (*apply_viewport)(int x, int y, int width, int height, bool origin_top_left, void* user_data);
void (*apply_scissor_rect)(int x, int y, int width, int height, bool origin_top_left, void* user_data);
void (*apply_pipeline)(sg_pipeline pip, void* user_data);
void (*apply_bindings)(const sg_bindings* bindings, void* user_data);
void (*apply_uniforms)(int ub_index, const sg_range* data, void* user_data);
void (*draw)(int base_element, int num_elements, int num_instances, void* user_data);
void (*end_pass)(void* user_data);
void (*commit)(void* user_data);
void (*alloc_buffer)(sg_buffer result, void* user_data);
void (*alloc_image)(sg_image result, void* user_data);
void (*alloc_sampler)(sg_sampler result, void* user_data);
void (*alloc_shader)(sg_shader result, void* user_data);
void (*alloc_pipeline)(sg_pipeline result, void* user_data);
void (*alloc_attachments)(sg_attachments result, void* user_data);
void (*dealloc_buffer)(sg_buffer buf_id, void* user_data);
void (*dealloc_image)(sg_image img_id, void* user_data);
void (*dealloc_sampler)(sg_sampler smp_id, void* user_data);
void (*dealloc_shader)(sg_shader shd_id, void* user_data);
void (*dealloc_pipeline)(sg_pipeline pip_id, void* user_data);
void (*dealloc_attachments)(sg_attachments atts_id, void* user_data);
void (*init_buffer)(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data);
void (*init_image)(sg_image img_id, const sg_image_desc* desc, void* user_data);
void (*init_sampler)(sg_sampler smp_id, const sg_sampler_desc* desc, void* user_data);
void (*init_shader)(sg_shader shd_id, const sg_shader_desc* desc, void* user_data);
void (*init_pipeline)(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data);
void (*init_attachments)(sg_attachments atts_id, const sg_attachments_desc* desc, void* user_data);
void (*uninit_buffer)(sg_buffer buf_id, void* user_data);
void (*uninit_image)(sg_image img_id, void* user_data);
void (*uninit_sampler)(sg_sampler smp_id, void* user_data);
void (*uninit_shader)(sg_shader shd_id, void* user_data);
void (*uninit_pipeline)(sg_pipeline pip_id, void* user_data);
void (*uninit_attachments)(sg_attachments atts_id, void* user_data);
void (*fail_buffer)(sg_buffer buf_id, void* user_data);
void (*fail_image)(sg_image img_id, void* user_data);
void (*fail_sampler)(sg_sampler smp_id, void* user_data);
void (*fail_shader)(sg_shader shd_id, void* user_data);
void (*fail_pipeline)(sg_pipeline pip_id, void* user_data);
void (*fail_attachments)(sg_attachments atts_id, void* user_data);
void (*push_debug_group)(const char* name, void* user_data);
void (*pop_debug_group)(void* user_data);
} sg_trace_hooks;
/*
sg_buffer_info
sg_image_info
sg_sampler_info
sg_shader_info
sg_pipeline_info
sg_attachments_info
These structs contain various internal resource attributes which
might be useful for debug-inspection. Please don't rely on the
actual content of those structs too much, as they are quite closely
tied to sokol_gfx.h internals and may change more frequently than
the other public API elements.
The *_info structs are used as the return values of the following functions:
sg_query_buffer_info()
sg_query_image_info()
sg_query_sampler_info()
sg_query_shader_info()
sg_query_pipeline_info()
sg_query_pass_info()
*/
typedef struct sg_slot_info {
sg_resource_state state; // the current state of this resource slot
uint32_t res_id; // type-neutral resource if (e.g. sg_buffer.id)
} sg_slot_info;
typedef struct sg_buffer_info {
sg_slot_info slot; // resource pool slot info
uint32_t update_frame_index; // frame index of last sg_update_buffer()
uint32_t append_frame_index; // frame index of last sg_append_buffer()
int append_pos; // current position in buffer for sg_append_buffer()
bool append_overflow; // is buffer in overflow state (due to sg_append_buffer)
int num_slots; // number of renaming-slots for dynamically updated buffers
int active_slot; // currently active write-slot for dynamically updated buffers
} sg_buffer_info;
typedef struct sg_image_info {
sg_slot_info slot; // resource pool slot info
uint32_t upd_frame_index; // frame index of last sg_update_image()
int num_slots; // number of renaming-slots for dynamically updated images
int active_slot; // currently active write-slot for dynamically updated images
} sg_image_info;
typedef struct sg_sampler_info {
sg_slot_info slot; // resource pool slot info
} sg_sampler_info;
typedef struct sg_shader_info {
sg_slot_info slot; // resource pool slot info
} sg_shader_info;
typedef struct sg_pipeline_info {
sg_slot_info slot; // resource pool slot info
} sg_pipeline_info;
typedef struct sg_attachments_info {
sg_slot_info slot; // resource pool slot info
} sg_attachments_info;
/*
sg_frame_stats
Allows to track generic and backend-specific stats about a
render frame. Obtained by calling sg_query_frame_stats(). The returned
struct contains information about the *previous* frame.
*/
typedef struct sg_frame_stats_gl {
uint32_t num_bind_buffer;
uint32_t num_active_texture;
uint32_t num_bind_texture;
uint32_t num_bind_sampler;
uint32_t num_use_program;
uint32_t num_render_state;
uint32_t num_vertex_attrib_pointer;
uint32_t num_vertex_attrib_divisor;
uint32_t num_enable_vertex_attrib_array;
uint32_t num_disable_vertex_attrib_array;
uint32_t num_uniform;
} sg_frame_stats_gl;
typedef struct sg_frame_stats_d3d11_pass {
uint32_t num_om_set_render_targets;
uint32_t num_clear_render_target_view;
uint32_t num_clear_depth_stencil_view;
uint32_t num_resolve_subresource;
} sg_frame_stats_d3d11_pass;
typedef struct sg_frame_stats_d3d11_pipeline {
uint32_t num_rs_set_state;
uint32_t num_om_set_depth_stencil_state;
uint32_t num_om_set_blend_state;
uint32_t num_ia_set_primitive_topology;
uint32_t num_ia_set_input_layout;
uint32_t num_vs_set_shader;
uint32_t num_vs_set_constant_buffers;
uint32_t num_ps_set_shader;
uint32_t num_ps_set_constant_buffers;
} sg_frame_stats_d3d11_pipeline;
typedef struct sg_frame_stats_d3d11_bindings {
uint32_t num_ia_set_vertex_buffers;
uint32_t num_ia_set_index_buffer;
uint32_t num_vs_set_shader_resources;
uint32_t num_ps_set_shader_resources;
uint32_t num_vs_set_samplers;
uint32_t num_ps_set_samplers;
} sg_frame_stats_d3d11_bindings;
typedef struct sg_frame_stats_d3d11_uniforms {
uint32_t num_update_subresource;
} sg_frame_stats_d3d11_uniforms;
typedef struct sg_frame_stats_d3d11_draw {
uint32_t num_draw_indexed_instanced;
uint32_t num_draw_indexed;
uint32_t num_draw_instanced;
uint32_t num_draw;
} sg_frame_stats_d3d11_draw;
typedef struct sg_frame_stats_d3d11 {
sg_frame_stats_d3d11_pass pass;
sg_frame_stats_d3d11_pipeline pipeline;
sg_frame_stats_d3d11_bindings bindings;
sg_frame_stats_d3d11_uniforms uniforms;
sg_frame_stats_d3d11_draw draw;
uint32_t num_map;
uint32_t num_unmap;
} sg_frame_stats_d3d11;
typedef struct sg_frame_stats_metal_idpool {
uint32_t num_added;
uint32_t num_released;
uint32_t num_garbage_collected;
} sg_frame_stats_metal_idpool;
typedef struct sg_frame_stats_metal_pipeline {
uint32_t num_set_blend_color;
uint32_t num_set_cull_mode;
uint32_t num_set_front_facing_winding;
uint32_t num_set_stencil_reference_value;
uint32_t num_set_depth_bias;
uint32_t num_set_render_pipeline_state;
uint32_t num_set_depth_stencil_state;
} sg_frame_stats_metal_pipeline;
typedef struct sg_frame_stats_metal_bindings {
uint32_t num_set_vertex_buffer;
uint32_t num_set_vertex_texture;
uint32_t num_set_vertex_sampler_state;
uint32_t num_set_fragment_buffer;
uint32_t num_set_fragment_texture;
uint32_t num_set_fragment_sampler_state;
} sg_frame_stats_metal_bindings;
typedef struct sg_frame_stats_metal_uniforms {
uint32_t num_set_vertex_buffer_offset;
uint32_t num_set_fragment_buffer_offset;
} sg_frame_stats_metal_uniforms;
typedef struct sg_frame_stats_metal {
sg_frame_stats_metal_idpool idpool;
sg_frame_stats_metal_pipeline pipeline;
sg_frame_stats_metal_bindings bindings;
sg_frame_stats_metal_uniforms uniforms;
} sg_frame_stats_metal;
typedef struct sg_frame_stats_wgpu_uniforms {
uint32_t num_set_bindgroup;
uint32_t size_write_buffer;
} sg_frame_stats_wgpu_uniforms;
typedef struct sg_frame_stats_wgpu_bindings {
uint32_t num_set_vertex_buffer;
uint32_t num_skip_redundant_vertex_buffer;
uint32_t num_set_index_buffer;
uint32_t num_skip_redundant_index_buffer;
uint32_t num_create_bindgroup;
uint32_t num_discard_bindgroup;
uint32_t num_set_bindgroup;
uint32_t num_skip_redundant_bindgroup;
uint32_t num_bindgroup_cache_hits;
uint32_t num_bindgroup_cache_misses;
uint32_t num_bindgroup_cache_collisions;
uint32_t num_bindgroup_cache_invalidates;
uint32_t num_bindgroup_cache_hash_vs_key_mismatch;
} sg_frame_stats_wgpu_bindings;
typedef struct sg_frame_stats_wgpu {
sg_frame_stats_wgpu_uniforms uniforms;
sg_frame_stats_wgpu_bindings bindings;
} sg_frame_stats_wgpu;
typedef struct sg_frame_stats {
uint32_t frame_index; // current frame counter, starts at 0
uint32_t num_passes;
uint32_t num_apply_viewport;
uint32_t num_apply_scissor_rect;
uint32_t num_apply_pipeline;
uint32_t num_apply_bindings;
uint32_t num_apply_uniforms;
uint32_t num_draw;
uint32_t num_update_buffer;
uint32_t num_append_buffer;
uint32_t num_update_image;
uint32_t size_apply_uniforms;
uint32_t size_update_buffer;
uint32_t size_append_buffer;
uint32_t size_update_image;
sg_frame_stats_gl gl;
sg_frame_stats_d3d11 d3d11;
sg_frame_stats_metal metal;
sg_frame_stats_wgpu wgpu;
} sg_frame_stats;
/*
sg_log_item
An enum with a unique item for each log message, warning, error
and validation layer message. Note that these messages are only
visible when a logger function is installed in the sg_setup() call.
*/
#define _SG_LOG_ITEMS \
_SG_LOGITEM_XMACRO(OK, "Ok") \
_SG_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \
_SG_LOGITEM_XMACRO(GL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (gl)") \
_SG_LOGITEM_XMACRO(GL_3D_TEXTURES_NOT_SUPPORTED, "3d textures not supported (gl)") \
_SG_LOGITEM_XMACRO(GL_ARRAY_TEXTURES_NOT_SUPPORTED, "array textures not supported (gl)") \
_SG_LOGITEM_XMACRO(GL_SHADER_COMPILATION_FAILED, "shader compilation failed (gl)") \
_SG_LOGITEM_XMACRO(GL_SHADER_LINKING_FAILED, "shader linking failed (gl)") \
_SG_LOGITEM_XMACRO(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, "vertex attribute not found in shader (gl)") \
_SG_LOGITEM_XMACRO(GL_IMAGE_SAMPLER_NAME_NOT_FOUND_IN_SHADER, "image-sampler name not found in shader (gl)") \
_SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNDEFINED, "framebuffer completeness check failed with GL_FRAMEBUFFER_UNDEFINED (gl)") \
_SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT (gl)") \
_SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (gl)") \
_SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNSUPPORTED, "framebuffer completeness check failed with GL_FRAMEBUFFER_UNSUPPORTED (gl)") \
_SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE (gl)") \
_SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNKNOWN, "framebuffer completeness check failed (unknown reason) (gl)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_FAILED, "CreateBuffer() failed (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_SRV_FAILED, "CreateShaderResourceView() failed for storage buffer (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for depth-stencil texture (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_FAILED, "CreateTexture2D() failed for depth-stencil texture (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 2d-, cube- or array-texture (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_FAILED, "CreateTexture2D() failed for 2d-, cube- or array-texture (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_2D_SRV_FAILED, "CreateShaderResourceView() failed for 2d-, cube- or array-texture (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 3D texture (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_FAILED, "CreateTexture3D() failed (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_3D_SRV_FAILED, "CreateShaderResourceView() failed for 3d texture (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_MSAA_TEXTURE_FAILED, "CreateTexture2D() failed for MSAA render target texture (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_SAMPLER_STATE_FAILED, "CreateSamplerState() failed (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED, "loading d3dcompiler_47.dll failed (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_FAILED, "shader compilation failed (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_OUTPUT, "") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_CONSTANT_BUFFER_FAILED, "CreateBuffer() failed for uniform constant buffer (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_INPUT_LAYOUT_FAILED, "CreateInputLayout() failed (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_RASTERIZER_STATE_FAILED, "CreateRasterizerState() failed (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED, "CreateDepthStencilState() failed (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_BLEND_STATE_FAILED, "CreateBlendState() failed (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_RTV_FAILED, "CreateRenderTargetView() failed (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_CREATE_DSV_FAILED, "CreateDepthStencilView() failed (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED, "Map() failed when updating buffer (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_MAP_FOR_APPEND_BUFFER_FAILED, "Map() failed when appending to buffer (d3d11)") \
_SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED, "Map() failed when updating image (d3d11)") \
_SG_LOGITEM_XMACRO(METAL_CREATE_BUFFER_FAILED, "failed to create buffer object (metal)") \
_SG_LOGITEM_XMACRO(METAL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (metal)") \
_SG_LOGITEM_XMACRO(METAL_CREATE_TEXTURE_FAILED, "failed to create texture object (metal)") \
_SG_LOGITEM_XMACRO(METAL_CREATE_SAMPLER_FAILED, "failed to create sampler object (metal)") \
_SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_FAILED, "shader compilation failed (metal)") \
_SG_LOGITEM_XMACRO(METAL_SHADER_CREATION_FAILED, "shader creation failed (metal)") \
_SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_OUTPUT, "") \
_SG_LOGITEM_XMACRO(METAL_SHADER_ENTRY_NOT_FOUND, "shader entry function not found (metal)") \
_SG_LOGITEM_XMACRO(METAL_CREATE_RPS_FAILED, "failed to create render pipeline state (metal)") \
_SG_LOGITEM_XMACRO(METAL_CREATE_RPS_OUTPUT, "") \
_SG_LOGITEM_XMACRO(METAL_CREATE_DSS_FAILED, "failed to create depth stencil state (metal)") \
_SG_LOGITEM_XMACRO(WGPU_BINDGROUPS_POOL_EXHAUSTED, "bindgroups pool exhausted (increase sg_desc.bindgroups_cache_size) (wgpu)") \
_SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE, "sg_desc.wgpu_bindgroups_cache_size must be > 1 (wgpu)") \
_SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_POW2, "sg_desc.wgpu_bindgroups_cache_size must be a power of 2 (wgpu)") \
_SG_LOGITEM_XMACRO(WGPU_CREATEBINDGROUP_FAILED, "wgpuDeviceCreateBindGroup failed") \
_SG_LOGITEM_XMACRO(WGPU_CREATE_BUFFER_FAILED, "wgpuDeviceCreateBuffer() failed") \
_SG_LOGITEM_XMACRO(WGPU_CREATE_TEXTURE_FAILED, "wgpuDeviceCreateTexture() failed") \
_SG_LOGITEM_XMACRO(WGPU_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed") \
_SG_LOGITEM_XMACRO(WGPU_CREATE_SAMPLER_FAILED, "wgpuDeviceCreateSampler() failed") \
_SG_LOGITEM_XMACRO(WGPU_CREATE_SHADER_MODULE_FAILED, "wgpuDeviceCreateShaderModule() failed") \
_SG_LOGITEM_XMACRO(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED, "wgpuDeviceCreateBindGroupLayout() for shader stage failed") \
_SG_LOGITEM_XMACRO(WGPU_CREATE_PIPELINE_LAYOUT_FAILED, "wgpuDeviceCreatePipelineLayout() failed") \
_SG_LOGITEM_XMACRO(WGPU_CREATE_RENDER_PIPELINE_FAILED, "wgpuDeviceCreateRenderPipeline() failed") \
_SG_LOGITEM_XMACRO(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed in create attachments") \
_SG_LOGITEM_XMACRO(DRAW_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING, "call to sg_apply_bindings() and/or sg_apply_uniforms() missing after sg_apply_pipeline()") \
_SG_LOGITEM_XMACRO(IDENTICAL_COMMIT_LISTENER, "attempting to add identical commit listener") \
_SG_LOGITEM_XMACRO(COMMIT_LISTENER_ARRAY_FULL, "commit listener array full") \
_SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SOKOL_TRACE_HOOKS is not defined") \
_SG_LOGITEM_XMACRO(DEALLOC_BUFFER_INVALID_STATE, "sg_dealloc_buffer(): buffer must be in ALLOC state") \
_SG_LOGITEM_XMACRO(DEALLOC_IMAGE_INVALID_STATE, "sg_dealloc_image(): image must be in alloc state") \
_SG_LOGITEM_XMACRO(DEALLOC_SAMPLER_INVALID_STATE, "sg_dealloc_sampler(): sampler must be in alloc state") \
_SG_LOGITEM_XMACRO(DEALLOC_SHADER_INVALID_STATE, "sg_dealloc_shader(): shader must be in ALLOC state") \
_SG_LOGITEM_XMACRO(DEALLOC_PIPELINE_INVALID_STATE, "sg_dealloc_pipeline(): pipeline must be in ALLOC state") \
_SG_LOGITEM_XMACRO(DEALLOC_ATTACHMENTS_INVALID_STATE, "sg_dealloc_attachments(): attachments must be in ALLOC state") \
_SG_LOGITEM_XMACRO(INIT_BUFFER_INVALID_STATE, "sg_init_buffer(): buffer must be in ALLOC state") \
_SG_LOGITEM_XMACRO(INIT_IMAGE_INVALID_STATE, "sg_init_image(): image must be in ALLOC state") \
_SG_LOGITEM_XMACRO(INIT_SAMPLER_INVALID_STATE, "sg_init_sampler(): sampler must be in ALLOC state") \
_SG_LOGITEM_XMACRO(INIT_SHADER_INVALID_STATE, "sg_init_shader(): shader must be in ALLOC state") \
_SG_LOGITEM_XMACRO(INIT_PIPELINE_INVALID_STATE, "sg_init_pipeline(): pipeline must be in ALLOC state") \
_SG_LOGITEM_XMACRO(INIT_ATTACHMENTS_INVALID_STATE, "sg_init_attachments(): pass must be in ALLOC state") \
_SG_LOGITEM_XMACRO(UNINIT_BUFFER_INVALID_STATE, "sg_uninit_buffer(): buffer must be in VALID or FAILED state") \
_SG_LOGITEM_XMACRO(UNINIT_IMAGE_INVALID_STATE, "sg_uninit_image(): image must be in VALID or FAILED state") \
_SG_LOGITEM_XMACRO(UNINIT_SAMPLER_INVALID_STATE, "sg_uninit_sampler(): sampler must be in VALID or FAILED state") \
_SG_LOGITEM_XMACRO(UNINIT_SHADER_INVALID_STATE, "sg_uninit_shader(): shader must be in VALID or FAILED state") \
_SG_LOGITEM_XMACRO(UNINIT_PIPELINE_INVALID_STATE, "sg_uninit_pipeline(): pipeline must be in VALID or FAILED state") \
_SG_LOGITEM_XMACRO(UNINIT_ATTACHMENTS_INVALID_STATE, "sg_uninit_attachments(): attachments must be in VALID or FAILED state") \
_SG_LOGITEM_XMACRO(FAIL_BUFFER_INVALID_STATE, "sg_fail_buffer(): buffer must be in ALLOC state") \
_SG_LOGITEM_XMACRO(FAIL_IMAGE_INVALID_STATE, "sg_fail_image(): image must be in ALLOC state") \
_SG_LOGITEM_XMACRO(FAIL_SAMPLER_INVALID_STATE, "sg_fail_sampler(): sampler must be in ALLOC state") \
_SG_LOGITEM_XMACRO(FAIL_SHADER_INVALID_STATE, "sg_fail_shader(): shader must be in ALLOC state") \
_SG_LOGITEM_XMACRO(FAIL_PIPELINE_INVALID_STATE, "sg_fail_pipeline(): pipeline must be in ALLOC state") \
_SG_LOGITEM_XMACRO(FAIL_ATTACHMENTS_INVALID_STATE, "sg_fail_attachments(): attachments must be in ALLOC state") \
_SG_LOGITEM_XMACRO(BUFFER_POOL_EXHAUSTED, "buffer pool exhausted") \
_SG_LOGITEM_XMACRO(IMAGE_POOL_EXHAUSTED, "image pool exhausted") \
_SG_LOGITEM_XMACRO(SAMPLER_POOL_EXHAUSTED, "sampler pool exhausted") \
_SG_LOGITEM_XMACRO(SHADER_POOL_EXHAUSTED, "shader pool exhausted") \
_SG_LOGITEM_XMACRO(PIPELINE_POOL_EXHAUSTED, "pipeline pool exhausted") \
_SG_LOGITEM_XMACRO(PASS_POOL_EXHAUSTED, "pass pool exhausted") \
_SG_LOGITEM_XMACRO(BEGINPASS_ATTACHMENT_INVALID, "sg_begin_pass: an attachment was provided that no longer exists") \
_SG_LOGITEM_XMACRO(DRAW_WITHOUT_BINDINGS, "attempting to draw without resource bindings") \
_SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_CANARY, "sg_buffer_desc not initialized") \
_SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_SIZE, "sg_buffer_desc.size and .data.size cannot both be 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA, "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)") \
_SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA_SIZE, "immutable buffer data size differs from buffer size") \
_SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_NO_DATA, "dynamic/stream usage buffers cannot be initialized with data") \
_SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED, "storage buffers not supported by the backend 3D API (requires OpenGL >= 4.3)") \
_SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4, "size of storage buffers must be a multiple of 4") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_NODATA, "sg_image_data: no data (.ptr and/or .size is zero)") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_DATA_SIZE, "sg_image_data: data size doesn't match expected surface size") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_CANARY, "sg_image_desc not initialized") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WIDTH, "sg_image_desc.width must be > 0") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_HEIGHT, "sg_image_desc.height must be > 0") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_PIXELFORMAT, "invalid pixel format for render-target image") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, "invalid pixel format for non-render-target image") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, "non-render-target images cannot be multisampled") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, "MSAA not supported for this pixel format") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS, "MSAA images must have num_mipmaps == 1") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_3D_IMAGE, "3D images cannot have a sample_count > 1") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_CUBE_IMAGE, "cube images cannot have sample_count > 1") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE, "3D images cannot have a depth/stencil image format") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_IMMUTABLE, "render target images must be SG_USAGE_IMMUTABLE") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_NO_DATA, "render target images cannot be initialized with data") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_INJECTED_NO_DATA, "images with injected textures cannot be initialized with data") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, "dynamic/stream images cannot be initialized with data") \
_SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, "compressed images must be immutable") \
_SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_CANARY, "sg_sampler_desc not initialized") \
_SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING, "sg_sampler_desc.max_anisotropy > 1 requires min/mag/mipmap_filter to be SG_FILTER_LINEAR") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_CANARY, "sg_shader_desc not initialized") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE, "shader source code required") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_BYTECODE, "shader byte code required") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, "shader source or byte code required") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, "shader byte code length (in bytes) required") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, "uniform block members must occupy continuous slots") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_SIZE_IS_ZERO, "bound uniform block size cannot be zero") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_METAL_BUFFER_SLOT_OUT_OF_RANGE, "uniform block 'msl_buffer_n' is out of range (must be 0..7)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_METAL_BUFFER_SLOT_COLLISION, "uniform block 'msl_buffer_n' must be unique across uniform blocks and storage buffers in same shader stage") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_HLSL_REGISTER_B_OUT_OF_RANGE, "uniform block 'hlsl_register_b_n' is out of range (must be 0..7)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_HLSL_REGISTER_B_COLLISION, "uniform block 'hlsl_register_b_n' must be unique across uniform blocks in same shader stage") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_WGSL_GROUP0_BINDING_OUT_OF_RANGE, "uniform block 'wgsl_group0_binding_n' is out of range (must be 0..15)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_WGSL_GROUP0_BINDING_COLLISION, "uniform block 'wgsl_group0_binding_n' must be unique across all uniform blocks") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_UB_MEMBERS, "GL backend requires uniform block member declarations") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_UNIFORM_GLSL_NAME, "uniform block member 'glsl_name' missing") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, "size of uniform block members doesn't match uniform block size") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_ARRAY_COUNT, "uniform array count must be >= 1") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_METAL_BUFFER_SLOT_OUT_OF_RANGE, "storage buffer 'msl_buffer_n' is out of range (must be 8..15)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_METAL_BUFFER_SLOT_COLLISION, "storage buffer 'msl_buffer_n' must be unique across uniform blocks and storage buffer in same shader stage") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_HLSL_REGISTER_T_OUT_OF_RANGE, "storage buffer 'hlsl_register_t_n' is out of range (must be 0..23)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_HLSL_REGISTER_T_COLLISION, "storage_buffer 'hlsl_register_t_n' must be unique across storage buffers and images in same shader stage") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_GLSL_BINDING_OUT_OF_RANGE, "storage buffer 'glsl_binding_n' is out of range (must be 0..15)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_GLSL_BINDING_COLLISION, "storage buffer 'glsl_binding_n' must be unique across shader stages") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_WGSL_GROUP1_BINDING_OUT_OF_RANGE, "storage buffer 'wgsl_group1_binding_n' is out of range (must be 0..127)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_WGSL_GROUP1_BINDING_COLLISION, "storage buffer 'wgsl_group1_binding_n' must be unique across all images, samplers and storage buffers") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_READONLY, "shader stage storage buffers must be readonly (sg_shader_desc.storage_buffers[].readonly)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_METAL_TEXTURE_SLOT_OUT_OF_RANGE, "image 'msl_texture_n' is out of range (must be 0..15)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_METAL_TEXTURE_SLOT_COLLISION, "image 'msl_texture_n' must be unique in same shader stage") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_HLSL_REGISTER_T_OUT_OF_RANGE, "image 'hlsl_register_t_n' is out of range (must be 0..23)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_HLSL_REGISTER_T_COLLISION, "image 'hlsl_register_t_n' must be unique across images and storage buffers in same shader stage") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_WGSL_GROUP1_BINDING_OUT_OF_RANGE, "image 'wgsl_group1_binding_n' is out of range (must be 0..127)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_WGSL_GROUP1_BINDING_COLLISION, "image 'wgsl_group1_binding_n' must be unique across all images, samplers and storage buffers") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_METAL_SAMPLER_SLOT_OUT_OF_RANGE, "sampler 'msl_sampler_n' is out of range (must be 0..15)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_METAL_SAMPLER_SLOT_COLLISION, "sampler 'msl_sampler_n' must be unique in same shader stage") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_HLSL_REGISTER_S_OUT_OF_RANGE, "sampler 'hlsl_register_s_n' is out of rang (must be 0..15)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_HLSL_REGISTER_S_COLLISION, "sampler 'hlsl_register_s_n' must be unique in same shader stage") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_WGSL_GROUP1_BINDING_OUT_OF_RANGE, "sampler 'wgsl_group1_binding_n' is out of range (must be 0..127)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_WGSL_GROUP1_BINDING_COLLISION, "sampler 'wgsl_group1_binding_n' must be unique across all images, samplers and storage buffers") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE, "image-sampler-pair image slot index is out of range (sg_shader_desc.image_sampler_pairs[].image_slot)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_SAMPLER_SLOT_OUT_OF_RANGE, "image-sampler-pair sampler slot index is out of range (sg_shader_desc.image_sampler_pairs[].sampler_slot)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_STAGE_MISMATCH, "image-sampler-pair stage doesn't match referenced image stage") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_SAMPLER_STAGE_MISMATCH, "image-sampler-pair stage doesn't match referenced sampler stage") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_GLSL_NAME, "image-sampler-pair 'glsl_name' missing") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NONFILTERING_SAMPLER_REQUIRED, "image sample type UNFILTERABLE_FLOAT, UINT, SINT can only be used with NONFILTERING sampler") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_COMPARISON_SAMPLER_REQUIRED, "image sample type DEPTH can only be used with COMPARISON sampler") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "one or more images are not referenced by by image-sampler-pairs (sg_shader_desc.image_sampler_pairs[].image_slot)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "one or more samplers are not referenced by image-sampler-pairs (sg_shader_desc.image_sampler_pairs[].sampler_slot)") \
_SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, "vertex attribute name/semantic string too long (max len 16)") \
_SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_CANARY, "sg_pipeline_desc not initialized") \
_SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_SHADER, "sg_pipeline_desc.shader missing or invalid") \
_SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_CONT_ATTRS, "sg_pipeline_desc.layout.attrs is not continuous") \
_SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4") \
_SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, "D3D11 missing vertex attribute semantics in shader") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_CANARY, "sg_attachments_desc not initialized") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_NO_ATTACHMENTS, "sg_attachments_desc no color or depth-stencil attachments") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_NO_CONT_COLOR_ATTS, "color attachments must occupy continuous slots") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE, "pass attachment image is not valid") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_MIPLEVEL, "pass attachment mip level is bigger than image has mipmaps") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_FACE, "pass attachment image is cubemap, but face index is too big") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_LAYER, "pass attachment image is array texture, but layer index is too big") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_SLICE, "pass attachment image is 3d texture, but slice value is too big") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE_NO_RT, "pass attachment image must be have render_target=true") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_COLOR_INV_PIXELFORMAT, "pass color-attachment images must be renderable color pixel format") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_INV_PIXELFORMAT, "pass depth-attachment image must be depth or depth-stencil pixel format") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES, "all pass attachments must have the same size") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE_SAMPLE_COUNTS, "all pass attachments must have the same sample count") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_COLOR_IMAGE_MSAA, "pass resolve attachments must have a color attachment image with sample count > 1") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE, "pass resolve attachment image not valid") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_SAMPLE_COUNT, "pass resolve attachment image sample count must be 1") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_MIPLEVEL, "pass resolve attachment mip level is bigger than image has mipmaps") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_FACE, "pass resolve attachment is cubemap, but face index is too big") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_LAYER, "pass resolve attachment is array texture, but layer index is too big") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_SLICE, "pass resolve attachment is 3d texture, but slice value is too big") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_NO_RT, "pass resolve attachment image must have render_target=true") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES, "pass resolve attachment size must match color attachment image size") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_FORMAT, "pass resolve attachment pixel format must match color attachment pixel format") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE, "pass depth attachment image is not valid") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_MIPLEVEL, "pass depth attachment mip level is bigger than image has mipmaps") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_FACE, "pass depth attachment image is cubemap, but face index is too big") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_LAYER, "pass depth attachment image is array texture, but layer index is too big") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_SLICE, "pass depth attachment image is 3d texture, but slice value is too big") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_NO_RT, "pass depth attachment image must be have render_target=true") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES, "pass depth attachment image size must match color attachment image size") \
_SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SAMPLE_COUNT, "pass depth attachment sample count must match color attachment sample count") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_CANARY, "sg_begin_pass: pass struct not initialized") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_ATTACHMENTS_EXISTS, "sg_begin_pass: attachments object no longer alive") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_ATTACHMENTS_VALID, "sg_begin_pass: attachments object not in resource state VALID") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE, "sg_begin_pass: one or more color attachment images are not valid") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE, "sg_begin_pass: one or more resolve attachment images are not valid") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE, "sg_begin_pass: one or more depth-stencil attachment images are not valid") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH, "sg_begin_pass: expected pass.swapchain.width > 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH_NOTSET, "sg_begin_pass: expected pass.swapchain.width == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT, "sg_begin_pass: expected pass.swapchain.height > 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT_NOTSET, "sg_begin_pass: expected pass.swapchain.height == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT, "sg_begin_pass: expected pass.swapchain.sample_count > 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT_NOTSET, "sg_begin_pass: expected pass.swapchain.sample_count == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT, "sg_begin_pass: expected pass.swapchain.color_format to be valid") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT_NOTSET, "sg_begin_pass: expected pass.swapchain.color_format to be unset") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_DEPTHFORMAT_NOTSET, "sg_begin_pass: expected pass.swapchain.depth_format to be unset") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE, "sg_begin_pass: expected pass.swapchain.metal.current_drawable != 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.current_drawable == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE, "sg_begin_pass: expected pass.swapchain.metal.depth_stencil_texture != 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.depth_stencil_texture == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE, "sg_begin_pass: expected pass.swapchain.metal.msaa_color_texture != 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.msaa_color_texture == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.render_view != 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.render_view == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.resolve_view != 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.resolve_view == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.depth_stencil_view != 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.depth_stencil_view == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.render_view != 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.render_view == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.resolve_view != 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.resolve_view == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.depth_stencil_view != 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.depth_stencil_view == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_GL_EXPECT_FRAMEBUFFER_NOTSET, "sg_begin_pass: expected pass.swapchain.gl.framebuffer == 0") \
_SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID_ID, "sg_apply_pipeline: invalid pipeline id provided") \
_SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_EXISTS, "sg_apply_pipeline: pipeline object no longer alive") \
_SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID, "sg_apply_pipeline: pipeline object not in valid state") \
_SG_LOGITEM_XMACRO(VALIDATE_APIP_SHADER_EXISTS, "sg_apply_pipeline: shader object no longer alive") \
_SG_LOGITEM_XMACRO(VALIDATE_APIP_SHADER_VALID, "sg_apply_pipeline: shader object not in valid state") \
_SG_LOGITEM_XMACRO(VALIDATE_APIP_CURPASS_ATTACHMENTS_EXISTS, "sg_apply_pipeline: current pass attachments no longer alive") \
_SG_LOGITEM_XMACRO(VALIDATE_APIP_CURPASS_ATTACHMENTS_VALID, "sg_apply_pipeline: current pass attachments not in valid state") \
_SG_LOGITEM_XMACRO(VALIDATE_APIP_ATT_COUNT, "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments") \
_SG_LOGITEM_XMACRO(VALIDATE_APIP_COLOR_FORMAT, "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format") \
_SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTH_FORMAT, "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format") \
_SG_LOGITEM_XMACRO(VALIDATE_APIP_SAMPLE_COUNT, "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE, "sg_apply_bindings: must be called after sg_apply_pipeline") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_EXISTS, "sg_apply_bindings: currently applied pipeline object no longer alive") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_VALID, "sg_apply_bindings: currently applied pipeline object not in valid state") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_VB, "sg_apply_bindings: vertex buffer binding is missing or buffer handle is invalid") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_EXISTS, "sg_apply_bindings: vertex buffer no longer alive") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_TYPE, "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_OVERFLOW, "sg_apply_bindings: buffer in vertex buffer slot is overflown") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_NO_IB, "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_IB, "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_EXISTS, "sg_apply_bindings: index buffer no longer alive") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_TYPE, "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_OVERFLOW, "sg_apply_bindings: buffer in index buffer slot is overflown") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: image binding is missing or the image handle is invalid") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_IMG_EXISTS, "sg_apply_bindings: bound image no longer alive") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of bound image doesn't match shader desc") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_MULTISAMPLED_IMAGE, "sg_apply_bindings: expected image with sample_count > 1") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_FILTERABLE_IMAGE, "sg_apply_bindings: filterable image expected") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_DEPTH_IMAGE, "sg_apply_bindings: depth image expected") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: sampler binding is missing or the sampler handle is invalid") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARISON but sampler has SG_COMPAREFUNC_NEVER") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING but sampler doesn't have SG_COMPAREFUNC_NEVER") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING, but sampler has SG_FILTER_LINEAR filters") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_SMP_EXISTS, "sg_apply_bindings: bound sampler no longer alive") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_EXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: storage buffer binding is missing or the buffer handle is invalid") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_STORAGEBUFFER_EXISTS, "sg_apply_bindings: bound storage buffer no longer alive") \
_SG_LOGITEM_XMACRO(VALIDATE_ABND_STORAGEBUFFER_BINDING_BUFFERTYPE, "sg_apply_bindings: buffer bound storage buffer slot is not of type storage buffer") \
_SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_PIPELINE, "sg_apply_uniforms: must be called after sg_apply_pipeline()") \
_SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_UB_AT_SLOT, "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot") \
_SG_LOGITEM_XMACRO(VALIDATE_AUB_SIZE, "sg_apply_uniforms: data size doesn't match declared uniform block size") \
_SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_USAGE, "sg_update_buffer: cannot update immutable buffer") \
_SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_SIZE, "sg_update_buffer: update size is bigger than buffer size") \
_SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_ONCE, "sg_update_buffer: only one update allowed per buffer and frame") \
_SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_APPEND, "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame") \
_SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_USAGE, "sg_append_buffer: cannot append to immutable buffer") \
_SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_SIZE, "sg_append_buffer: overall appended size is bigger than buffer size") \
_SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_UPDATE, "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame") \
_SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_USAGE, "sg_update_image: cannot update immutable image") \
_SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_ONCE, "sg_update_image: only one update allowed per image and frame") \
_SG_LOGITEM_XMACRO(VALIDATION_FAILED, "validation layer checks failed") \
#define _SG_LOGITEM_XMACRO(item,msg) SG_LOGITEM_##item,
typedef enum sg_log_item {
_SG_LOG_ITEMS
} sg_log_item;
#undef _SG_LOGITEM_XMACRO
/*
sg_desc
The sg_desc struct contains configuration values for sokol_gfx,
it is used as parameter to the sg_setup() call.
The default configuration is:
.buffer_pool_size 128
.image_pool_size 128
.sampler_pool_size 64
.shader_pool_size 32
.pipeline_pool_size 64
.pass_pool_size 16
.uniform_buffer_size 4 MB (4*1024*1024)
.max_commit_listeners 1024
.disable_validation false
.mtl_force_managed_storage_mode false
.wgpu_disable_bindgroups_cache false
.wgpu_bindgroups_cache_size 1024
.allocator.alloc_fn 0 (in this case, malloc() will be called)
.allocator.free_fn 0 (in this case, free() will be called)
.allocator.user_data 0
.environment.defaults.color_format: default value depends on selected backend:
all GL backends: SG_PIXELFORMAT_RGBA8
Metal and D3D11: SG_PIXELFORMAT_BGRA8
WebGPU: *no default* (must be queried from WebGPU swapchain object)
.environment.defaults.depth_format: SG_PIXELFORMAT_DEPTH_STENCIL
.environment.defaults.sample_count: 1
Metal specific:
(NOTE: All Objective-C object references are transferred through
a bridged cast (__bridge const void*) to sokol_gfx, which will use an
unretained bridged cast (__bridge id<xxx>) to retrieve the Objective-C
references back. Since the bridge cast is unretained, the caller
must hold a strong reference to the Objective-C object until sg_setup()
returns.
.mtl_force_managed_storage_mode
when enabled, Metal buffers and texture resources are created in managed storage
mode, otherwise sokol-gfx will decide whether to create buffers and
textures in managed or shared storage mode (this is mainly a debugging option)
.mtl_use_command_buffer_with_retained_references
when true, the sokol-gfx Metal backend will use Metal command buffers which
bump the reference count of resource objects as long as they are inflight,
this is slower than the default command-buffer-with-unretained-references
method, this may be a workaround when confronted with lifetime validation
errors from the Metal validation layer until a proper fix has been implemented
.environment.metal.device
a pointer to the MTLDevice object
D3D11 specific:
.environment.d3d11.device
a pointer to the ID3D11Device object, this must have been created
before sg_setup() is called
.environment.d3d11.device_context
a pointer to the ID3D11DeviceContext object
.d3d11_shader_debugging
set this to true to compile shaders which are provided as HLSL source
code with debug information and without optimization, this allows
shader debugging in tools like RenderDoc, to output source code
instead of byte code from sokol-shdc, omit the `--binary` cmdline
option
WebGPU specific:
.wgpu_disable_bindgroups_cache
When this is true, the WebGPU backend will create and immediately
release a BindGroup object in the sg_apply_bindings() call, only
use this for debugging purposes.
.wgpu_bindgroups_cache_size
The size of the bindgroups cache for re-using BindGroup objects
between sg_apply_bindings() calls. The smaller the cache size,
the more likely are cache slot collisions which will cause
a BindGroups object to be destroyed and a new one created.
Use the information returned by sg_query_stats() to check
if this is a frequent occurrence, and increase the cache size as
needed (the default is 1024).
NOTE: wgpu_bindgroups_cache_size must be a power-of-2 number!
.environment.wgpu.device
a WGPUDevice handle
When using sokol_gfx.h and sokol_app.h together, consider using the
helper function sglue_environment() in the sokol_glue.h header to
initialize the sg_desc.environment nested struct. sglue_environment() returns
a completely initialized sg_environment struct with information
provided by sokol_app.h.
*/
typedef struct sg_environment_defaults {
sg_pixel_format color_format;
sg_pixel_format depth_format;
int sample_count;
} sg_environment_defaults;
typedef struct sg_metal_environment {
const void* device;
} sg_metal_environment;
typedef struct sg_d3d11_environment {
const void* device;
const void* device_context;
} sg_d3d11_environment;
typedef struct sg_wgpu_environment {
const void* device;
} sg_wgpu_environment;
typedef struct sg_environment {
sg_environment_defaults defaults;
sg_metal_environment metal;
sg_d3d11_environment d3d11;
sg_wgpu_environment wgpu;
} sg_environment;
/*
sg_commit_listener
Used with function sg_add_commit_listener() to add a callback
which will be called in sg_commit(). This is useful for libraries
building on top of sokol-gfx to be notified about when a frame
ends (instead of having to guess, or add a manual 'new-frame'
function.
*/
typedef struct sg_commit_listener {
void (*func)(void* user_data);
void* user_data;
} sg_commit_listener;
/*
sg_allocator
Used in sg_desc to provide custom memory-alloc and -free functions
to sokol_gfx.h. If memory management should be overridden, both the
alloc_fn and free_fn function must be provided (e.g. it's not valid to
override one function but not the other).
*/
typedef struct sg_allocator {
void* (*alloc_fn)(size_t size, void* user_data);
void (*free_fn)(void* ptr, void* user_data);
void* user_data;
} sg_allocator;
/*
sg_logger
Used in sg_desc to provide a logging function. Please be aware
that without logging function, sokol-gfx will be completely
silent, e.g. it will not report errors, warnings and
validation layer messages. For maximum error verbosity,
compile in debug mode (e.g. NDEBUG *not* defined) and provide a
compatible logger function in the sg_setup() call
(for instance the standard logging function from sokol_log.h).
*/
typedef struct sg_logger {
void (*func)(
const char* tag, // always "sg"
uint32_t log_level, // 0=panic, 1=error, 2=warning, 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);
void* user_data;
} sg_logger;
typedef struct sg_desc {
uint32_t _start_canary;
int buffer_pool_size;
int image_pool_size;
int sampler_pool_size;
int shader_pool_size;
int pipeline_pool_size;
int attachments_pool_size;
int uniform_buffer_size;
int max_commit_listeners;
bool disable_validation; // disable validation layer even in debug mode, useful for tests
bool d3d11_shader_debugging; // if true, HLSL shaders are compiled with D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION
bool mtl_force_managed_storage_mode; // for debugging: use Metal managed storage mode for resources even with UMA
bool mtl_use_command_buffer_with_retained_references; // Metal: use a managed MTLCommandBuffer which ref-counts used resources
bool wgpu_disable_bindgroups_cache; // set to true to disable the WebGPU backend BindGroup cache
int wgpu_bindgroups_cache_size; // number of slots in the WebGPU bindgroup cache (must be 2^N)
sg_allocator allocator;
sg_logger logger; // optional log function override
sg_environment environment;
uint32_t _end_canary;
} sg_desc;
// setup and misc functions
SOKOL_GFX_API_DECL void sg_setup(const sg_desc* desc);
SOKOL_GFX_API_DECL void sg_shutdown(void);
SOKOL_GFX_API_DECL bool sg_isvalid(void);
SOKOL_GFX_API_DECL void sg_reset_state_cache(void);
SOKOL_GFX_API_DECL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks);
SOKOL_GFX_API_DECL void sg_push_debug_group(const char* name);
SOKOL_GFX_API_DECL void sg_pop_debug_group(void);
SOKOL_GFX_API_DECL bool sg_add_commit_listener(sg_commit_listener listener);
SOKOL_GFX_API_DECL bool sg_remove_commit_listener(sg_commit_listener listener);
// resource creation, destruction and updating
SOKOL_GFX_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc);
SOKOL_GFX_API_DECL sg_image sg_make_image(const sg_image_desc* desc);
SOKOL_GFX_API_DECL sg_sampler sg_make_sampler(const sg_sampler_desc* desc);
SOKOL_GFX_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc);
SOKOL_GFX_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc);
SOKOL_GFX_API_DECL sg_attachments sg_make_attachments(const sg_attachments_desc* desc);
SOKOL_GFX_API_DECL void sg_destroy_buffer(sg_buffer buf);
SOKOL_GFX_API_DECL void sg_destroy_image(sg_image img);
SOKOL_GFX_API_DECL void sg_destroy_sampler(sg_sampler smp);
SOKOL_GFX_API_DECL void sg_destroy_shader(sg_shader shd);
SOKOL_GFX_API_DECL void sg_destroy_pipeline(sg_pipeline pip);
SOKOL_GFX_API_DECL void sg_destroy_attachments(sg_attachments atts);
SOKOL_GFX_API_DECL void sg_update_buffer(sg_buffer buf, const sg_range* data);
SOKOL_GFX_API_DECL void sg_update_image(sg_image img, const sg_image_data* data);
SOKOL_GFX_API_DECL int sg_append_buffer(sg_buffer buf, const sg_range* data);
SOKOL_GFX_API_DECL bool sg_query_buffer_overflow(sg_buffer buf);
SOKOL_GFX_API_DECL bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size);
// rendering functions
SOKOL_GFX_API_DECL void sg_begin_pass(const sg_pass* pass);
SOKOL_GFX_API_DECL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left);
SOKOL_GFX_API_DECL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left);
SOKOL_GFX_API_DECL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left);
SOKOL_GFX_API_DECL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left);
SOKOL_GFX_API_DECL void sg_apply_pipeline(sg_pipeline pip);
SOKOL_GFX_API_DECL void sg_apply_bindings(const sg_bindings* bindings);
SOKOL_GFX_API_DECL void sg_apply_uniforms(int ub_slot, const sg_range* data);
SOKOL_GFX_API_DECL void sg_draw(int base_element, int num_elements, int num_instances);
SOKOL_GFX_API_DECL void sg_end_pass(void);
SOKOL_GFX_API_DECL void sg_commit(void);
// getting information
SOKOL_GFX_API_DECL sg_desc sg_query_desc(void);
SOKOL_GFX_API_DECL sg_backend sg_query_backend(void);
SOKOL_GFX_API_DECL sg_features sg_query_features(void);
SOKOL_GFX_API_DECL sg_limits sg_query_limits(void);
SOKOL_GFX_API_DECL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt);
SOKOL_GFX_API_DECL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes);
SOKOL_GFX_API_DECL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes);
// get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID)
SOKOL_GFX_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf);
SOKOL_GFX_API_DECL sg_resource_state sg_query_image_state(sg_image img);
SOKOL_GFX_API_DECL sg_resource_state sg_query_sampler_state(sg_sampler smp);
SOKOL_GFX_API_DECL sg_resource_state sg_query_shader_state(sg_shader shd);
SOKOL_GFX_API_DECL sg_resource_state sg_query_pipeline_state(sg_pipeline pip);
SOKOL_GFX_API_DECL sg_resource_state sg_query_attachments_state(sg_attachments atts);
// get runtime information about a resource
SOKOL_GFX_API_DECL sg_buffer_info sg_query_buffer_info(sg_buffer buf);
SOKOL_GFX_API_DECL sg_image_info sg_query_image_info(sg_image img);
SOKOL_GFX_API_DECL sg_sampler_info sg_query_sampler_info(sg_sampler smp);
SOKOL_GFX_API_DECL sg_shader_info sg_query_shader_info(sg_shader shd);
SOKOL_GFX_API_DECL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip);
SOKOL_GFX_API_DECL sg_attachments_info sg_query_attachments_info(sg_attachments atts);
// get desc structs matching a specific resource (NOTE that not all creation attributes may be provided)
SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf);
SOKOL_GFX_API_DECL sg_image_desc sg_query_image_desc(sg_image img);
SOKOL_GFX_API_DECL sg_sampler_desc sg_query_sampler_desc(sg_sampler smp);
SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_desc(sg_shader shd);
SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip);
SOKOL_GFX_API_DECL sg_attachments_desc sg_query_attachments_desc(sg_attachments atts);
// get resource creation desc struct with their default values replaced
SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc);
SOKOL_GFX_API_DECL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc);
SOKOL_GFX_API_DECL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc);
SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc);
SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc);
SOKOL_GFX_API_DECL sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc* desc);
// assorted query functions
SOKOL_GFX_API_DECL size_t sg_query_buffer_size(sg_buffer buf);
SOKOL_GFX_API_DECL sg_buffer_type sg_query_buffer_type(sg_buffer buf);
SOKOL_GFX_API_DECL sg_usage sg_query_buffer_usage(sg_buffer buf);
SOKOL_GFX_API_DECL sg_image_type sg_query_image_type(sg_image img);
SOKOL_GFX_API_DECL int sg_query_image_width(sg_image img);
SOKOL_GFX_API_DECL int sg_query_image_height(sg_image img);
SOKOL_GFX_API_DECL int sg_query_image_num_slices(sg_image img);
SOKOL_GFX_API_DECL int sg_query_image_num_mipmaps(sg_image img);
SOKOL_GFX_API_DECL sg_pixel_format sg_query_image_pixelformat(sg_image img);
SOKOL_GFX_API_DECL sg_usage sg_query_image_usage(sg_image img);
SOKOL_GFX_API_DECL int sg_query_image_sample_count(sg_image img);
// separate resource allocation and initialization (for async setup)
SOKOL_GFX_API_DECL sg_buffer sg_alloc_buffer(void);
SOKOL_GFX_API_DECL sg_image sg_alloc_image(void);
SOKOL_GFX_API_DECL sg_sampler sg_alloc_sampler(void);
SOKOL_GFX_API_DECL sg_shader sg_alloc_shader(void);
SOKOL_GFX_API_DECL sg_pipeline sg_alloc_pipeline(void);
SOKOL_GFX_API_DECL sg_attachments sg_alloc_attachments(void);
SOKOL_GFX_API_DECL void sg_dealloc_buffer(sg_buffer buf);
SOKOL_GFX_API_DECL void sg_dealloc_image(sg_image img);
SOKOL_GFX_API_DECL void sg_dealloc_sampler(sg_sampler smp);
SOKOL_GFX_API_DECL void sg_dealloc_shader(sg_shader shd);
SOKOL_GFX_API_DECL void sg_dealloc_pipeline(sg_pipeline pip);
SOKOL_GFX_API_DECL void sg_dealloc_attachments(sg_attachments attachments);
SOKOL_GFX_API_DECL void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc);
SOKOL_GFX_API_DECL void sg_init_image(sg_image img, const sg_image_desc* desc);
SOKOL_GFX_API_DECL void sg_init_sampler(sg_sampler smg, const sg_sampler_desc* desc);
SOKOL_GFX_API_DECL void sg_init_shader(sg_shader shd, const sg_shader_desc* desc);
SOKOL_GFX_API_DECL void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc);
SOKOL_GFX_API_DECL void sg_init_attachments(sg_attachments attachments, const sg_attachments_desc* desc);
SOKOL_GFX_API_DECL void sg_uninit_buffer(sg_buffer buf);
SOKOL_GFX_API_DECL void sg_uninit_image(sg_image img);
SOKOL_GFX_API_DECL void sg_uninit_sampler(sg_sampler smp);
SOKOL_GFX_API_DECL void sg_uninit_shader(sg_shader shd);
SOKOL_GFX_API_DECL void sg_uninit_pipeline(sg_pipeline pip);
SOKOL_GFX_API_DECL void sg_uninit_attachments(sg_attachments atts);
SOKOL_GFX_API_DECL void sg_fail_buffer(sg_buffer buf);
SOKOL_GFX_API_DECL void sg_fail_image(sg_image img);
SOKOL_GFX_API_DECL void sg_fail_sampler(sg_sampler smp);
SOKOL_GFX_API_DECL void sg_fail_shader(sg_shader shd);
SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip);
SOKOL_GFX_API_DECL void sg_fail_attachments(sg_attachments atts);
// frame stats
SOKOL_GFX_API_DECL void sg_enable_frame_stats(void);
SOKOL_GFX_API_DECL void sg_disable_frame_stats(void);
SOKOL_GFX_API_DECL bool sg_frame_stats_enabled(void);
SOKOL_GFX_API_DECL sg_frame_stats sg_query_frame_stats(void);
/* Backend-specific structs and functions, these may come in handy for mixing
sokol-gfx rendering with 'native backend' rendering functions.
This group of functions will be expanded as needed.
*/
typedef struct sg_d3d11_buffer_info {
const void* buf; // ID3D11Buffer*
} sg_d3d11_buffer_info;
typedef struct sg_d3d11_image_info {
const void* tex2d; // ID3D11Texture2D*
const void* tex3d; // ID3D11Texture3D*
const void* res; // ID3D11Resource* (either tex2d or tex3d)
const void* srv; // ID3D11ShaderResourceView*
} sg_d3d11_image_info;
typedef struct sg_d3d11_sampler_info {
const void* smp; // ID3D11SamplerState*
} sg_d3d11_sampler_info;
typedef struct sg_d3d11_shader_info {
const void* cbufs[SG_MAX_UNIFORMBLOCK_BINDSLOTS]; // ID3D11Buffer* (constant buffers by bind slot)
const void* vs; // ID3D11VertexShader*
const void* fs; // ID3D11PixelShader*
} sg_d3d11_shader_info;
typedef struct sg_d3d11_pipeline_info {
const void* il; // ID3D11InputLayout*
const void* rs; // ID3D11RasterizerState*
const void* dss; // ID3D11DepthStencilState*
const void* bs; // ID3D11BlendState*
} sg_d3d11_pipeline_info;
typedef struct sg_d3d11_attachments_info {
const void* color_rtv[SG_MAX_COLOR_ATTACHMENTS]; // ID3D11RenderTargetView
const void* resolve_rtv[SG_MAX_COLOR_ATTACHMENTS]; // ID3D11RenderTargetView
const void* dsv; // ID3D11DepthStencilView
} sg_d3d11_attachments_info;
typedef struct sg_mtl_buffer_info {
const void* buf[SG_NUM_INFLIGHT_FRAMES]; // id<MTLBuffer>
int active_slot;
} sg_mtl_buffer_info;
typedef struct sg_mtl_image_info {
const void* tex[SG_NUM_INFLIGHT_FRAMES]; // id<MTLTexture>
int active_slot;
} sg_mtl_image_info;
typedef struct sg_mtl_sampler_info {
const void* smp; // id<MTLSamplerState>
} sg_mtl_sampler_info;
typedef struct sg_mtl_shader_info {
const void* vertex_lib; // id<MTLLibrary>
const void* fragment_lib; // id<MTLLibrary>
const void* vertex_func; // id<MTLFunction>
const void* fragment_func; // id<MTLFunction>
} sg_mtl_shader_info;
typedef struct sg_mtl_pipeline_info {
const void* rps; // id<MTLRenderPipelineState>
const void* dss; // id<MTLDepthStencilState>
} sg_mtl_pipeline_info;
typedef struct sg_wgpu_buffer_info {
const void* buf; // WGPUBuffer
} sg_wgpu_buffer_info;
typedef struct sg_wgpu_image_info {
const void* tex; // WGPUTexture
const void* view; // WGPUTextureView
} sg_wgpu_image_info;
typedef struct sg_wgpu_sampler_info {
const void* smp; // WGPUSampler
} sg_wgpu_sampler_info;
typedef struct sg_wgpu_shader_info {
const void* vs_mod; // WGPUShaderModule
const void* fs_mod; // WGPUShaderModule
const void* bgl; // WGPUBindGroupLayout;
} sg_wgpu_shader_info;
typedef struct sg_wgpu_pipeline_info {
const void* pip; // WGPURenderPipeline
} sg_wgpu_pipeline_info;
typedef struct sg_wgpu_attachments_info {
const void* color_view[SG_MAX_COLOR_ATTACHMENTS]; // WGPUTextureView
const void* resolve_view[SG_MAX_COLOR_ATTACHMENTS]; // WGPUTextureView
const void* ds_view; // WGPUTextureView
} sg_wgpu_attachments_info;
typedef struct sg_gl_buffer_info {
uint32_t buf[SG_NUM_INFLIGHT_FRAMES];
int active_slot;
} sg_gl_buffer_info;
typedef struct sg_gl_image_info {
uint32_t tex[SG_NUM_INFLIGHT_FRAMES];
uint32_t tex_target;
uint32_t msaa_render_buffer;
int active_slot;
} sg_gl_image_info;
typedef struct sg_gl_sampler_info {
uint32_t smp;
} sg_gl_sampler_info;
typedef struct sg_gl_shader_info {
uint32_t prog;
} sg_gl_shader_info;
typedef struct sg_gl_attachments_info {
uint32_t framebuffer;
uint32_t msaa_resolve_framebuffer[SG_MAX_COLOR_ATTACHMENTS];
} sg_gl_attachments_info;
// D3D11: return ID3D11Device
SOKOL_GFX_API_DECL const void* sg_d3d11_device(void);
// D3D11: return ID3D11DeviceContext
SOKOL_GFX_API_DECL const void* sg_d3d11_device_context(void);
// D3D11: get internal buffer resource objects
SOKOL_GFX_API_DECL sg_d3d11_buffer_info sg_d3d11_query_buffer_info(sg_buffer buf);
// D3D11: get internal image resource objects
SOKOL_GFX_API_DECL sg_d3d11_image_info sg_d3d11_query_image_info(sg_image img);
// D3D11: get internal sampler resource objects
SOKOL_GFX_API_DECL sg_d3d11_sampler_info sg_d3d11_query_sampler_info(sg_sampler smp);
// D3D11: get internal shader resource objects
SOKOL_GFX_API_DECL sg_d3d11_shader_info sg_d3d11_query_shader_info(sg_shader shd);
// D3D11: get internal pipeline resource objects
SOKOL_GFX_API_DECL sg_d3d11_pipeline_info sg_d3d11_query_pipeline_info(sg_pipeline pip);
// D3D11: get internal pass resource objects
SOKOL_GFX_API_DECL sg_d3d11_attachments_info sg_d3d11_query_attachments_info(sg_attachments atts);
// Metal: return __bridge-casted MTLDevice
SOKOL_GFX_API_DECL const void* sg_mtl_device(void);
// Metal: return __bridge-casted MTLRenderCommandEncoder in current pass (or zero if outside pass)
SOKOL_GFX_API_DECL const void* sg_mtl_render_command_encoder(void);
// Metal: get internal __bridge-casted buffer resource objects
SOKOL_GFX_API_DECL sg_mtl_buffer_info sg_mtl_query_buffer_info(sg_buffer buf);
// Metal: get internal __bridge-casted image resource objects
SOKOL_GFX_API_DECL sg_mtl_image_info sg_mtl_query_image_info(sg_image img);
// Metal: get internal __bridge-casted sampler resource objects
SOKOL_GFX_API_DECL sg_mtl_sampler_info sg_mtl_query_sampler_info(sg_sampler smp);
// Metal: get internal __bridge-casted shader resource objects
SOKOL_GFX_API_DECL sg_mtl_shader_info sg_mtl_query_shader_info(sg_shader shd);
// Metal: get internal __bridge-casted pipeline resource objects
SOKOL_GFX_API_DECL sg_mtl_pipeline_info sg_mtl_query_pipeline_info(sg_pipeline pip);
// WebGPU: return WGPUDevice object
SOKOL_GFX_API_DECL const void* sg_wgpu_device(void);
// WebGPU: return WGPUQueue object
SOKOL_GFX_API_DECL const void* sg_wgpu_queue(void);
// WebGPU: return this frame's WGPUCommandEncoder
SOKOL_GFX_API_DECL const void* sg_wgpu_command_encoder(void);
// WebGPU: return WGPURenderPassEncoder of current pass
SOKOL_GFX_API_DECL const void* sg_wgpu_render_pass_encoder(void);
// WebGPU: get internal buffer resource objects
SOKOL_GFX_API_DECL sg_wgpu_buffer_info sg_wgpu_query_buffer_info(sg_buffer buf);
// WebGPU: get internal image resource objects
SOKOL_GFX_API_DECL sg_wgpu_image_info sg_wgpu_query_image_info(sg_image img);
// WebGPU: get internal sampler resource objects
SOKOL_GFX_API_DECL sg_wgpu_sampler_info sg_wgpu_query_sampler_info(sg_sampler smp);
// WebGPU: get internal shader resource objects
SOKOL_GFX_API_DECL sg_wgpu_shader_info sg_wgpu_query_shader_info(sg_shader shd);
// WebGPU: get internal pipeline resource objects
SOKOL_GFX_API_DECL sg_wgpu_pipeline_info sg_wgpu_query_pipeline_info(sg_pipeline pip);
// WebGPU: get internal pass resource objects
SOKOL_GFX_API_DECL sg_wgpu_attachments_info sg_wgpu_query_attachments_info(sg_attachments atts);
// GL: get internal buffer resource objects
SOKOL_GFX_API_DECL sg_gl_buffer_info sg_gl_query_buffer_info(sg_buffer buf);
// GL: get internal image resource objects
SOKOL_GFX_API_DECL sg_gl_image_info sg_gl_query_image_info(sg_image img);
// GL: get internal sampler resource objects
SOKOL_GFX_API_DECL sg_gl_sampler_info sg_gl_query_sampler_info(sg_sampler smp);
// GL: get internal shader resource objects
SOKOL_GFX_API_DECL sg_gl_shader_info sg_gl_query_shader_info(sg_shader shd);
// GL: get internal pass resource objects
SOKOL_GFX_API_DECL sg_gl_attachments_info sg_gl_query_attachments_info(sg_attachments atts);
// reference-based equivalents for c++
inline void sg_setup(const sg_desc& desc) { return sg_setup(&desc); }
inline sg_buffer sg_make_buffer(const sg_buffer_desc& desc) { return sg_make_buffer(&desc); }
inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image(&desc); }
inline sg_sampler sg_make_sampler(const sg_sampler_desc& desc) { return sg_make_sampler(&desc); }
inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); }
inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); }
inline sg_attachments sg_make_attachments(const sg_attachments_desc& desc) { return sg_make_attachments(&desc); }
inline void sg_update_image(sg_image img, const sg_image_data& data) { return sg_update_image(img, &data); }
inline void sg_begin_pass(const sg_pass& pass) { return sg_begin_pass(&pass); }
inline void sg_apply_bindings(const sg_bindings& bindings) { return sg_apply_bindings(&bindings); }
inline void sg_apply_uniforms(int ub_slot, const sg_range& data) { return sg_apply_uniforms(ub_slot, &data); }
inline sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc& desc) { return sg_query_buffer_defaults(&desc); }
inline sg_image_desc sg_query_image_defaults(const sg_image_desc& desc) { return sg_query_image_defaults(&desc); }
inline sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc& desc) { return sg_query_sampler_defaults(&desc); }
inline sg_shader_desc sg_query_shader_defaults(const sg_shader_desc& desc) { return sg_query_shader_defaults(&desc); }
inline sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc& desc) { return sg_query_pipeline_defaults(&desc); }
inline sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc& desc) { return sg_query_attachments_defaults(&desc); }
inline void sg_init_buffer(sg_buffer buf, const sg_buffer_desc& desc) { return sg_init_buffer(buf, &desc); }
inline void sg_init_image(sg_image img, const sg_image_desc& desc) { return sg_init_image(img, &desc); }
inline void sg_init_sampler(sg_sampler smp, const sg_sampler_desc& desc) { return sg_init_sampler(smp, &desc); }
inline void sg_init_shader(sg_shader shd, const sg_shader_desc& desc) { return sg_init_shader(shd, &desc); }
inline void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc& desc) { return sg_init_pipeline(pip, &desc); }
inline void sg_init_attachments(sg_attachments atts, const sg_attachments_desc& desc) { return sg_init_attachments(atts, &desc); }
inline void sg_update_buffer(sg_buffer buf_id, const sg_range& data) { return sg_update_buffer(buf_id, &data); }
inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_append_buffer(buf_id, &data); }