Skip to content

Commit

Permalink
sokol_gfx.h: documentation update wip (bindings cleanup)
Browse files Browse the repository at this point in the history
  • Loading branch information
floooh committed Nov 2, 2024
1 parent ab5d834 commit 8b3d531
Showing 1 changed file with 120 additions and 67 deletions.
187 changes: 120 additions & 67 deletions sokol_gfx.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@
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 image-objects,
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)
Expand Down Expand Up @@ -1496,33 +1496,30 @@
- when writing WGSL shader code by hand, a specific bind-slot convention
must be used:

All uniform block structs must use `@group(0)`, with up to
4 uniform blocks per shader stage.
- Vertex shader uniform block bindings must start at `@group(0) @binding(0)`
- Fragment shader uniform blocks bindings must start at `@group(0) @binding(4)`
All uniform block structs must use `@group(0)` and bindings in the
range 0..127:

All textures and samplers must use `@group(1)` and start at specific
offsets depending on resource type and shader stage.
- Vertex shader textures must start at `@group(1) @binding(0)`
- Vertex shader samplers must start at `@group(1) @binding(16)`
- Vertex shader storage buffers must start at `@group(1) @binding(32)`
- Fragment shader textures must start at `@group(1) @binding(48)`
- Fragment shader samplers must start at `@group(1) @binding(64)`
- Fragment shader storage buffers must start at `@group(1) @binding(80)`
@group(0) @binding(0..7)

Note that the actual number of allowed per-stage texture- and sampler-bindings
in sokol-gfx is currently lower than the above ranges (currently only up to
12 textures, 8 samplers and 8 storage buffers are allowed per shader stage).
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 convention since sokol-shdc assigns bind slots
automatically.
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 256 bytes of uniform buffer size). The default size
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
Expand All @@ -1544,7 +1541,7 @@
.wgpu.num_bindgroup_cache_vs_hash_key_mismatch

The value to pay attention to is `.wgpu.num_bindgroup_cache_collisions`,
if this number if consistently higher than a few percent of the
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.

Expand Down Expand Up @@ -1630,14 +1627,14 @@ extern "C" {
Resource id typedefs:

sg_buffer: vertex- and index-buffers
sg_image: images used as textures and render targets
sg_sampler sampler object describing how a texture is sampled in a shader
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
number which uniquely identifies the resource object.
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
Expand Down Expand Up @@ -1680,7 +1677,7 @@ typedef struct sg_range {
#define SG_RANGE_REF(x) &(sg_range){ &x, sizeof(x) }
#endif

// various compile-time constants
// various compile-time constants in the public API
enum {
SG_INVALID_ID = 0,
SG_NUM_INFLIGHT_FRAMES = 2,
Expand Down Expand Up @@ -1744,13 +1741,12 @@ typedef enum sg_backend {

- sample: the pixelformat can be sampled as texture at least with
nearest filtering
- filter: the pixelformat can be samples as texture with linear
- filter: the pixelformat can be sampled as texture with linear
filtering
- render: the pixelformat can be used for render targets
- blend: blending is supported when using the pixelformat for
render targets
- msaa: multisample-antialiasing is supported when using the
pixelformat for render targets
- 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)
Expand Down Expand Up @@ -1853,27 +1849,25 @@ typedef enum sg_pixel_format {
} sg_pixel_format;

/*
Runtime information about a pixel format, returned
by sg_query_pixelformat().
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 target
bool blend; // alpha-blending is supported
bool msaa; // pixel format can be used as MSAA render target
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()
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 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
Expand Down Expand Up @@ -1987,6 +1981,7 @@ typedef enum sg_buffer_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.

Expand Down Expand Up @@ -2026,7 +2021,7 @@ typedef enum 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_desc and used by the validation
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
Expand Down Expand Up @@ -2231,7 +2226,8 @@ typedef enum sg_vertex_step {

The data type of a uniform block member. This is used to
describe the internal layout of uniform blocks when creating
a shader object.
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,
Expand All @@ -2252,7 +2248,7 @@ typedef enum sg_uniform_type {
sg_uniform_layout

A hint for the interior memory layout of uniform blocks. This is
only really relevant for the GL backend where the internal layout
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
Expand Down Expand Up @@ -2332,6 +2328,8 @@ typedef enum sg_face_winding {
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
Expand All @@ -2345,7 +2343,7 @@ typedef enum sg_face_winding {
The default compare func for depth- and stencil-tests is
SG_COMPAREFUNC_ALWAYS.

The default compare func for sampler is SG_COMPAREFUNC_NEVER.
The default compare func for samplers is SG_COMPAREFUNC_NEVER.
*/
typedef enum sg_compare_func {
_SG_COMPAREFUNC_DEFAULT, // value 0 reserved for default-init
Expand All @@ -2366,7 +2364,7 @@ typedef enum sg_compare_func {

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 members:
object in the following sg_pipeline_desc struct items:

sg_pipeline_desc
.stencil
Expand Down Expand Up @@ -2437,8 +2435,8 @@ typedef enum 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 members when
creating a pipeline object:
fragment blending operation. It is used in the following struct items
when creating a pipeline object:

sg_pipeline_desc
.colors[i]
Expand Down Expand Up @@ -2520,7 +2518,7 @@ typedef enum sg_load_action {
/*
sg_store_action

Defines the store action that be performed at the end of a render pass:
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
Expand All @@ -2539,11 +2537,11 @@ typedef enum sg_store_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 targets should be cleared,
- 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 target or discarded
stored back into the render attachment or discarded
*/
typedef struct sg_color_attachment_action {
sg_load_action load_action; // default: SG_LOADACTION_CLEAR
Expand Down Expand Up @@ -2591,8 +2589,9 @@ typedef struct sg_pass_action {
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.
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
Expand All @@ -2610,7 +2609,7 @@ typedef struct sg_pass_action {
- when MSAA rendering is used, another WGPUTextureView
which serves as MSAA resolve target and will be displayed

Metal (NOTE that the rolves of provided surfaces is slightly different
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.
Expand All @@ -2622,7 +2621,7 @@ typedef struct sg_pass_action {
CAMetalDrawable.

NOTE that for Metal you must use an ObjC __bridge cast to
properly tunnel the ObjC object handle through a C void*, e.g.:
properly tunnel the ObjC object id through a C void*, e.g.:

swapchain.metal.current_drawable = (__bridge const void*) [mtkView currentDrawable];

Expand Down Expand Up @@ -2674,7 +2673,7 @@ typedef struct sg_swapchain {
function.

For an offscreen rendering pass, an sg_pass_action struct and sg_attachments
object must be provided, and for swapchain passes, and sg_pass_action and
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().

Expand Down Expand Up @@ -2710,29 +2709,83 @@ typedef struct sg_pass {
/*
sg_bindings

The sg_bindings structure defines the resource binding slots
of the sokol_gfx render pipeline, used as argument to the
sg_apply_bindings() function.
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 vertex shader stage images
- 0..N vertex shader stage samplers
- 0..N vertex shader storage buffers
- 0..N fragment shader stage images
- 0..N fragment shader stage samplers
- 0..N fragment shader storage buffers
- 0..N images
- 0..N samplers
- 0..N storage buffers

For the max number of bindings, see the constant definitions:
Where 'N' is defined in the following constants:

- SG_MAX_VERTEXBUFFER_BINDSLOTS
- SG_MAX_SHADERSTAGE_IMAGES
- SG_MAX_SHADERSTAGE_SAMPLERS
- SG_MAX_SHADERSTAGE_STORAGEBUFFERS
- 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.
Expand Down

0 comments on commit 8b3d531

Please sign in to comment.