From e7da6ecb8d84621fc59ec712e04d84d02bee4644 Mon Sep 17 00:00:00 2001 From: Valerio Santinelli Date: Tue, 8 Oct 2024 15:12:49 +0200 Subject: [PATCH] Updated Sokol to the current version. Some issues on macOS though --- src/binocle/core/binocle_gd.c | 37 +- src/binocle/core/binocle_gd.h | 2 +- src/binocle/core/binocle_sokol.c | 2 +- src/binocle/core/binocle_sokol.h | 3 +- src/binocle/core/binocle_sokol.m | 26 +- src/binocle/core/binocle_window.c | 25 + src/binocle/core/binocle_window.h | 17 + src/deps/sokol/sokol_gfx.h | 5476 ++++++++++++++++++----------- 8 files changed, 3447 insertions(+), 2141 deletions(-) diff --git a/src/binocle/core/binocle_gd.c b/src/binocle/core/binocle_gd.c index 3c911b3e..96348c7a 100644 --- a/src/binocle/core/binocle_gd.c +++ b/src/binocle/core/binocle_gd.c @@ -126,12 +126,15 @@ void binocle_gd_destroy(binocle_gd *gd) { void binocle_gd_init(binocle_gd *gd, binocle_window *win) { #if defined(BINOCLE_GL) sg_desc desc = { + .environment = binocle_window_get_environment(win), .logger.func = slog_func, }; #elif defined(BINOCLE_METAL) binocle_metal_init(win->mtl_view); + sg_environment environment = binocle_window_get_environment(win); + environment.metal.device = binocle_metal_get_device(); sg_desc desc = { - .context = binocle_metal_get_context(), + .environment = environment, .logger.func = slog_func, }; #endif @@ -182,8 +185,8 @@ void binocle_gd_setup_default_pipeline(binocle_gd *gd, uint32_t offscreen_width, gd->offscreen.action = offscreen_action; // Render pass that renders to the offscreen render target - gd->offscreen.pass = sg_make_pass(&(sg_pass_desc){ - .color_attachments[0].image = gd->offscreen.render_target, + gd->offscreen.attachments = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = gd->offscreen.render_target, .label = "offscreen-pass", }); @@ -250,7 +253,7 @@ void binocle_gd_setup_default_pipeline(binocle_gd *gd, uint32_t offscreen_width, .label = "offscreen-sampler", .min_filter = SG_FILTER_LINEAR, .mag_filter = SG_FILTER_LINEAR, - .mipmap_filter = SG_FILTER_NONE, + .mipmap_filter = SG_FILTER_LINEAR, }); // Render to screen pipeline @@ -347,7 +350,7 @@ void binocle_gd_setup_default_pipeline(binocle_gd *gd, uint32_t offscreen_width, .label = "display-sampler", .min_filter = SG_FILTER_LINEAR, .mag_filter = SG_FILTER_LINEAR, - .mipmap_filter = SG_FILTER_NONE, + .mipmap_filter = SG_FILTER_LINEAR, }); } @@ -409,7 +412,10 @@ void binocle_gd_render_offscreen(binocle_gd *gd) { } sg_update_buffer(gd->offscreen.vbuf, &(sg_range){ .ptr=gd->vertices, .size=gd->num_vertices * sizeof(binocle_vpct) }); - sg_begin_pass(gd->offscreen.pass, &gd->offscreen.action); + sg_begin_pass(&(sg_pass){ + .action = gd->offscreen.action, + .attachments = gd->offscreen.attachments, + }); ks_mergesort(binocle_gd_sort_commands, gd->num_commands, gd->commands, 0); @@ -465,7 +471,10 @@ void binocle_gd_render_screen(binocle_gd *gd, struct binocle_window *window, flo gd->display.bind.fs.images[0] = gd->offscreen.render_target; - sg_begin_default_pass(&gd->display.action, window->width, window->height); + sg_begin_pass(&(sg_pass){ + .action = gd->display.action, + .swapchain = binocle_window_get_swapchain(window), + }); sg_apply_pipeline(gd->display.pip); sg_apply_bindings(&gd->display.bind); sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &SG_RANGE(screen_vs_params)); @@ -540,8 +549,8 @@ void binocle_gd_setup_flat_pipeline(binocle_gd *gd) { }; gd->flat.action = action; - gd->flat.pass = sg_make_pass(&(sg_pass_desc){ - .color_attachments[0].image = gd->offscreen.render_target, + gd->flat.attachments = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = gd->offscreen.render_target, }); binocle_log_info("Setting up flat pipeline"); @@ -788,7 +797,10 @@ void binocle_gd_draw_quad_to_screen(binocle_gd *gd, sg_shader shader, sg_image r /* default pass action */ sg_pass_action pass_action = { 0 }; - sg_begin_pass(gd->display.pass, &pass_action); + sg_begin_pass(&(sg_pass){ + .action = pass_action, + .attachments = gd->display.attachments, + }); sg_apply_pipeline(pip); sg_apply_bindings(&bind); sg_draw(0, 6, 1); @@ -874,7 +886,10 @@ void binocle_gd_render_flat(binocle_gd *gd) { } sg_update_buffer(gd->flat.vbuf, &(sg_range){ .ptr=gd->flat_vertices, .size=gd->flat_num_vertices * sizeof(binocle_vpct) }); - sg_begin_pass(gd->flat.pass, &gd->flat.action); + sg_begin_pass(&(sg_pass){ + .action = gd->flat.action, + .attachments = gd->flat.attachments, + }); for (uint32_t i = 0 ; i < gd->flat_num_commands ; i++) { binocle_gd_command_t *cmd = &gd->flat_commands[i]; diff --git a/src/binocle/core/binocle_gd.h b/src/binocle/core/binocle_gd.h index 9ad8a621..9ac1ab94 100644 --- a/src/binocle/core/binocle_gd.h +++ b/src/binocle/core/binocle_gd.h @@ -38,7 +38,7 @@ enum sg_blend_factor; enum sg_blend_op; typedef struct binocle_gd_gfx_t { - sg_pass pass; + sg_attachments attachments; sg_pipeline pip; sg_bindings bind; sg_pass_action action; diff --git a/src/binocle/core/binocle_sokol.c b/src/binocle/core/binocle_sokol.c index a1a6cbf3..90a8af15 100644 --- a/src/binocle/core/binocle_sokol.c +++ b/src/binocle/core/binocle_sokol.c @@ -9,7 +9,7 @@ #elif defined(__IPHONEOS__) || defined(__ANDROID__) #define SOKOL_GLES3 #else - #define SOKOL_GLCORE33 + #define SOKOL_GLCORE #endif #elif defined(BINOCLE_METAL) #define SOKOL_METAL diff --git a/src/binocle/core/binocle_sokol.h b/src/binocle/core/binocle_sokol.h index 5a19b840..bf7ba4ee 100644 --- a/src/binocle/core/binocle_sokol.h +++ b/src/binocle/core/binocle_sokol.h @@ -3,6 +3,7 @@ #include "sokol_gfx.h" #include "sokol_log.h" void binocle_metal_init(void *mtl_view); -sg_context_desc binocle_metal_get_context(void); +// sg_context_desc binocle_metal_get_context(void); +const void* binocle_metal_get_device(void); GLuint binocle_sokol_tex_id(sg_image img_id); #endif \ No newline at end of file diff --git a/src/binocle/core/binocle_sokol.m b/src/binocle/core/binocle_sokol.m index 09ff7596..50464b7b 100644 --- a/src/binocle/core/binocle_sokol.m +++ b/src/binocle/core/binocle_sokol.m @@ -7,7 +7,7 @@ #if defined(__IPHONEOS__) || defined(__ANDROID__) #define SOKOL_GLES3 #else -#define SOKOL_GLCORE33 +#define SOKOL_GLCORE #endif #elif defined(BINOCLE_METAL) #define SOKOL_METAL @@ -44,16 +44,20 @@ void binocle_metal_init(void *mtl_view) { return cached_drawable_hack; } -sg_context_desc binocle_metal_get_context(void) { - return (sg_context_desc) { - .sample_count = 1, - .metal = { - .device = (__bridge const void*) device, - .renderpass_descriptor_userdata_cb = binocle_sokol_mtk_get_render_pass_descriptor, - .drawable_userdata_cb = binocle_sokol_mtk_get_drawable, - .user_data = (void*)metal_layer, - } - }; +//sg_context_desc binocle_metal_get_context(void) { +// return (sg_context_desc) { +// .sample_count = 1, +// .metal = { +// .device = (__bridge const void*) device, +// .renderpass_descriptor_userdata_cb = binocle_sokol_mtk_get_render_pass_descriptor, +// .drawable_userdata_cb = binocle_sokol_mtk_get_drawable, +// .user_data = (void*)metal_layer, +// } +// }; +//} + +const void* binocle_metal_get_device(void) { + return (__bridge const void*) device; } GLuint binocle_sokol_tex_id(sg_image img_id) { diff --git a/src/binocle/core/binocle_window.c b/src/binocle/core/binocle_window.c index 7b1c337a..292df883 100644 --- a/src/binocle/core/binocle_window.c +++ b/src/binocle/core/binocle_window.c @@ -24,6 +24,8 @@ binocle_window *binocle_window_new(uint32_t width, uint32_t height, char *title) res->is_fullscreen = false; res->current_frame_delta = 0; res->framerate_timer = binocle_timer_new(); + res->sample_count = 0; + res->has_depth_buffer = true; // Default to 60 FPS binocle_window_set_target_fps(res, 60); @@ -349,4 +351,27 @@ bool binocle_window_get_display_size(uint32_t *w, uint32_t *h) { } } return false; +} + +sg_environment binocle_window_get_environment(binocle_window *window) { + return (sg_environment) { + .defaults = { + .color_format = SG_PIXELFORMAT_RGBA8, + .depth_format = window->has_depth_buffer ? SG_PIXELFORMAT_NONE : SG_PIXELFORMAT_DEPTH_STENCIL, + .sample_count = window->sample_count, + }, +}; +} +sg_swapchain binocle_window_get_swapchain(binocle_window *window) { + return (sg_swapchain) { + .width = window->width, + .height = window->height, + .sample_count = window->sample_count, + .color_format = SG_PIXELFORMAT_RGBA8, + .depth_format = window->has_depth_buffer ? SG_PIXELFORMAT_NONE : SG_PIXELFORMAT_DEPTH_STENCIL, + .gl = { + // we just assume here that the GL framebuffer is always 0 + .framebuffer = 0, + } + }; } \ No newline at end of file diff --git a/src/binocle/core/binocle_window.h b/src/binocle/core/binocle_window.h index 5efb1d49..dbe1fff9 100644 --- a/src/binocle/core/binocle_window.h +++ b/src/binocle/core/binocle_window.h @@ -46,6 +46,9 @@ typedef struct binocle_window { SDL_Window *window; + + bool has_depth_buffer; + uint32_t sample_count; } binocle_window; /** @@ -190,4 +193,18 @@ void binocle_window_get_real_size(binocle_window *win, uint32_t *w, uint32_t *h) */ bool binocle_window_get_display_size(uint32_t *w, uint32_t *h); +/** + * \brief Build the environment to pass to Sokol + * @param window a pointer to the window + * @return the environment + */ +sg_environment binocle_window_get_environment(binocle_window *window); + +/** + * \brief Build the swapchain data to pass to Sokol + * @param window a pointer to the window + * @return the swapchain data + */ +sg_swapchain binocle_window_get_swapchain(binocle_window *window); + #endif //BINOCLE_WINDOW_H diff --git a/src/deps/sokol/sokol_gfx.h b/src/deps/sokol/sokol_gfx.h index 03b43b0f..9cf317a7 100644 --- a/src/deps/sokol/sokol_gfx.h +++ b/src/deps/sokol/sokol_gfx.h @@ -17,19 +17,19 @@ In the same place define one of the following to select the rendering backend: - #define SOKOL_GLCORE33 + #define SOKOL_GLCORE #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL #define SOKOL_WGPU #define SOKOL_DUMMY_BACKEND - I.e. for the GL 3.3 Core Profile it should look like this: + I.e. for the desktop GL it should look like this: #include ... #include ... #define SOKOL_IMPL - #define SOKOL_GLCORE33 + #define SOKOL_GLCORE #include "sokol_gfx.h" The dummy backend replaces the platform-specific backend code with empty @@ -89,7 +89,7 @@ sg_setup(const sg_desc*) Depending on the selected 3D backend, sokol-gfx requires some - information, like a device pointer framebuffer pixel formats + 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: @@ -99,7 +99,7 @@ #include "sokol_glue.h" //... sg_setup(&(sg_desc){ - .context = sapp_sgcontext(), + .environment = sglue_environment(), }); To get any logging output for errors and from the validation layer, you @@ -113,37 +113,43 @@ }); --- create resource objects (at least buffers, shaders and pipelines, - and optionally images, samplers and passes): + 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_pass sg_make_pass(const sg_pass_desc*) + sg_attachments sg_make_attachments(const sg_attachments_desc*) - --- start rendering to the default frame buffer with: + --- start a render pass: - sg_begin_default_pass(const sg_pass_action* action, int width, int height) + sg_begin_pass(const sg_pass* pass); - ...or alternatively with: + 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_default_passf(const sg_pass_action* action, float width, float height) + sg_begin_pass(&(sg_pass){ .action = { ... }, .swapchain = sglue_swapchain() }) - ...which takes the framebuffer width and height as float values. + ...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. - --- or start rendering to an offscreen framebuffer with: + 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 pass, const sg_pass_action* action) + sg_begin_pass(&(sg_pass){ .action = { ... }, .attachments = attachments }); - --- set the pipeline state for the next draw call with: + --- set the render pipeline state for the next draw call with: sg_apply_pipeline(sg_pipeline pip) --- fill an sg_bindings struct with the resource bindings for the next - draw call (1..N vertex buffers, 0 or 1 index buffer, 0..N image objects and - 0..N sampler objects on the vertex-shader- and fragment-shader-stage - and then call + draw call (0..N vertex buffers, 0 or 1 index buffer, 0..N image-objects, + samplers and storage-buffers), and call: sg_apply_bindings(const sg_bindings* bindings) @@ -189,7 +195,7 @@ sg_destroy_sampler(sg_sampler smp) sg_destroy_shader(sg_shader shd) sg_destroy_pipeline(sg_pipeline pip) - sg_destroy_pass(sg_pass pass) + sg_destroy_attachments(sg_attachments atts) --- to set a new viewport rectangle, call @@ -310,7 +316,7 @@ 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_pass_desc sg_query_pass_desc(sg_pass pass) + 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 @@ -330,7 +336,7 @@ 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_pass_desc sg_query_pass_defaults(const sg_pass_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 @@ -344,7 +350,7 @@ 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_pass_info sg_query_pass_info(sg_pass pass) + 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 @@ -361,6 +367,25 @@ 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: ================== @@ -381,17 +406,15 @@ Not all of those limit values are used by all backends, but it is good practice to provide them none-the-less. - (2) 3D-API "context information" (sometimes also called "bindings"): - sokol_gfx.h doesn't create or initialize 3D API objects which are - closely related to the presentation layer (this includes the "rendering - device", the swapchain, and any objects which depend on the - swapchain). These API objects (or callback functions to obtain - them, if those objects might change between frames), must - be provided in a nested sg_context_desc struct inside the - sg_desc struct. If sokol_gfx.h is used together with - sokol_app.h, have a look at the sokol_glue.h header which provides - a convenience function to get a sg_context_desc struct filled out - with context information provided by sokol_app.h + (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. @@ -409,7 +432,7 @@ 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 be called inside a render pass: + The following sokol-gfx functions must only be called inside a render pass: sg_apply_viewport(f) sg_apply_scissor_rect(f) @@ -418,21 +441,55 @@ sg_apply_uniforms sg_draw - A frame must have at least one render pass, and this must be the 'default - pass' which renders into the 'default' (swapchain) framebuffer. The default - pass must always be the last pass in the frame before the sg_commit() - call. - - The default and offscreen passes form a dependency tree with the default - pass at the root, offscreen passes as nodes, and render target images as - dependencies between passes. + 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. The sg_swapchain struct must contain the + following information: + + - 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_pass object (for the default pass, the - pass-state is managed internally instead). + 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 a pass object looks like this: + 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 @@ -454,24 +511,35 @@ }); NOTE: when creating render target images, have in mind that some default values - are aligned with the default framebuffer attributes, this is sometimes not - what you want: + are aligned with the default environment attributes in the sg_environment struct + that was passed into the sg_setup() call: - - the default values for .pixel_format and .sample_count are the same - as the default framebuffer - - the default value for .num_mipmaps is always 1 + - 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 a pass object: + Next create an attachments object: - const sg_pass pass = sg_make_pass(&(sg_pass_desc){ - .color_attachments[0].image = color_img, - .depth_stencil_attachment.image = depth_img, + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = color_img, + .depth_stencil.image = depth_img, }); - When using the sg_pass object in a render pass you also need to define - what actions should happen at the start and end of the render pass - in an sg_pass_action struct (for instance whether the render target should - be cleared). + 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: @@ -487,7 +555,7 @@ the depth-stencil-attachments actions. The same pass action with the defaults explicitly filled in would look like this: - const sg_pass_action = { + const sg_pass_action pass_action = { .colors[0] = { .load_action = SG_LOADACTION_CLEAR, .store_action = SG_STOREACTION_STORE, @@ -508,7 +576,22 @@ With the sg_pass object and sg_pass_action struct in place everything is ready now for the actual render pass: - sg_begin_pass(pass, &pass_action); + 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(); @@ -517,16 +600,16 @@ 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 pass creation time, for instance + 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_pass pass = sg_make_pass(&(sg_pass_desc){ - .color_attachments[0] = { + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img, .mip_level = 2, .slice = 3, }, - .depth_stencil_attachment.image = depth_img, + .depth_stencil.image = depth_img, }); If MSAA offscreen rendering is desired, the multi-sample rendering result @@ -535,7 +618,7 @@ NOTE: currently multisample-images cannot be bound as textures. - Creating a simple pass object for multisampled rendering requires + 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 @@ -563,15 +646,15 @@ .sample_count = 4, }); - ...create the pass object: + ...create the attachments object: - const sg_pass pass = sg_make_pass(&(sg_pass_desc){ - .color_attachments[0].image = color_img, - .resolve_attachments[0].image = resolve_img, - .depth_stencil_attachment.image = depth_img, + 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 a pass object defines a resolve image in a specific resolve attachment slot, + 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 @@ -588,7 +671,7 @@ The actual render pass looks as usual: - sg_begin_pass(pass, &pass_action); + sg_begin_pass(&(sg_pass){ .action = pass_action, .attachments = atts }); ... sg_end_pass(); @@ -634,7 +717,9 @@ the sg_make_shader() function requires the following information: - Shader code or shader binary blobs for the vertex- and fragment- shader-stage: - - for the desktop GL backend, source code must be provided in '#version 330' syntax + - for the desktop GL backend, source code can be provided in '#version 410' or + '#version 430', version 430 is required for storage buffer support, but note + that this is not available on macOS - for the GLES3 backend, source code must be provided in '#version 300 es' syntax - for the D3D11 backend, shaders can be provided as source or binary blobs, the source code should be in HLSL4.0 (for best compatibility) or alternatively @@ -673,6 +758,12 @@ - please also NOTE the documentation sections about UNIFORM DATA LAYOUT and CROSS-BACKEND COMMON UNIFORM DATA LAYOUT below! + - A description of each storage buffer used in the shader: + - a boolean 'readonly' flag, note that currently only + readonly storage buffers are supported + - note that storage buffers are not supported on all backends + and platforms + - A description of each texture/image used in the shader: - the expected image type: - SG_IMAGETYPE_2D @@ -689,7 +780,7 @@ (currently it's not supported to fetch data from multisampled textures in shaders, but this is planned for a later time) - - A description of each sampler used in the shader: + - A description of each texture sampler used in the shader: - SG_SAMPLERTYPE_FILTERING, - SG_SAMPLERTYPE_NONFILTERING, - SG_SAMPLERTYPE_COMPARISON, @@ -701,7 +792,7 @@ 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 tradtional GLSL 'sampler object'. + groups them into a traditional GLSL 'sampler object'. Compatibility rules for image-sample-type vs sampler-type are as follows: @@ -869,50 +960,126 @@ The by far easiest way to tackle the common uniform block layout problem is to use the sokol-shdc shader cross-compiler tool! - - WORKING WITH CONTEXTS - ===================== - sokol-gfx allows to switch between different rendering contexts and - associate resource objects with contexts. This is useful to - create GL applications that render into multiple windows. - - A rendering context keeps track of all resources created while - the context is active. When the context is destroyed, all resources - "belonging to the context" are destroyed as well. - - A default context will be created and activated implicitly in - sg_setup(), and destroyed in sg_shutdown(). So for a typical application - which *doesn't* use multiple contexts, nothing changes, and calling - the context functions isn't necessary. - - Three functions have been added to work with contexts: - - --- sg_context sg_setup_context(): - This must be called once after a GL context has been created and - made active. - - --- void sg_activate_context(sg_context ctx) - This must be called after making a different GL context active. - Apart from 3D-API-specific actions, the call to sg_activate_context() - will internally call sg_reset_state_cache(). - - --- void sg_discard_context(sg_context ctx) - This must be called right before a GL context is destroyed and - will destroy all resources associated with the context (that - have been created while the context was active) The GL context must be - active at the time sg_discard_context(sg_context ctx) is called. - - Also note that resources (buffers, images, shaders and pipelines) must - only be used or destroyed while the same GL context is active that - was also active while the resource was created (an exception is - resource sharing on GL, such resources can be used while - another context is active, but must still be destroyed under - the same context that was active during creation). - - For more information, check out the multiwindow-glfw sample: - - https://github.com/floooh/sokol-samples/blob/master/glfw/multiwindow-glfw.c - + 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 `readonly buffer [name] { ... }` + - do NOT annotate storage buffers with `layout(...)`, sokol-shdc will take care of that + - declare a struct which describes a single array item in the storage buffer interface block + - only put a single flexible array member into the storage buffer interface block + + E.g. a complete example in 'sokol-shdc GLSL': + + ```glsl + // declare a struct: + struct sb_vertex { + vec3 pos; + vec4 color; + } + // declare a buffer interface block with a single flexible struct array: + readonly buffer vertices { + sb_vertex vtx[]; + } + // in the shader function, access the storage buffer like this: + void main() { + vec3 pos = vtx[gl_VertexIndex].pos; + ... + } + ``` + + Backend-specific storage-buffer caveats (not relevant when using sokol-shdc): + + D3D11: + - storage buffers are created as 'raw' Byte Address Buffers + (https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-intro#raw-views-of-buffers) + - in HLSL, use a ByteAddressBuffer to access the buffer content + (https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-byteaddressbuffer) + - in D3D11, storage buffers and textures share the same bind slots, sokol-gfx reserves + shader resource slots 0..15 for textures and 16..23 for storage buffers. + - e.g. in HLSL, storage buffer bindings start at register(t16) no matter the shader stage + + Metal: + - in Metal there is no internal difference between vertex-, uniform- and + storage-buffers, all are bound to the same 'buffer bind slots' with the + following reserved ranges: + - vertex shader stage: + - uniform buffers (internal): slots 0..3 + - vertex buffers: slots 4..11 + - storage buffers: slots 12..19 + - fragment shader stage: + - uniform buffers (internal): slots 0..3 + - storage buffers: slots 4..11 + - this means in MSL, storage buffer bindings start at [[buffer(12)]] in the vertex + shaders, and at [[buffer(4)]] in fragment shaders + + GL: + - the GL backend doesn't use name-lookup to find storage buffer bindings, this + means you must annotate buffers with `layout(std430, binding=N)` in GLSL + - ...where N is 0..7 in the vertex shader, and 8..15 in the fragment shader + + WebGPU: + - in WGSL, use the following bind locations for the various shader resource types: + - vertex shader stage: + - textures `@group(1) @binding(0..15)` + - samplers `@group(1) @binding(16..31)` + - storage buffers `@group(1) @binding(32..47)` + - fragment shader stage: + - textures `@group(1) @binding(48..63)` + - samplers `@group(1) @binding(64..79)` + - storage buffers `@group(1) @binding(80..95)` TRACE HOOKS: ============ @@ -1115,7 +1282,7 @@ 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_pass sg_make_pass(const sg_pass_desc* desc) + sg_attachments sg_make_attachments(const sg_attachments_desc* desc) This will result in one of three cases: @@ -1173,7 +1340,7 @@ sg_sampler sg_alloc_sampler(void) sg_shader sg_alloc_shader(void) sg_pipeline sg_alloc_pipeline(void) - sg_pass sg_alloc_pass(void) + sg_attachments sg_alloc_attachments(void) This will return a handle with the underlying resource object in the ALLOC state: @@ -1198,7 +1365,7 @@ 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_pass(sg_pass pass, const sg_pass_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 @@ -1214,7 +1381,7 @@ void sg_uninit_sampler(sg_sampler smp) void sg_uninit_shader(sg_shader shd) void sg_uninit_pipeline(sg_pipeline pip) - void sg_uninit_pass(sg_pass pass) + 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. @@ -1226,7 +1393,7 @@ void sg_dealloc_sampler(sg_sampler smp) void sg_dealloc_shader(sg_shader shd) void sg_dealloc_pipeline(sg_pipeline pip) - void sg_dealloc_pass(sg_pass pass) + 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. @@ -1239,7 +1406,7 @@ void sg_destroy_sampler(sg_sampler smp) void sg_destroy_shader(sg_shader shd) void sg_destroy_pipeline(sg_pipeline pip) - void sg_destroy_pass(sg_pass pass) + 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 @@ -1253,21 +1420,21 @@ sg_fail_sampler(sg_sampler smp) sg_fail_shader(sg_shader shd) sg_fail_pipeline(sg_pipeline pip) - sg_fail_pass(sg_pass pass) + sg_fail_attachments(sg_attachments atts) This is recommended if anything went wrong outside of sokol-gfx during asynchronous - resource creation (for instance a file loading operation failed). In this case, + 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 passes. Most notably, trying + 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 - a pass object with invalid image objects. + an attachments object with invalid image objects. WEBGPU CAVEATS @@ -1279,7 +1446,7 @@ 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 WebGPU code is slower than similar WebGL2 + 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 @@ -1294,12 +1461,14 @@ offsets depending on resource type and shader stage. - Vertex shader textures must start at `@group(1) @binding(0)` - Vertex shader samplers must start at `@group(1) @binding(16)` - - Fragment shader textures must start at `@group(1) @binding(32)` - - Fragment shader samplers must start at `@group(1) @binding(48)` + - Vertex shader storage buffers must start at `@group(1) @binding(32)` + - Fragment shader textures must start at `@group(1) @binding(48)` + - Fragment shader samplers must start at `@group(1) @binding(64)` + - Fragment shader storage buffers must start at `@group(1) @binding(80)` Note that the actual number of allowed per-stage texture- and sampler-bindings in sokol-gfx is currently lower than the above ranges (currently only up to - 12 textures and 8 samplers per shader stage are allowed). + 12 textures, 8 samplers and 8 storage buffers are allowed per shader stage). If you use sokol-shdc to generate WGSL shader code, you don't need to worry about the above binding convention since sokol-shdc assigns bind slots @@ -1319,7 +1488,7 @@ 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 numbers, with the default being + 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: @@ -1327,6 +1496,7 @@ .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`, @@ -1343,7 +1513,7 @@ - The sokol shader compiler generally adds `diagnostic(off, derivative_uniformity);` into the WGSL output. Currently only the Chrome WebGPU implementation seems - to accept this. + 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, @@ -1359,10 +1529,10 @@ will generate broken Javascript code. - sokol-gfx requires the WebGPU device feature `depth32float-stencil8` to be enabled - (this should be supported widely supported) + (this should be widely supported) - sokol-gfx expects that the WebGPU device feature `float32-filterable` to *not* be - enabled (this would exclude all iOS devices) + enabled (since this would exclude all iOS devices) LICENSE @@ -1420,28 +1590,26 @@ extern "C" { sg_sampler sampler object 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_pass: a bundle of render targets and actions on them - sg_context: a 'context handle' for switching between 3D-API contexts + 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. The 32-bit resource id is split into a 16-bit pool index in the lower bits, - and a 16-bit 'unique counter' in the upper bits. The index allows fast - pool lookups, and combined with the unique-mask it allows to detect + 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 struct so that the compiler - can complain when the wrong resource type is used. + 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_pass { uint32_t id; } sg_pass; -typedef struct sg_context { uint32_t id; } sg_context; +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 @@ -1478,6 +1646,7 @@ enum { SG_MAX_SHADERSTAGE_IMAGES = 12, SG_MAX_SHADERSTAGE_SAMPLERS = 8, SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS = 12, + SG_MAX_SHADERSTAGE_STORAGEBUFFERS = 8, SG_MAX_SHADERSTAGE_UBS = 4, SG_MAX_UB_MEMBERS = 16, SG_MAX_VERTEX_ATTRIBUTES = 16, @@ -1499,7 +1668,7 @@ typedef struct sg_color { float r, g, b, a; } sg_color; to get the currently active backend. */ typedef enum sg_backend { - SG_BACKEND_GLCORE33, + SG_BACKEND_GLCORE, SG_BACKEND_GLES3, SG_BACKEND_D3D11, SG_BACKEND_METAL_IOS, @@ -1528,7 +1697,7 @@ typedef enum sg_backend { 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 bool members: + an sg_pixelformat_info struct with the following members: - sample: the pixelformat can be sampled as texture at least with nearest filtering @@ -1540,17 +1709,17 @@ typedef enum sg_backend { - msaa: multisample-antialiasing is supported when using the pixelformat for render targets - 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: - - for Metal and D3D11 it is SG_PIXELFORMAT_BGRA8 - - for GL backends it 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: - This is mainly because of the default framebuffer which is setup outside - of sokol_gfx.h. On some backends, using BGRA for the default frame buffer - allows more efficient frame flips. For your own offscreen-render-targets, - use whatever renderable pixel format is convenient for you. + - 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 @@ -1587,6 +1756,7 @@ typedef enum sg_pixel_format { SG_PIXELFORMAT_BGRA8, SG_PIXELFORMAT_RGB10A2, SG_PIXELFORMAT_RG11B10F, + SG_PIXELFORMAT_RGB9E5, SG_PIXELFORMAT_RG32UI, SG_PIXELFORMAT_RG32SI, @@ -1601,12 +1771,15 @@ typedef enum sg_pixel_format { 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, @@ -1614,17 +1787,23 @@ typedef enum sg_pixel_format { SG_PIXELFORMAT_BC6H_RGBF, SG_PIXELFORMAT_BC6H_RGBUF, SG_PIXELFORMAT_BC7_RGBA, - SG_PIXELFORMAT_PVRTC_RGB_2BPP, - SG_PIXELFORMAT_PVRTC_RGB_4BPP, - SG_PIXELFORMAT_PVRTC_RGBA_2BPP, - SG_PIXELFORMAT_PVRTC_RGBA_4BPP, + 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_RG11, - SG_PIXELFORMAT_ETC2_RG11SN, + SG_PIXELFORMAT_ETC2_SRGB8A8, + SG_PIXELFORMAT_EAC_R11, + SG_PIXELFORMAT_EAC_R11SN, + SG_PIXELFORMAT_EAC_RG11, + SG_PIXELFORMAT_EAC_RG11SN, - SG_PIXELFORMAT_RGB9E5, + SG_PIXELFORMAT_ASTC_4x4_RGBA, + SG_PIXELFORMAT_ASTC_4x4_SRGBA, _SG_PIXELFORMAT_NUM, _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF @@ -1635,15 +1814,14 @@ typedef enum sg_pixel_format { 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 depth; // pixel format is a depth format - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[3]; - #endif + 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 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; /* @@ -1655,9 +1833,7 @@ typedef struct sg_features { bool image_clamp_to_border; // border color and clamp-to-border UV-wrap mode is supported bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[3]; - #endif + bool storage_buffer; // storage buffers are supported } sg_features; /* @@ -1670,7 +1846,7 @@ typedef struct sg_limits { 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_vectors; // <= GL_MAX_VERTEX_UNIFORM_VECTORS (only on GL backends) + 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; @@ -1747,8 +1923,10 @@ typedef enum sg_usage { /* sg_buffer_type - This indicates whether a buffer contains vertex- or index-data, - used in the sg_buffer_desc.type member when creating a buffer. + Indicates whether a buffer will be bound as vertex-, + index- or storage-buffer. + + Used in the sg_buffer_desc.type member when creating a buffer. The default value is SG_BUFFERTYPE_VERTEXBUFFER. */ @@ -1756,6 +1934,7 @@ typedef enum sg_buffer_type { _SG_BUFFERTYPE_DEFAULT, // value 0 reserved for default-init SG_BUFFERTYPE_VERTEXBUFFER, SG_BUFFERTYPE_INDEXBUFFER, + SG_BUFFERTYPE_STORAGEBUFFER, _SG_BUFFERTYPE_NUM, _SG_BUFFERTYPE_FORCE_U32 = 0x7FFFFFFF } sg_buffer_type; @@ -1806,8 +1985,10 @@ typedef enum sg_image_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 layer in sg_apply_bindings() to check if the provided image object - is compatible with what the shader expects, and also required by the - WebGPU backend. + 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 @@ -1817,7 +1998,7 @@ typedef enum sg_image_type { - SG_PIXELFORMAT_RG32F - SG_PIXELFORMAT_RGBA32F - (when using sokol-shdc, also check out the tags `@image_sample_type` + (when using sokol-shdc, also check out the meta tags `@image_sample_type` and `@sampler_type`) */ typedef enum sg_image_sample_type { @@ -1877,12 +2058,12 @@ typedef enum sg_cube_face { sg_shader_stage There are 2 shader stages: vertex- and fragment-shader-stage. - Each shader stage consists of: + Each shader stage - - one slot for a shader function (provided as source- or byte-code) - - SG_MAX_SHADERSTAGE_UBS slots for uniform blocks - - SG_MAX_SHADERSTAGE_IMAGES slots for images used as textures by - the shader function + - SG_MAX_SHADERSTAGE_UBS slots for applying uniform data + - SG_MAX_SHADERSTAGE_IMAGES slots for images used as textures + - SG_MAX_SHADERSTAGE_SAMPLERS slots for texture samplers + - SG_MAX_SHADERSTAGE_STORAGEBUFFERS slots for storage buffer bindings */ typedef enum sg_shader_stage { SG_SHADERSTAGE_VS, @@ -1917,20 +2098,10 @@ typedef enum sg_primitive_type { 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 min_filter and mag_filter the default is SG_FILTER_NEAREST. - - For mipmap_filter the default is SG_FILTER_NONE. - - The following restrictions apply: - - - an image object with (num_mipmaps == 1) must use SG_FILTER_NONE - - min_filter and mag_filter cannot be SG_FILTER_NONE - - Those restrictions are checked in the validation layer. + For the default is SG_FILTER_NEAREST. */ typedef enum sg_filter { _SG_FILTER_DEFAULT, // value 0 reserved for default-init - SG_FILTER_NONE, SG_FILTER_NEAREST, SG_FILTER_LINEAR, _SG_FILTER_NUM, @@ -2340,13 +2511,13 @@ typedef enum sg_store_action { sg_pass_action The sg_pass_action struct defines the actions to be performed - at the start of and end of a render pass. + 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 targets 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 + - at the end of the pass: whether the rendering result should be + stored back into the render target or discarded */ typedef struct sg_color_attachment_action { sg_load_action load_action; // default: SG_LOADACTION_CLEAR @@ -2367,13 +2538,149 @@ typedef struct sg_stencil_attachment_action { } sg_stencil_attachment_action; typedef struct sg_pass_action { - uint32_t _start_canary; sg_color_attachment_action colors[SG_MAX_COLOR_ATTACHMENTS]; sg_depth_attachment_action depth; sg_stencil_attachment_action stencil; - uint32_t _end_canary; } 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 rolves 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 handle 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, and 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 @@ -2389,12 +2696,17 @@ typedef struct sg_pass_action { - 0..1 index buffer offsets - 0..N vertex shader stage images - 0..N vertex shader stage samplers + - 0..N vertex shader storage buffers - 0..N fragment shader stage images - 0..N fragment shader stage samplers + - 0..N fragment shader storage buffers - The max number of vertex buffer and shader stage images - are defined by the SG_MAX_VERTEX_BUFFERS and - SG_MAX_SHADERSTAGE_IMAGES configuration constants. + For the max number of bindings, see the constant definitions: + + - SG_MAX_VERTEX_BUFFERS + - SG_MAX_SHADERSTAGE_IMAGES + - SG_MAX_SHADERSTAGE_SAMPLERS + - SG_MAX_SHADERSTAGE_STORAGEBUFFERS The optional buffer offsets can be used to put different unrelated chunks of vertex- and/or index-data into the same buffer objects. @@ -2402,6 +2714,7 @@ typedef struct sg_pass_action { typedef struct sg_stage_bindings { sg_image images[SG_MAX_SHADERSTAGE_IMAGES]; sg_sampler samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_buffer storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; } sg_stage_bindings; typedef struct sg_bindings { @@ -2428,11 +2741,7 @@ typedef struct sg_bindings { .usage: SG_USAGE_IMMUTABLE .data.ptr 0 (*must* be valid for immutable buffers) .data.size 0 (*must* be > 0 for immutable buffers) - .label 0 (optional string label for trace hooks) - - The label will be ignored by sokol_gfx.h, it is only useful - when hooking into sg_make_buffer() or sg_init_buffer() via - the sg_install_trace_hooks() function. + .label 0 (optional string label) For immutable buffers which are initialized with initial data, keep the .size item zero-initialized, and set the size together with the @@ -2508,17 +2817,17 @@ typedef struct sg_image_data { .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.context.color_format for render targets - .sample_count: 1 for textures, or sg_desc.context.sample_count for render targets + .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.context.sample_count? + "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 the default framebuffer, but it's better that + 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. @@ -2536,15 +2845,16 @@ typedef struct sg_image_data { .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, you can provide either a D3D11 texture, or a - shader-resource-view, or both. If only a texture is provided, a matching - shader-resource-view will be created. If only a shader-resource-view is - provided, the texture will be looked up from the shader-resource-view. + 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). @@ -2569,6 +2879,7 @@ typedef struct sg_image_desc { 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; @@ -2579,7 +2890,7 @@ typedef struct sg_image_desc { .min_filter: SG_FILTER_NEAREST .mag_filter: SG_FILTER_NEAREST - .mipmap_filter SG_FILTER_NONE + .mipmap_filter SG_FILTER_NEAREST .wrap_u: SG_WRAP_REPEAT .wrap_v: SG_WRAP_REPEAT .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) @@ -2643,6 +2954,9 @@ typedef struct sg_sampler_desc { - the texture slot of the involved texture - the sampler slot of the involved sampler - for GLSL only: the name of the combined image-sampler object + - reflection info for each storage-buffer used by the shader: + - whether the storage buffer is readonly (currently this + must be true) For all GL backends, shader source-code must be provided. For D3D11 and Metal, either shader source-code or byte-code can be provided. @@ -2671,6 +2985,11 @@ typedef struct sg_shader_uniform_block_desc { sg_shader_uniform_desc uniforms[SG_MAX_UB_MEMBERS]; } sg_shader_uniform_block_desc; +typedef struct sg_shader_storage_buffer_desc { + bool used; + bool readonly; +} sg_shader_storage_buffer_desc; + typedef struct sg_shader_image_desc { bool used; bool multisampled; @@ -2696,6 +3015,7 @@ typedef struct sg_shader_stage_desc { const char* entry; const char* d3d11_target; sg_shader_uniform_block_desc uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + sg_shader_storage_buffer_desc storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; sg_shader_image_desc images[SG_MAX_SHADERSTAGE_IMAGES]; sg_shader_sampler_desc samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; sg_shader_image_sampler_pair_desc image_sampler_pairs[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; @@ -2783,18 +3103,12 @@ typedef struct sg_vertex_buffer_layout_state { int stride; sg_vertex_step step_func; int step_rate; - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[2]; - #endif } sg_vertex_buffer_layout_state; typedef struct sg_vertex_attr_state { int buffer_index; int offset; sg_vertex_format format; - #if defined(SOKOL_ZIG_BINDINGS) - uint32_t __pad[2]; - #endif } sg_vertex_attr_state; typedef struct sg_vertex_layout_state { @@ -2863,16 +3177,19 @@ typedef struct sg_pipeline_desc { } sg_pipeline_desc; /* - sg_pass_desc + sg_attachments_desc - Creation parameters for an sg_pass object, used as argument to the - sg_make_pass() function. + Creation parameters for an sg_attachments object, used as argument to the + sg_make_attachments() function. - A pass object contains 1..4 color attachments, 0..4 msaa-resolve - attachemnts, and none or one depth-stencil attachment. + 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 consists of an image, and two additional indices describing - which subimage the pass will render into: one mipmap index, and if the image + 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. @@ -2882,7 +3199,7 @@ typedef struct sg_pipeline_desc { same sample count. If a resolve attachment is set, an MSAA-resolve operation from the - associated color attachment into the resolve attachment image will take + 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 @@ -2890,20 +3207,20 @@ typedef struct sg_pipeline_desc { NOTE that MSAA depth-stencil attachments cannot be msaa-resolved! */ -typedef struct sg_pass_attachment_desc { +typedef struct sg_attachment_desc { sg_image image; int mip_level; int slice; // cube texture: face; array texture: layer; 3D texture: slice -} sg_pass_attachment_desc; +} sg_attachment_desc; -typedef struct sg_pass_desc { +typedef struct sg_attachments_desc { uint32_t _start_canary; - sg_pass_attachment_desc color_attachments[SG_MAX_COLOR_ATTACHMENTS]; - sg_pass_attachment_desc resolve_attachments[SG_MAX_COLOR_ATTACHMENTS]; - sg_pass_attachment_desc depth_stencil_attachment; + 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_pass_desc; +} sg_attachments_desc; /* sg_trace_hooks @@ -2925,18 +3242,17 @@ typedef struct sg_trace_hooks { 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_pass)(const sg_pass_desc* desc, sg_pass 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_pass)(sg_pass pass, 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_default_pass)(const sg_pass_action* pass_action, int width, int height, void* user_data); - void (*begin_pass)(sg_pass pass, const sg_pass_action* pass_action, 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); @@ -2950,31 +3266,31 @@ typedef struct sg_trace_hooks { 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_pass)(sg_pass 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_pass)(sg_pass pass_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_pass)(sg_pass pass_id, const sg_pass_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_pass)(sg_pass pass_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_pass)(sg_pass pass_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; @@ -2985,7 +3301,7 @@ typedef struct sg_trace_hooks { sg_sampler_info sg_shader_info sg_pipeline_info - sg_pass_info + sg_attachments_info These structs contain various internal resource attributes which might be useful for debug-inspection. Please don't rely on the @@ -3005,7 +3321,6 @@ typedef struct sg_trace_hooks { 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) - uint32_t ctx_id; // the context this resource belongs to } sg_slot_info; typedef struct sg_buffer_info { @@ -3037,16 +3352,16 @@ typedef struct sg_pipeline_info { sg_slot_info slot; // resource pool slot info } sg_pipeline_info; -typedef struct sg_pass_info { +typedef struct sg_attachments_info { sg_slot_info slot; // resource pool slot info -} sg_pass_info; +} sg_attachments_info; /* sg_frame_stats - Allows to track generic and backend-specific tracking stats about a + Allows to track generic and backend-specific stats about a render frame. Obtained by calling sg_query_frame_stats(). The returned - struct will contains information about the *previous* frame. + struct contains information about the *previous* frame. */ typedef struct sg_frame_stats_gl { uint32_t num_bind_buffer; @@ -3131,6 +3446,7 @@ typedef struct sg_frame_stats_metal_bindings { uint32_t num_set_vertex_buffer; uint32_t num_set_vertex_texture; uint32_t num_set_vertex_sampler_state; + uint32_t num_set_fragment_buffer; uint32_t num_set_fragment_texture; uint32_t num_set_fragment_sampler_state; } sg_frame_stats_metal_bindings; @@ -3164,6 +3480,7 @@ typedef struct sg_frame_stats_wgpu_bindings { 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; @@ -3213,9 +3530,14 @@ typedef struct sg_frame_stats { _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_TEXTURE_NAME_NOT_FOUND_IN_SHADER, "texture name not found in shader (gl)") \ - _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_INCOMPLETE, "framebuffer completeness check failed (gl)") \ - _SG_LOGITEM_XMACRO(GL_MSAA_FRAMEBUFFER_INCOMPLETE, "completeness check failed for msaa resolve framebuffer (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)") \ @@ -3262,55 +3584,53 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(WGPU_CREATE_SHADER_MODULE_FAILED, "wgpuDeviceCreateShaderModule() failed") \ _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_IMAGES, "shader uses too many sampled images on shader stage (wgpu)") \ _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_SAMPLERS, "shader uses too many samplers on shader stage (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_STORAGEBUFFERS, "shader uses too many storage buffer bindings on shader stage (wgpu)") \ _SG_LOGITEM_XMACRO(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED, "wgpuDeviceCreateBindGroupLayout() for shader stage failed") \ _SG_LOGITEM_XMACRO(WGPU_CREATE_PIPELINE_LAYOUT_FAILED, "wgpuDeviceCreatePipelineLayout() failed") \ _SG_LOGITEM_XMACRO(WGPU_CREATE_RENDER_PIPELINE_FAILED, "wgpuDeviceCreateRenderPipeline() failed") \ - _SG_LOGITEM_XMACRO(WGPU_PASS_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed in create pass") \ - _SG_LOGITEM_XMACRO(UNINIT_BUFFER_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in buffer uninit (must be same as for creation)") \ - _SG_LOGITEM_XMACRO(UNINIT_IMAGE_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in image uninit (must be same as for creation)") \ - _SG_LOGITEM_XMACRO(UNINIT_SAMPLER_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in sampler uninit (must be same as for creation)") \ - _SG_LOGITEM_XMACRO(UNINIT_SHADER_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in shader uninit (must be same as for creation)") \ - _SG_LOGITEM_XMACRO(UNINIT_PIPELINE_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in pipeline uninit (must be same as for creation)") \ - _SG_LOGITEM_XMACRO(UNINIT_PASS_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in pass uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed in create attachments") \ _SG_LOGITEM_XMACRO(IDENTICAL_COMMIT_LISTENER, "attempting to add identical commit listener") \ _SG_LOGITEM_XMACRO(COMMIT_LISTENER_ARRAY_FULL, "commit listener array full") \ - _SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined") \ + _SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SOKOL_TRACE_HOOKS is not defined") \ _SG_LOGITEM_XMACRO(DEALLOC_BUFFER_INVALID_STATE, "sg_dealloc_buffer(): buffer must be in ALLOC state") \ _SG_LOGITEM_XMACRO(DEALLOC_IMAGE_INVALID_STATE, "sg_dealloc_image(): image must be in alloc state") \ _SG_LOGITEM_XMACRO(DEALLOC_SAMPLER_INVALID_STATE, "sg_dealloc_sampler(): sampler must be in alloc state") \ _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_PASS_INVALID_STATE, "sg_dealloc_pass(): pass 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_PASS_INVALID_STATE, "sg_init_pass(): pass 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_PASS_INVALID_STATE, "sg_uninit_pass(): pass 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_PASS_INVALID_STATE, "sg_fail_pass(): pass 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") \ @@ -3329,8 +3649,6 @@ typedef struct sg_frame_stats { _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_MINFILTER_NONE, "sg_sampler_desc.min_filter cannot be SG_FILTER_NONE") \ - _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_MAGFILTER_NONE, "sg_sampler_desc.mag_filter cannot be SG_FILTER_NONE") \ _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") \ @@ -3344,6 +3662,8 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, "size of uniform block members doesn't match uniform block size") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_ARRAY_COUNT, "uniform array count must be >= 1") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_STORAGEBUFFERS, "shader stage storage buffers must occupy continuous slots (sg_shader_desc.vs|fs.storage_buffers[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_READONLY, "shader stage storage buffers must be readonly (sg_shader_desc.vs|fs.storage_buffers[].readonly)") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMAGES, "shader stage images must occupy continuous slots (sg_shader_desc.vs|fs.images[])") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_SAMPLERS, "shader stage samplers must occupy continuous slots (sg_shader_desc.vs|fs.samplers[])") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE, "shader stage: image-sampler-pair image slot index is out of range (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)") \ @@ -3357,53 +3677,84 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more images are note referenced by (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more samplers are not referenced by image-sampler-pairs (sg_shader_desc.vs|fs.image_sampler_pairs[].sampler_slot)") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMAGE_SAMPLER_PAIRS, "shader stage image-sampler-pairs must occupy continuous slots (sg_shader_desc.vs|fs.image_samplers[])") \ - _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_SEMANTICS, "D3D11 backend requires vertex attribute semantics") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, "vertex attribute name/semantic string too long (max len 16)") \ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_CANARY, "sg_pipeline_desc not initialized") \ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_SHADER, "sg_pipeline_desc.shader missing or invalid") \ - _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_ATTRS, "sg_pipeline_desc.layout.attrs is empty or not continuous") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_CONT_ATTRS, "sg_pipeline_desc.layout.attrs is not continuous") \ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4") \ _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, "D3D11 missing vertex attribute semantics in shader") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_CANARY, "sg_pass_desc not initialized") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_NO_ATTACHMENTS, "sg_pass_desc no color or depth-stencil attachments") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS, "color attachments must occupy continuous slots") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE, "pass attachment image is not valid") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_MIPLEVEL, "pass attachment mip level is bigger than image has mipmaps") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_FACE, "pass attachment image is cubemap, but face index is too big") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_LAYER, "pass attachment image is array texture, but layer index is too big") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_SLICE, "pass attachment image is 3d texture, but slice value is too big") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE_NO_RT, "pass attachment image must be have render_target=true") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT, "pass color-attachment images must be renderable color pixel format") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT, "pass depth-attachment image must be depth or depth-stencil pixel format") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE_SIZES, "all pass attachments must have the same size") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS, "all pass attachments must have the same sample count") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_COLOR_IMAGE_MSAA, "pass resolve attachments must have a color attachment image with sample count > 1") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_IMAGE, "pass resolve attachment image not valid") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_SAMPLE_COUNT, "pass resolve attachment image sample count must be 1") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_MIPLEVEL, "pass resolve attachment mip level is bigger than image has mipmaps") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_FACE, "pass resolve attachment is cubemap, but face index is too big") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_LAYER, "pass resolve attachment is array texture, but layer index is too big") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_SLICE, "pass resolve attachment is 3d texture, but slice value is too big") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_IMAGE_NO_RT, "pass resolve attachment image must have render_target=true") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_IMAGE_SIZES, "pass resolve attachment size must match color attachment image size") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_IMAGE_FORMAT, "pass resolve attachment pixel format must match color attachment pixel format") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_IMAGE, "pass depth attachment image is not valid") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_MIPLEVEL, "pass depth attachment mip level is bigger than image has mipmaps") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_FACE, "pass depth attachment image is cubemap, but face index is too big") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_LAYER, "pass depth attachment image is array texture, but layer index is too big") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_SLICE, "pass depth attachment image is 3d texture, but slice value is too big") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_IMAGE_NO_RT, "pass depth attachment image must be have render_target=true") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES, "pass depth attachment image size must match color attachment image size") \ - _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_IMAGE_SAMPLE_COUNT, "pass depth attachment sample count must match color attachment sample count") \ - _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_PASS, "sg_begin_pass: pass must be valid") \ + _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") \ @@ -3433,7 +3784,10 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING on vertex stage, but sampler has SG_FILTER_LINEAR filters") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on vertex stage") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_SMP_EXISTS, "sg_apply_bindings: sampler bound to vertex stage no longer alive") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMG_SMP_MIPMAPS, "sg_apply_bindings: image bound to vertex stage has mipmap_count == 1, but associated sampler mipmap filer is not SG_MIPMAPFILTER_NONE") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: storage buffer binding on vertex stage is missing or the buffer handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_STORAGEBUFFER_EXISTS, "sg_apply_bindings: storage buffer bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_STORAGEBUFFER_BINDING_BUFFERTYPE, "sg_apply_bindings: buffer bound to vertex stage storage buffer slot is not of type storage buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: unexpected storage buffer binding on vertex stage") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: image binding on fragment stage is missing or the image handle is invalid") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_EXISTS, "sg_apply_bindings: image bound to fragment stage no longer alive") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of image bound to fragment stage doesn't match shader desc") \ @@ -3447,7 +3801,10 @@ typedef struct sg_frame_stats { _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING on fragment stage, but sampler has SG_FILTER_LINEAR filters") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on fragment stage") \ _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_SMP_EXISTS, "sg_apply_bindings: sampler bound to fragment stage no longer alive") \ - _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_SMP_MIPMAPS, "sg_apply_bindings: image bound to fragment stage has mipmap_count == 1, but associated sampler mipmap filer is not SG_MIPMAPFILTER_NONE") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: storage buffer binding on fragment stage is missing or the buffer handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_STORAGEBUFFER_EXISTS, "sg_apply_bindings: storage buffer bound to fragment stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_STORAGEBUFFER_BINDING_BUFFERTYPE, "sg_apply_bindings: buffer bound to frahment stage storage buffer slot is not of type storage buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: unexpected storage buffer binding on fragment stage") \ _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_PIPELINE, "sg_apply_uniforms: must be called after sg_apply_pipeline()") \ _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_UB_AT_SLOT, "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot") \ _SG_LOGITEM_XMACRO(VALIDATE_AUB_SIZE, "sg_apply_uniforms: data size doesn't match declared uniform block size") \ @@ -3474,13 +3831,6 @@ typedef enum sg_log_item { The sg_desc struct contains configuration values for sokol_gfx, it is used as parameter to the sg_setup() call. - NOTE that all callback function pointers come in two versions, one without - a userdata pointer, and one with a userdata pointer. You would - either initialize one or the other depending on whether you pass data - to your callbacks. - - FIXME: explain the various configuration options - The default configuration is: .buffer_pool_size 128 @@ -3489,7 +3839,6 @@ typedef enum sg_log_item { .shader_pool_size 32 .pipeline_pool_size 64 .pass_pool_size 16 - .context_pool_size 16 .uniform_buffer_size 4 MB (4*1024*1024) .max_commit_listeners 1024 .disable_validation false @@ -3501,12 +3850,12 @@ typedef enum sg_log_item { .allocator.free_fn 0 (in this case, free() will be called) .allocator.user_data 0 - .context.color_format: default value depends on selected backend: + .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 swapchain) - .context.depth_format SG_PIXELFORMAT_DEPTH_STENCIL - .context.sample_count 1 + 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 @@ -3520,43 +3869,27 @@ typedef enum sg_log_item { 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) - .context.metal.device + .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 - .context.metal.renderpass_descriptor_cb - .context.metal_renderpass_descriptor_userdata_cb - A C callback function to obtain the MTLRenderPassDescriptor for the - current frame when rendering to the default framebuffer, will be called - in sg_begin_default_pass(). - .context.metal.drawable_cb - .context.metal.drawable_userdata_cb - a C callback function to obtain a MTLDrawable for the current - frame when rendering to the default framebuffer, will be called in - sg_end_pass() of the default pass - .context.metal.user_data - optional user data pointer passed to the userdata versions of - callback functions D3D11 specific: - .context.d3d11.device + .environment.d3d11.device a pointer to the ID3D11Device object, this must have been created before sg_setup() is called - .context.d3d11.device_context + .environment.d3d11.device_context a pointer to the ID3D11DeviceContext object - .context.d3d11.render_target_view_cb - .context.d3d11.render_target_view_userdata_cb - a C callback function to obtain a pointer to the current - ID3D11RenderTargetView object of the default framebuffer, - this function will be called in sg_begin_pass() when rendering - to the default framebuffer - .context.d3d11.depth_stencil_view_cb - .context.d3d11.depth_stencil_view_userdata_cb - a C callback function to obtain a pointer to the current - ID3D11DepthStencilView object of the default framebuffer, - this function will be called in sg_begin_pass() when rendering - to the default framebuffer - .context.d3d11.user_data - optional user data pointer passed to the userdata versions of - callback functions + .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 @@ -3569,80 +3902,43 @@ typedef enum sg_log_item { 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 occurence, and increase the cache size as + 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! - .context.wgpu.device + .environment.wgpu.device a WGPUDevice handle - .context.wgpu.render_format - WGPUTextureFormat of the swap chain surface - .context.wgpu.render_view_cb - .context.wgpu.render_view_userdata_cb - callback to get the current WGPUTextureView of the swapchain's - rendering attachment (may be an MSAA surface) - .context.wgpu.resolve_view_cb - .context.wgpu.resolve_view_userdata_cb - callback to get the current WGPUTextureView of the swapchain's - MSAA-resolve-target surface, must return 0 if not MSAA rendering - .context.wgpu.depth_stencil_view_cb - .context.wgpu.depth_stencil_view_userdata_cb - callback to get current default-pass depth-stencil-surface WGPUTextureView - the pixel format of the default WGPUTextureView must be WGPUTextureFormat_Depth32FloatStencil8 - .context.wgpu.user_data - optional user data pointer passed to the userdata versions of - callback functions When using sokol_gfx.h and sokol_app.h together, consider using the - helper function sapp_sgcontext() in the sokol_glue.h header to - initialize the sg_desc.context nested struct. sapp_sgcontext() returns - a completely initialized sg_context_desc struct with information + 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_metal_context_desc { +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; - const void* (*renderpass_descriptor_cb)(void); - const void* (*renderpass_descriptor_userdata_cb)(void*); - const void* (*drawable_cb)(void); - const void* (*drawable_userdata_cb)(void*); - void* user_data; -} sg_metal_context_desc; +} sg_metal_environment; -typedef struct sg_d3d11_context_desc { +typedef struct sg_d3d11_environment { const void* device; const void* device_context; - const void* (*render_target_view_cb)(void); - const void* (*render_target_view_userdata_cb)(void*); - const void* (*depth_stencil_view_cb)(void); - const void* (*depth_stencil_view_userdata_cb)(void*); - void* user_data; -} sg_d3d11_context_desc; - -typedef struct sg_wgpu_context_desc { - const void* device; // WGPUDevice - const void* (*render_view_cb)(void); // returns WGPUTextureView - const void* (*render_view_userdata_cb)(void*); - const void* (*resolve_view_cb)(void); // returns WGPUTextureView - const void* (*resolve_view_userdata_cb)(void*); - const void* (*depth_stencil_view_cb)(void); // returns WGPUTextureView - const void* (*depth_stencil_view_userdata_cb)(void*); - void* user_data; -} sg_wgpu_context_desc; +} sg_d3d11_environment; -typedef struct sg_gl_context_desc { - uint32_t (*default_framebuffer_cb)(void); - uint32_t (*default_framebuffer_userdata_cb)(void*); - void* user_data; -} sg_gl_context_desc; +typedef struct sg_wgpu_environment { + const void* device; +} sg_wgpu_environment; -typedef struct sg_context_desc { - sg_pixel_format color_format; - sg_pixel_format depth_format; - int sample_count; - sg_metal_context_desc metal; - sg_d3d11_context_desc d3d11; - sg_wgpu_context_desc wgpu; - sg_gl_context_desc gl; -} sg_context_desc; +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 @@ -3679,8 +3975,9 @@ typedef struct sg_allocator { that without logging function, sokol-gfx will be completely silent, e.g. it will not report errors, warnings and validation layer messages. For maximum error verbosity, - compile in debug mode (e.g. NDEBUG *not* defined) and install - a logger (for instance the standard logging function from sokol_log.h). + compile in debug mode (e.g. NDEBUG *not* defined) and provide a + compatible logger function in the sg_setup() call + (for instance the standard logging function from sokol_log.h). */ typedef struct sg_logger { void (*func)( @@ -3701,17 +3998,18 @@ typedef struct sg_desc { int sampler_pool_size; int shader_pool_size; int pipeline_pool_size; - int pass_pool_size; - int context_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_context_desc context; + sg_environment environment; uint32_t _end_canary; } sg_desc; @@ -3732,13 +4030,13 @@ 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_pass sg_make_pass(const sg_pass_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_pass(sg_pass pass); +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); @@ -3746,9 +4044,7 @@ 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_default_pass(const sg_pass_action* pass_action, int width, int height); -SOKOL_GFX_API_DECL void sg_begin_default_passf(const sg_pass_action* pass_action, float width, float height); -SOKOL_GFX_API_DECL void sg_begin_pass(sg_pass pass, const sg_pass_action* pass_action); +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); @@ -3766,34 +4062,36 @@ 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_pass_state(sg_pass pass); +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_pass_info sg_query_pass_info(sg_pass pass); +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_pass_desc sg_query_pass_desc(sg_pass pass); +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_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc); +SOKOL_GFX_API_DECL sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc* desc); // separate resource allocation and initialization (for async setup) SOKOL_GFX_API_DECL sg_buffer sg_alloc_buffer(void); @@ -3801,31 +4099,31 @@ 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_pass sg_alloc_pass(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_pass(sg_pass pass); +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_pass(sg_pass pass, const sg_pass_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_pass(sg_pass pass); +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_pass(sg_pass pass); +SOKOL_GFX_API_DECL void sg_fail_attachments(sg_attachments atts); // frame stats SOKOL_GFX_API_DECL void sg_enable_frame_stats(void); @@ -3833,31 +4131,190 @@ 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); -// rendering contexts (optional) -SOKOL_GFX_API_DECL sg_context sg_setup_context(void); -SOKOL_GFX_API_DECL void sg_activate_context(sg_context ctx_id); -SOKOL_GFX_API_DECL void sg_discard_context(sg_context ctx_id); - -/* Backend-specific helper functions, these may come in handy for mixing +/* 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* vs_cbufs[SG_MAX_SHADERSTAGE_UBS]; // ID3D11Buffer* (vertex stage constant buffers) + const void* fs_cbufs[SG_MAX_SHADERSTAGE_UBS]; // ID3D11Buffer* (fragment stage constant buffers) + 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 + int active_slot; +} sg_mtl_buffer_info; + +typedef struct sg_mtl_image_info { + const void* tex[SG_NUM_INFLIGHT_FRAMES]; // id + int active_slot; +} sg_mtl_image_info; + +typedef struct sg_mtl_sampler_info { + const void* smp; // id +} sg_mtl_sampler_info; + +typedef struct sg_mtl_shader_info { + const void* vs_lib; // id + const void* fs_lib; // id + const void* vs_func; // id + const void* fs_func; // id +} sg_mtl_shader_info; + +typedef struct sg_mtl_pipeline_info { + const void* rps; // id + const void* dss; // id +} 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 currrent pass +// 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); #ifdef __cplusplus } // extern "C" @@ -3870,12 +4327,10 @@ inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image( inline sg_sampler sg_make_sampler(const sg_sampler_desc& desc) { return sg_make_sampler(&desc); } inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); } inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); } -inline sg_pass sg_make_pass(const sg_pass_desc& desc) { return sg_make_pass(&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_default_pass(const sg_pass_action& pass_action, int width, int height) { return sg_begin_default_pass(&pass_action, width, height); } -inline void sg_begin_default_passf(const sg_pass_action& pass_action, float width, float height) { return sg_begin_default_passf(&pass_action, width, height); } -inline void sg_begin_pass(sg_pass pass, const sg_pass_action& pass_action) { return sg_begin_pass(pass, &pass_action); } +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(sg_shader_stage stage, int ub_index, const sg_range& data) { return sg_apply_uniforms(stage, ub_index, &data); } @@ -3884,14 +4339,14 @@ inline sg_image_desc sg_query_image_defaults(const sg_image_desc& desc) { return 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_pass_desc sg_query_pass_defaults(const sg_pass_desc& desc) { return sg_query_pass_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_pass(sg_pass pass, const sg_pass_desc& desc) { return sg_init_pass(pass, &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); } @@ -3908,8 +4363,8 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #ifdef SOKOL_GFX_IMPL #define SOKOL_GFX_IMPL_INCLUDED (1) -#if !(defined(SOKOL_GLCORE33)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_DUMMY_BACKEND)) -#error "Please select a backend with SOKOL_GLCORE33, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" +#if !(defined(SOKOL_GLCORE)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_DUMMY_BACKEND)) +#error "Please select a backend with SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" #endif #if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) #error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sg_desc.allocator to override memory allocation functions" @@ -4020,18 +4475,19 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #endif #endif #import + #import // needed for CAMetalDrawable #elif defined(SOKOL_WGPU) #include #if defined(__EMSCRIPTEN__) #include #endif -#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) +#elif defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) #define _SOKOL_ANY_GL (1) // include platform specific GL headers (or on Win32: use an embedded GL loader) #if !defined(SOKOL_EXTERNAL_GL_LOADER) #if defined(_WIN32) - #if defined(SOKOL_GLCORE33) && !defined(SOKOL_EXTERNAL_GL_LOADER) + #if defined(SOKOL_GLCORE) && !defined(SOKOL_EXTERNAL_GL_LOADER) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif @@ -4058,8 +4514,13 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #include #endif #elif defined(__linux__) || defined(__unix__) - #define GL_GLEXT_PROTOTYPES - #include + #if defined(SOKOL_GLCORE) + #define GL_GLEXT_PROTOTYPES + #include + #else + #include + #include + #endif #endif #endif @@ -4297,13 +4758,21 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define GL_CLAMP_TO_BORDER 0x812D #define GL_TEXTURE_BORDER_COLOR 0x1004 #define GL_CURRENT_PROGRAM 0x8B8D - #define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB + #define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A #define GL_UNPACK_ALIGNMENT 0x0CF5 #define GL_FRAMEBUFFER_SRGB 0x8DB9 #define GL_TEXTURE_COMPARE_MODE 0x884C #define GL_TEXTURE_COMPARE_FUNC 0x884D #define GL_COMPARE_REF_TO_TEXTURE 0x884E #define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F + #define GL_TEXTURE_MAX_LEVEL 0x813D + #define GL_FRAMEBUFFER_UNDEFINED 0x8219 + #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 + #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 + #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD + #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 + #define GL_MAJOR_VERSION 0x821B + #define GL_MINOR_VERSION 0x821C #endif #ifndef GL_UNSIGNED_INT_2_10_10_10_REV @@ -4327,6 +4796,9 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 #endif + #ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT + #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F + #endif #ifndef GL_COMPRESSED_RED_RGTC1 #define GL_COMPRESSED_RED_RGTC1 0x8DBB #endif @@ -4366,18 +4838,36 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #ifndef GL_COMPRESSED_RGB8_ETC2 #define GL_COMPRESSED_RGB8_ETC2 0x9274 #endif + #ifndef GL_COMPRESSED_SRGB8_ETC2 + #define GL_COMPRESSED_SRGB8_ETC2 0x9275 + #endif #ifndef GL_COMPRESSED_RGBA8_ETC2_EAC #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 #endif + #ifndef GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC + #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 + #endif #ifndef GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 #endif + #ifndef GL_COMPRESSED_R11_EAC + #define GL_COMPRESSED_R11_EAC 0x9270 + #endif + #ifndef GL_COMPRESSED_SIGNED_R11_EAC + #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 + #endif #ifndef GL_COMPRESSED_RG11_EAC #define GL_COMPRESSED_RG11_EAC 0x9272 #endif #ifndef GL_COMPRESSED_SIGNED_RG11_EAC #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 #endif + #ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR + #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 + #endif + #ifndef GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR + #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 + #endif #ifndef GL_DEPTH24_STENCIL8 #define GL_DEPTH24_STENCIL8 0x88F0 #endif @@ -4395,6 +4885,21 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #endif #endif +#if defined(SOKOL_GLES3) + // on WebGL2, GL_FRAMEBUFFER_UNDEFINED technically doesn't exist (it is defined + // in the Emscripten headers, but may not exist in other WebGL2 shims) + // see: https://github.com/floooh/sokol/pull/933 + #ifndef GL_FRAMEBUFFER_UNDEFINED + #define GL_FRAMEBUFFER_UNDEFINED 0x8219 + #endif +#endif + +// make some GL constants generally available to simplify compilation, +// use of those constants will be filtered by runtime flags +#ifndef GL_SHADER_STORAGE_BUFFER +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#endif + // ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ // ██ ██ ██ ██ ██ ██ ██ ██ ██ // ███████ ██ ██████ ██ ██ ██ ██ ███████ @@ -4405,7 +4910,6 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ // resource pool slots typedef struct { uint32_t id; - uint32_t ctx_id; sg_resource_state state; } _sg_slot_t; @@ -4427,7 +4931,7 @@ _SOKOL_PRIVATE int _sg_slot_index(uint32_t id); // constants enum { - _SG_STRING_SIZE = 16, + _SG_STRING_SIZE = 32, _SG_SLOT_SHIFT = 16, _SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1, _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), @@ -4436,8 +4940,7 @@ enum { _SG_DEFAULT_SAMPLER_POOL_SIZE = 64, _SG_DEFAULT_SHADER_POOL_SIZE = 32, _SG_DEFAULT_PIPELINE_POOL_SIZE = 64, - _SG_DEFAULT_PASS_POOL_SIZE = 16, - _SG_DEFAULT_CONTEXT_POOL_SIZE = 16, + _SG_DEFAULT_ATTACHMENTS_POOL_SIZE = 16, _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, _SG_DEFAULT_MAX_COMMIT_LISTENERS = 1024, _SG_DEFAULT_WGPU_BINDGROUP_CACHE_SIZE = 1024, @@ -4548,6 +5051,11 @@ typedef struct { size_t size; } _sg_shader_uniform_block_t; +typedef struct { + bool used; + bool readonly; +} _sg_shader_storage_buffer_t; + typedef struct { sg_image_type image_type; sg_image_sample_type sample_type; @@ -4566,10 +5074,12 @@ typedef struct { typedef struct { int num_uniform_blocks; + int num_storage_buffers; int num_images; int num_samplers; int num_image_samplers; _sg_shader_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_shader_storage_buffer_t storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; _sg_shader_sampler_t samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; _sg_shader_image_sampler_t image_samplers[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; @@ -4624,6 +5134,16 @@ _SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_sh stage->image_samplers[img_smp_index].sampler_slot = img_smp_desc->sampler_slot; stage->num_image_samplers++; } + SOKOL_ASSERT(stage->num_storage_buffers == 0); + for (int sbuf_index = 0; sbuf_index < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; sbuf_index++) { + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + if (!sbuf_desc->used) { + break; + } + stage->storage_buffers[sbuf_index].used = sbuf_desc->used; + stage->storage_buffers[sbuf_index].readonly = sbuf_desc->readonly; + stage->num_storage_buffers++; + } } } @@ -4672,36 +5192,36 @@ typedef struct { sg_image image_id; int mip_level; int slice; -} _sg_pass_attachment_common_t; +} _sg_attachment_common_t; typedef struct { int width; int height; - int num_color_atts; - _sg_pass_attachment_common_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_pass_attachment_common_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_pass_attachment_common_t ds_att; -} _sg_pass_common_t; + int num_colors; + _sg_attachment_common_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_attachment_common_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_attachment_common_t depth_stencil; +} _sg_attachments_common_t; -_SOKOL_PRIVATE void _sg_pass_attachment_common_init(_sg_pass_attachment_common_t* cmn, const sg_pass_attachment_desc* desc) { +_SOKOL_PRIVATE void _sg_attachment_common_init(_sg_attachment_common_t* cmn, const sg_attachment_desc* desc) { cmn->image_id = desc->image; cmn->mip_level = desc->mip_level; cmn->slice = desc->slice; } -_SOKOL_PRIVATE void _sg_pass_common_init(_sg_pass_common_t* cmn, const sg_pass_desc* desc, int width, int height) { +_SOKOL_PRIVATE void _sg_attachments_common_init(_sg_attachments_common_t* cmn, const sg_attachments_desc* desc, int width, int height) { SOKOL_ASSERT((width > 0) && (height > 0)); cmn->width = width; cmn->height = height; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (desc->color_attachments[i].image.id != SG_INVALID_ID) { - cmn->num_color_atts++; - _sg_pass_attachment_common_init(&cmn->color_atts[i], &desc->color_attachments[i]); - _sg_pass_attachment_common_init(&cmn->resolve_atts[i], &desc->resolve_attachments[i]); + if (desc->colors[i].image.id != SG_INVALID_ID) { + cmn->num_colors++; + _sg_attachment_common_init(&cmn->colors[i], &desc->colors[i]); + _sg_attachment_common_init(&cmn->resolves[i], &desc->resolves[i]); } } - if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { - _sg_pass_attachment_common_init(&cmn->ds_att, &desc->depth_stencil_attachment); + if (desc->depth_stencil.image.id != SG_INVALID_ID) { + _sg_attachment_common_init(&cmn->depth_stencil, &desc->depth_stencil); } } @@ -4743,22 +5263,20 @@ typedef struct { typedef struct { _sg_slot_t slot; - _sg_pass_common_t cmn; + _sg_attachments_common_t cmn; struct { - _sg_dummy_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_dummy_attachment_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_dummy_attachment_t ds_att; + _sg_dummy_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_dummy_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_dummy_attachment_t depth_stencil; } dmy; -} _sg_dummy_pass_t; -typedef _sg_dummy_pass_t _sg_pass_t; -typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; - -typedef struct { - _sg_slot_t slot; -} _sg_dummy_context_t; -typedef _sg_dummy_context_t _sg_context_t; +} _sg_dummy_attachments_t; +typedef _sg_dummy_attachments_t _sg_attachments_t; #elif defined(_SOKOL_ANY_GL) + +#define _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE (SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS * SG_NUM_SHADER_STAGES) +#define _SG_GL_STORAGEBUFFER_STAGE_INDEX_PITCH (SG_MAX_SHADERSTAGE_STORAGEBUFFERS) + typedef struct { _sg_slot_t slot; _sg_buffer_common_t cmn; @@ -4862,24 +5380,16 @@ typedef struct { typedef struct { _sg_slot_t slot; - _sg_pass_common_t cmn; + _sg_attachments_common_t cmn; struct { GLuint fb; - _sg_gl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_gl_attachment_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_gl_attachment_t ds_att; + _sg_gl_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_gl_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_gl_attachment_t depth_stencil; GLuint msaa_resolve_framebuffer[SG_MAX_COLOR_ATTACHMENTS]; } gl; -} _sg_gl_pass_t; -typedef _sg_gl_pass_t _sg_pass_t; -typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; - -typedef struct { - _sg_slot_t slot; - GLuint vao; - GLuint default_framebuffer; -} _sg_gl_context_t; -typedef _sg_gl_context_t _sg_context_t; +} _sg_gl_attachments_t; +typedef _sg_gl_attachments_t _sg_attachments_t; typedef struct { _sg_gl_attr_t gl_attr; @@ -4892,8 +5402,6 @@ typedef struct { GLuint sampler; } _sg_gl_cache_texture_sampler_bind_slot; -#define _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE (SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS * SG_NUM_SHADER_STAGES) - typedef struct { sg_depth_state depth; sg_stencil_state stencil; @@ -4908,8 +5416,11 @@ typedef struct { _sg_gl_cache_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; GLuint vertex_buffer; GLuint index_buffer; + GLuint storage_buffer; // general bind point + GLuint stage_storage_buffers[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; GLuint stored_vertex_buffer; GLuint stored_index_buffer; + GLuint stored_storage_buffer; GLuint prog; _sg_gl_cache_texture_sampler_bind_slot texture_samplers[_SG_GL_TEXTURE_SAMPLER_CACHE_SIZE]; _sg_gl_cache_texture_sampler_bind_slot stored_texture_sampler; @@ -4923,12 +5434,7 @@ typedef struct { typedef struct { bool valid; - bool in_pass; - int cur_pass_width; - int cur_pass_height; - _sg_context_t* cur_context; - _sg_pass_t* cur_pass; - sg_pass cur_pass_id; + GLuint vao; _sg_gl_state_cache_t cache; bool ext_anisotropic; GLint max_anisotropy; @@ -4947,6 +5453,7 @@ typedef struct { _sg_buffer_common_t cmn; struct { ID3D11Buffer* buf; + ID3D11ShaderResourceView* srv; } d3d11; } _sg_d3d11_buffer_t; typedef _sg_d3d11_buffer_t _sg_buffer_t; @@ -5023,42 +5530,27 @@ typedef struct { typedef struct { _sg_slot_t slot; - _sg_pass_common_t cmn; + _sg_attachments_common_t cmn; struct { - _sg_d3d11_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_d3d11_attachment_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_d3d11_attachment_t ds_att; + _sg_d3d11_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_d3d11_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_d3d11_attachment_t depth_stencil; } d3d11; -} _sg_d3d11_pass_t; -typedef _sg_d3d11_pass_t _sg_pass_t; -typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; - -typedef struct { - _sg_slot_t slot; -} _sg_d3d11_context_t; -typedef _sg_d3d11_context_t _sg_context_t; +} _sg_d3d11_attachments_t; +typedef _sg_d3d11_attachments_t _sg_attachments_t; typedef struct { bool valid; ID3D11Device* dev; ID3D11DeviceContext* ctx; - const void* (*rtv_cb)(void); - const void* (*rtv_userdata_cb)(void*); - const void* (*dsv_cb)(void); - const void* (*dsv_userdata_cb)(void*); - void* user_data; - bool in_pass; bool use_indexed_draw; bool use_instanced_draw; - int cur_width; - int cur_height; - int num_rtvs; - _sg_pass_t* cur_pass; - sg_pass cur_pass_id; _sg_pipeline_t* cur_pipeline; sg_pipeline cur_pipeline_id; - ID3D11RenderTargetView* cur_rtvs[SG_MAX_COLOR_ATTACHMENTS]; - ID3D11DepthStencilView* cur_dsv; + struct { + ID3D11RenderTargetView* render_view; + ID3D11RenderTargetView* resolve_view; + } cur_pass; // on-demand loaded d3dcompiler_47.dll handles HINSTANCE d3dcompiler_dll; bool d3dcompiler_dll_load_failed; @@ -5155,57 +5647,39 @@ typedef struct { typedef struct { _sg_slot_t slot; - _sg_pass_common_t cmn; + _sg_attachments_common_t cmn; struct { - _sg_mtl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_mtl_attachment_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_mtl_attachment_t ds_att; + _sg_mtl_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_mtl_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_mtl_attachment_t depth_stencil; } mtl; -} _sg_mtl_pass_t; -typedef _sg_mtl_pass_t _sg_pass_t; -typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; +} _sg_mtl_attachments_t; +typedef _sg_mtl_attachments_t _sg_attachments_t; -typedef struct { - _sg_slot_t slot; -} _sg_mtl_context_t; -typedef _sg_mtl_context_t _sg_context_t; - -// resouce binding state cache +// resource binding state cache typedef struct { const _sg_pipeline_t* cur_pipeline; sg_pipeline cur_pipeline_id; const _sg_buffer_t* cur_indexbuffer; - int cur_indexbuffer_offset; sg_buffer cur_indexbuffer_id; - const _sg_buffer_t* cur_vertexbuffers[SG_MAX_VERTEX_BUFFERS]; + int cur_indexbuffer_offset; int cur_vertexbuffer_offsets[SG_MAX_VERTEX_BUFFERS]; sg_buffer cur_vertexbuffer_ids[SG_MAX_VERTEX_BUFFERS]; - const _sg_image_t* cur_vs_images[SG_MAX_SHADERSTAGE_IMAGES]; sg_image cur_vs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; - const _sg_image_t* cur_fs_images[SG_MAX_SHADERSTAGE_IMAGES]; sg_image cur_fs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; - const _sg_sampler_t* cur_vs_samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; sg_sampler cur_vs_sampler_ids[SG_MAX_SHADERSTAGE_SAMPLERS]; - const _sg_sampler_t* cur_fs_samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; sg_sampler cur_fs_sampler_ids[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_buffer cur_vs_storagebuffer_ids[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + sg_buffer cur_fs_storagebuffer_ids[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; } _sg_mtl_state_cache_t; typedef struct { bool valid; bool use_shared_storage_mode; - const void*(*renderpass_descriptor_cb)(void); - const void*(*renderpass_descriptor_userdata_cb)(void*); - const void*(*drawable_cb)(void); - const void*(*drawable_userdata_cb)(void*); - void* user_data; uint32_t cur_frame_rotate_index; int ub_size; int cur_ub_offset; uint8_t* cur_ub_base_ptr; - bool in_pass; - bool pass_valid; - int cur_width; - int cur_height; _sg_mtl_state_cache_t state_cache; _sg_mtl_idpool_t idpool; dispatch_semaphore_t sem; @@ -5213,6 +5687,7 @@ typedef struct { id cmd_queue; id cmd_buffer; id cmd_encoder; + id cur_drawable; id uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; } _sg_mtl_backend_t; @@ -5223,6 +5698,7 @@ typedef struct { #define _SG_WGPU_NUM_BINDGROUPS (2) // 0: uniforms, 1: images and sampler on both shader stages #define _SG_WGPU_UNIFORM_BINDGROUP_INDEX (0) #define _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX (1) +#define _SG_WGPU_MAX_BINDGROUP_ENTRIES (SG_NUM_SHADER_STAGES * (SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS + SG_MAX_SHADERSTAGE_STORAGEBUFFERS)) typedef struct { _sg_slot_t slot; @@ -5285,20 +5761,14 @@ typedef struct { typedef struct { _sg_slot_t slot; - _sg_pass_common_t cmn; + _sg_attachments_common_t cmn; struct { - _sg_wgpu_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_wgpu_attachment_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_wgpu_attachment_t ds_att; + _sg_wgpu_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_wgpu_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_wgpu_attachment_t depth_stencil; } wgpu; -} _sg_wgpu_pass_t; -typedef _sg_wgpu_pass_t _sg_pass_t; -typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; - -typedef struct { - _sg_slot_t slot; -} _sg_wgpu_context_t; -typedef _sg_wgpu_context_t _sg_context_t; +} _sg_wgpu_attachments_t; +typedef _sg_wgpu_attachments_t _sg_attachments_t; // a pool of per-frame uniform buffers typedef struct { @@ -5317,10 +5787,20 @@ typedef struct { uint32_t id; } _sg_wgpu_bindgroup_handle_t; -#define _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS (1 + SG_NUM_SHADER_STAGES * (SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS)) +typedef enum { + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_NONE = 0, + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_IMAGE = 0x1111111111111111, + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER = 0x2222222222222222, + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_STORAGEBUFFER = 0x3333333333333333, + _SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE = 0x4444444444444444, +} _sg_wgpu_bindgroups_cache_item_type_t; + +#define _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS (1 + _SG_WGPU_MAX_BINDGROUP_ENTRIES) typedef struct { uint64_t hash; - uint32_t items[_SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS]; + // the format of cache key items is (_sg_wgpu_bindgroups_cache_item_type_t << 32) | handle.id, + // where the item type is a per-resource-type bit pattern + uint64_t items[_SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS]; } _sg_wgpu_bindgroups_cache_key_t; typedef struct { @@ -5355,18 +5835,8 @@ typedef struct { // the WGPU backend state typedef struct { bool valid; - WGPUDevice dev; - WGPUTextureView (*render_view_cb)(void); - WGPUTextureView (*render_view_userdata_cb)(void*); - WGPUTextureView (*resolve_view_cb)(void); - WGPUTextureView (*resolve_view_userdata_cb)(void*); - WGPUTextureView (*depth_stencil_view_cb)(void); - WGPUTextureView (*depth_stencil_view_userdata_cb)(void*); - void* user_data; - bool in_pass; bool use_indexed_draw; - int cur_width; - int cur_height; + WGPUDevice dev; WGPUSupportedLimits limits; WGPUQueue queue; WGPUCommandEncoder cmd_enc; @@ -5392,15 +5862,13 @@ typedef struct { _sg_pool_t sampler_pool; _sg_pool_t shader_pool; _sg_pool_t pipeline_pool; - _sg_pool_t pass_pool; - _sg_pool_t context_pool; + _sg_pool_t attachments_pool; _sg_buffer_t* buffers; _sg_image_t* images; _sg_sampler_t* samplers; _sg_shader_t* shaders; _sg_pipeline_t* pipelines; - _sg_pass_t* passes; - _sg_context_t* contexts; + _sg_attachments_t* attachments; } _sg_pools_t; typedef struct { @@ -5415,27 +5883,49 @@ typedef struct { int num_vbs; int num_vs_imgs; int num_vs_smps; + int num_vs_sbufs; int num_fs_imgs; int num_fs_smps; + int num_fs_sbufs; int vb_offsets[SG_MAX_VERTEX_BUFFERS]; int ib_offset; _sg_buffer_t* vbs[SG_MAX_VERTEX_BUFFERS]; _sg_buffer_t* ib; _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES]; _sg_sampler_t* vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_buffer_t* vs_sbufs[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES]; _sg_sampler_t* fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_buffer_t* fs_sbufs[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; } _sg_bindings_t; +typedef struct { + bool sample; + bool filter; + bool render; + bool blend; + bool msaa; + bool depth; +} _sg_pixelformat_info_t; + typedef struct { bool valid; sg_desc desc; // original desc with default values patched in uint32_t frame_index; - sg_context active_context; - sg_pass cur_pass; + struct { + bool valid; + bool in_pass; + sg_attachments atts_id; // SG_INVALID_ID in a swapchain pass + _sg_attachments_t* atts; // 0 in a swapchain pass + int width; + int height; + struct { + sg_pixel_format color_fmt; + sg_pixel_format depth_fmt; + int sample_count; + } swapchain; + } cur_pass; sg_pipeline cur_pipeline; - bool pass_valid; - bool bindings_applied; bool next_draw_valid; #if defined(SOKOL_DEBUG) sg_log_item validate_error; @@ -5444,7 +5934,7 @@ typedef struct { sg_backend backend; sg_features features; sg_limits limits; - sg_pixelformat_info formats[_SG_PIXELFORMAT_NUM]; + _sg_pixelformat_info_t formats[_SG_PIXELFORMAT_NUM]; bool stats_enabled; sg_frame_stats stats; sg_frame_stats prev_stats; @@ -5732,6 +6222,7 @@ _SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_BC1_RGBA: case SG_PIXELFORMAT_BC2_RGBA: case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC3_SRGBA: case SG_PIXELFORMAT_BC4_R: case SG_PIXELFORMAT_BC4_RSN: case SG_PIXELFORMAT_BC5_RG: @@ -5739,15 +6230,22 @@ _SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_BC6H_RGBF: case SG_PIXELFORMAT_BC6H_RGBUF: case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: case SG_PIXELFORMAT_PVRTC_RGB_2BPP: case SG_PIXELFORMAT_PVRTC_RGB_4BPP: case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: case SG_PIXELFORMAT_ETC2_RGB8A1: case SG_PIXELFORMAT_ETC2_RGBA8: - case SG_PIXELFORMAT_ETC2_RG11: - case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return true; default: return false; @@ -5822,6 +6320,9 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { case SG_PIXELFORMAT_RGBA32SI: case SG_PIXELFORMAT_RGBA32F: return 16; + case SG_PIXELFORMAT_DEPTH: + case SG_PIXELFORMAT_DEPTH_STENCIL: + return 4; default: SOKOL_UNREACHABLE; return 0; @@ -5868,20 +6369,28 @@ _SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) case SG_PIXELFORMAT_BC4_R: case SG_PIXELFORMAT_BC4_RSN: case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: pitch = ((width + 3) / 4) * 8; pitch = pitch < 8 ? 8 : pitch; break; case SG_PIXELFORMAT_BC2_RGBA: case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC3_SRGBA: case SG_PIXELFORMAT_BC5_RG: case SG_PIXELFORMAT_BC5_RGSN: case SG_PIXELFORMAT_BC6H_RGBF: case SG_PIXELFORMAT_BC6H_RGBUF: case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: case SG_PIXELFORMAT_ETC2_RGBA8: - case SG_PIXELFORMAT_ETC2_RG11: - case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: pitch = ((width + 3) / 4) * 16; pitch = pitch < 16 ? 16 : pitch; break; @@ -5909,17 +6418,25 @@ _SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { case SG_PIXELFORMAT_BC4_R: case SG_PIXELFORMAT_BC4_RSN: case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: case SG_PIXELFORMAT_ETC2_RGB8A1: case SG_PIXELFORMAT_ETC2_RGBA8: - case SG_PIXELFORMAT_ETC2_RG11: - case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: case SG_PIXELFORMAT_BC2_RGBA: case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC3_SRGBA: case SG_PIXELFORMAT_BC5_RG: case SG_PIXELFORMAT_BC5_RGSN: case SG_PIXELFORMAT_BC6H_RGBF: case SG_PIXELFORMAT_BC6H_RGBUF: case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: num_rows = ((height + 3) / 4); break; case SG_PIXELFORMAT_PVRTC_RGB_4BPP: @@ -5958,7 +6475,7 @@ _SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height, } // capability table pixel format helper functions -_SOKOL_PRIVATE void _sg_pixelformat_all(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_all(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->filter = true; pfi->blend = true; @@ -5966,88 +6483,95 @@ _SOKOL_PRIVATE void _sg_pixelformat_all(sg_pixelformat_info* pfi) { pfi->msaa = true; } -_SOKOL_PRIVATE void _sg_pixelformat_s(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_s(_sg_pixelformat_info_t* pfi) { pfi->sample = true; } -_SOKOL_PRIVATE void _sg_pixelformat_sf(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_sf(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->filter = true; } -_SOKOL_PRIVATE void _sg_pixelformat_sr(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_sr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfr(_sg_pixelformat_info_t* pfi) { pfi->sample = true; + pfi->filter = true; pfi->render = true; } -_SOKOL_PRIVATE void _sg_pixelformat_srmd(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_srmd(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->render = true; pfi->msaa = true; pfi->depth = true; } -_SOKOL_PRIVATE void _sg_pixelformat_srm(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_srm(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->render = true; pfi->msaa = true; } -_SOKOL_PRIVATE void _sg_pixelformat_sfrm(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_sfrm(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->filter = true; pfi->render = true; pfi->msaa = true; } -_SOKOL_PRIVATE void _sg_pixelformat_sbrm(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_sbrm(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->blend = true; pfi->render = true; pfi->msaa = true; } -_SOKOL_PRIVATE void _sg_pixelformat_sbr(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_sbr(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->blend = true; pfi->render = true; } -_SOKOL_PRIVATE void _sg_pixelformat_sfbr(sg_pixelformat_info* pfi) { +_SOKOL_PRIVATE void _sg_pixelformat_sfbr(_sg_pixelformat_info_t* pfi) { pfi->sample = true; pfi->filter = true; pfi->blend = true; pfi->render = true; } -_SOKOL_PRIVATE void _sg_resolve_default_pass_action(const sg_pass_action* from, sg_pass_action* to) { - SOKOL_ASSERT(from && to); - *to = *from; +_SOKOL_PRIVATE sg_pass_action _sg_pass_action_defaults(const sg_pass_action* action) { + SOKOL_ASSERT(action); + sg_pass_action res = *action; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (to->colors[i].load_action == _SG_LOADACTION_DEFAULT) { - to->colors[i].load_action = SG_LOADACTION_CLEAR; - to->colors[i].clear_value.r = SG_DEFAULT_CLEAR_RED; - to->colors[i].clear_value.g = SG_DEFAULT_CLEAR_GREEN; - to->colors[i].clear_value.b = SG_DEFAULT_CLEAR_BLUE; - to->colors[i].clear_value.a = SG_DEFAULT_CLEAR_ALPHA; + if (res.colors[i].load_action == _SG_LOADACTION_DEFAULT) { + res.colors[i].load_action = SG_LOADACTION_CLEAR; + res.colors[i].clear_value.r = SG_DEFAULT_CLEAR_RED; + res.colors[i].clear_value.g = SG_DEFAULT_CLEAR_GREEN; + res.colors[i].clear_value.b = SG_DEFAULT_CLEAR_BLUE; + res.colors[i].clear_value.a = SG_DEFAULT_CLEAR_ALPHA; } - if (to->colors[i].store_action == _SG_STOREACTION_DEFAULT) { - to->colors[i].store_action = SG_STOREACTION_STORE; + if (res.colors[i].store_action == _SG_STOREACTION_DEFAULT) { + res.colors[i].store_action = SG_STOREACTION_STORE; } } - if (to->depth.load_action == _SG_LOADACTION_DEFAULT) { - to->depth.load_action = SG_LOADACTION_CLEAR; - to->depth.clear_value = SG_DEFAULT_CLEAR_DEPTH; + if (res.depth.load_action == _SG_LOADACTION_DEFAULT) { + res.depth.load_action = SG_LOADACTION_CLEAR; + res.depth.clear_value = SG_DEFAULT_CLEAR_DEPTH; } - if (to->depth.store_action == _SG_STOREACTION_DEFAULT) { - to->depth.store_action = SG_STOREACTION_DONTCARE; + if (res.depth.store_action == _SG_STOREACTION_DEFAULT) { + res.depth.store_action = SG_STOREACTION_DONTCARE; } - if (to->stencil.load_action == _SG_LOADACTION_DEFAULT) { - to->stencil.load_action = SG_LOADACTION_CLEAR; - to->stencil.clear_value = SG_DEFAULT_CLEAR_STENCIL; + if (res.stencil.load_action == _SG_LOADACTION_DEFAULT) { + res.stencil.load_action = SG_LOADACTION_CLEAR; + res.stencil.clear_value = SG_DEFAULT_CLEAR_STENCIL; } - if (to->stencil.store_action == _SG_STOREACTION_DEFAULT) { - to->stencil.store_action = SG_STOREACTION_DONTCARE; + if (res.stencil.store_action == _SG_STOREACTION_DEFAULT) { + res.stencil.store_action = SG_STOREACTION_DONTCARE; } + return res; } // ██████ ██ ██ ███ ███ ███ ███ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ @@ -6082,22 +6606,6 @@ _SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) { // empty } -_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_dummy_discard_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); -} - -_SOKOL_PRIVATE void _sg_dummy_activate_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); -} - _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); _SOKOL_UNUSED(buf); @@ -6165,64 +6673,61 @@ _SOKOL_PRIVATE void _sg_dummy_discard_pipeline(_sg_pipeline_t* pip) { _SOKOL_UNUSED(pip); } -_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pass(_sg_pass_t* pass, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && desc); +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); SOKOL_ASSERT(color_images && resolve_images); - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - const sg_pass_attachment_desc* color_desc = &desc->color_attachments[i]; + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; _SOKOL_UNUSED(color_desc); SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); - SOKOL_ASSERT(0 == pass->dmy.color_atts[i].image); + SOKOL_ASSERT(0 == atts->dmy.colors[i].image); SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); - pass->dmy.color_atts[i].image = color_images[i]; + atts->dmy.colors[i].image = color_images[i]; - const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; if (resolve_desc->image.id != SG_INVALID_ID) { - SOKOL_ASSERT(0 == pass->dmy.resolve_atts[i].image); + SOKOL_ASSERT(0 == atts->dmy.resolves[i].image); SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); - pass->dmy.resolve_atts[i].image = resolve_images[i]; + atts->dmy.resolves[i].image = resolve_images[i]; } } - SOKOL_ASSERT(0 == pass->dmy.ds_att.image); - const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + SOKOL_ASSERT(0 == atts->dmy.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; if (ds_desc->image.id != SG_INVALID_ID) { SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); - pass->dmy.ds_att.image = ds_img; + atts->dmy.depth_stencil.image = ds_img; } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_dummy_discard_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - _SOKOL_UNUSED(pass); +_SOKOL_PRIVATE void _sg_dummy_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + _SOKOL_UNUSED(atts); } -_SOKOL_PRIVATE _sg_image_t* _sg_dummy_pass_color_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - return pass->dmy.color_atts[index].image; +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->dmy.colors[index].image; } -_SOKOL_PRIVATE _sg_image_t* _sg_dummy_pass_resolve_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - return pass->dmy.resolve_atts[index].image; +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->dmy.resolves[index].image; } -_SOKOL_PRIVATE _sg_image_t* _sg_dummy_pass_ds_image(const _sg_pass_t* pass) { - SOKOL_ASSERT(pass); - return pass->dmy.ds_att.image; +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_attachments_ds_image(const _sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + return atts->dmy.depth_stencil.image; } -_SOKOL_PRIVATE void _sg_dummy_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { - SOKOL_ASSERT(action); +_SOKOL_PRIVATE void _sg_dummy_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); _SOKOL_UNUSED(pass); - _SOKOL_UNUSED(action); - _SOKOL_UNUSED(w); - _SOKOL_UNUSED(h); } _SOKOL_PRIVATE void _sg_dummy_end_pass(void) { @@ -6312,8 +6817,13 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data // optional GL loader for win32 #if defined(_SOKOL_USE_WIN32_GL_LOADER) +#ifndef SG_GL_FUNCS_EXT +#define SG_GL_FUNCS_EXT +#endif + // X Macro list of GL function names and signatures #define _SG_GL_FUNCS \ + SG_GL_FUNCS_EXT \ _SG_XMACRO(glBindVertexArray, void, (GLuint array)) \ _SG_XMACRO(glFramebufferTextureLayer, void, (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)) \ _SG_XMACRO(glGenFramebuffers, void, (GLsizei n, GLuint * framebuffers)) \ @@ -6416,7 +6926,8 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glSamplerParameteri, void, (GLuint sampler, GLenum pname, GLint param)) \ _SG_XMACRO(glSamplerParameterf, void, (GLuint sampler, GLenum pname, GLfloat param)) \ _SG_XMACRO(glSamplerParameterfv, void, (GLuint sampler, GLenum pname, const GLfloat* params)) \ - _SG_XMACRO(glDeleteSamplers, void, (GLsizei n, const GLuint* samplers)) + _SG_XMACRO(glDeleteSamplers, void, (GLsizei n, const GLuint* samplers)) \ + _SG_XMACRO(glBindBufferBase, void, (GLenum target, GLuint index, GLuint buffer)) // generate GL function pointer typedefs #define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; @@ -6463,6 +6974,7 @@ _SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { switch (t) { case SG_BUFFERTYPE_VERTEXBUFFER: return GL_ARRAY_BUFFER; case SG_BUFFERTYPE_INDEXBUFFER: return GL_ELEMENT_ARRAY_BUFFER; + case SG_BUFFERTYPE_STORAGEBUFFER: return GL_SHADER_STORAGE_BUFFER; default: SOKOL_UNREACHABLE; return 0; } } @@ -6644,14 +7156,12 @@ _SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) { _SOKOL_PRIVATE GLenum _sg_gl_min_filter(sg_filter min_f, sg_filter mipmap_f) { if (min_f == SG_FILTER_NEAREST) { switch (mipmap_f) { - case SG_FILTER_NONE: return GL_NEAREST; case SG_FILTER_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; case SG_FILTER_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; default: SOKOL_UNREACHABLE; return (GLenum)0; } } else if (min_f == SG_FILTER_LINEAR) { switch (mipmap_f) { - case SG_FILTER_NONE: return GL_LINEAR; case SG_FILTER_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; case SG_FILTER_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; default: SOKOL_UNREACHABLE; return (GLenum)0; @@ -6672,7 +7182,7 @@ _SOKOL_PRIVATE GLenum _sg_gl_mag_filter(sg_filter mag_f) { _SOKOL_PRIVATE GLenum _sg_gl_wrap(sg_wrap w) { switch (w) { case SG_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; #else case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_EDGE; @@ -6805,6 +7315,8 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; case SG_PIXELFORMAT_BC3_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC3_SRGBA: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; case SG_PIXELFORMAT_BC4_R: return GL_COMPRESSED_RED_RGTC1; case SG_PIXELFORMAT_BC4_RSN: @@ -6819,6 +7331,8 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; case SG_PIXELFORMAT_BC7_RGBA: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_BC7_SRGBA: + return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; case SG_PIXELFORMAT_PVRTC_RGB_4BPP: @@ -6829,14 +7343,26 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_SRGB8: + return GL_COMPRESSED_SRGB8_ETC2; case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC; - case SG_PIXELFORMAT_ETC2_RG11: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + case SG_PIXELFORMAT_EAC_R11: + return GL_COMPRESSED_R11_EAC; + case SG_PIXELFORMAT_EAC_R11SN: + return GL_COMPRESSED_SIGNED_R11_EAC; + case SG_PIXELFORMAT_EAC_RG11: return GL_COMPRESSED_RG11_EAC; - case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_EAC_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; default: SOKOL_UNREACHABLE; return 0; } @@ -6895,6 +7421,7 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_BC1_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; case SG_PIXELFORMAT_BC2_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; case SG_PIXELFORMAT_BC3_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC3_SRGBA: return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; case SG_PIXELFORMAT_BC4_R: return GL_COMPRESSED_RED_RGTC1; case SG_PIXELFORMAT_BC4_RSN: return GL_COMPRESSED_SIGNED_RED_RGTC1; case SG_PIXELFORMAT_BC5_RG: return GL_COMPRESSED_RED_GREEN_RGTC2; @@ -6902,15 +7429,22 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_BC6H_RGBF: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; case SG_PIXELFORMAT_BC6H_RGBUF: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; case SG_PIXELFORMAT_BC7_RGBA: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_BC7_SRGBA: return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_SRGB8: return GL_COMPRESSED_SRGB8_ETC2; case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC; - case SG_PIXELFORMAT_ETC2_RG11: return GL_COMPRESSED_RG11_EAC; - case SG_PIXELFORMAT_ETC2_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + case SG_PIXELFORMAT_EAC_R11: return GL_COMPRESSED_R11_EAC; + case SG_PIXELFORMAT_EAC_R11SN: return GL_COMPRESSED_SIGNED_R11_EAC; + case SG_PIXELFORMAT_EAC_RG11: return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_EAC_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; default: SOKOL_UNREACHABLE; return 0; } } @@ -7023,6 +7557,7 @@ _SOKOL_PRIVATE void _sg_gl_init_pixelformats_s3tc(void) { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); } _SOKOL_PRIVATE void _sg_gl_init_pixelformats_rgtc(void) { @@ -7036,6 +7571,7 @@ _SOKOL_PRIVATE void _sg_gl_init_pixelformats_bptc(void) { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); } _SOKOL_PRIVATE void _sg_gl_init_pixelformats_pvrtc(void) { @@ -7047,12 +7583,21 @@ _SOKOL_PRIVATE void _sg_gl_init_pixelformats_pvrtc(void) { _SOKOL_PRIVATE void _sg_gl_init_pixelformats_etc2(void) { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); } +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_astc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + } + _SOKOL_PRIVATE void _sg_gl_init_limits(void) { _SG_GL_CHECK_ERROR(); GLint gl_int; @@ -7069,9 +7614,9 @@ _SOKOL_PRIVATE void _sg_gl_init_limits(void) { gl_int = SG_MAX_VERTEX_ATTRIBUTES; } _sg.limits.max_vertex_attrs = gl_int; - glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &gl_int); + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &gl_int); _SG_GL_CHECK_ERROR(); - _sg.limits.gl_max_vertex_uniform_vectors = gl_int; + _sg.limits.gl_max_vertex_uniform_components = gl_int; glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int); _SG_GL_CHECK_ERROR(); _sg.limits.max_image_size_3d = gl_int; @@ -7090,14 +7635,20 @@ _SOKOL_PRIVATE void _sg_gl_init_limits(void) { _sg.limits.gl_max_combined_texture_image_units = gl_int; } -#if defined(SOKOL_GLCORE33) -_SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { - _sg.backend = SG_BACKEND_GLCORE33; +#if defined(SOKOL_GLCORE) +_SOKOL_PRIVATE void _sg_gl_init_caps_glcore(void) { + _sg.backend = SG_BACKEND_GLCORE; + GLint major_version = 0; + GLint minor_version = 0; + glGetIntegerv(GL_MAJOR_VERSION, &major_version); + glGetIntegerv(GL_MINOR_VERSION, &minor_version); + const int version = major_version * 100 + minor_version * 10; _sg.features.origin_top_left = false; _sg.features.image_clamp_to_border = true; _sg.features.mrt_independent_blend_state = false; _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = version >= 430; // scan extensions bool has_s3tc = false; // BC1..BC3 @@ -7105,6 +7656,7 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { bool has_bptc = false; // BC6H and BC7 bool has_pvrtc = false; bool has_etc2 = false; + bool has_astc = false; GLint num_ext = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); for (int i = 0; i < num_ext; i++) { @@ -7122,6 +7674,8 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { has_etc2 = true; } else if (strstr(ext, "_texture_filter_anisotropic")) { _sg.gl.ext_anisotropic = true; + } else if (strstr(ext, "_texture_compression_astc_ldr")) { + has_astc = true; } } } @@ -7153,6 +7707,9 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { if (has_etc2) { _sg_gl_init_pixelformats_etc2(); } + if (has_astc) { + _sg_gl_init_pixelformats_astc(); + } } #endif @@ -7164,6 +7721,7 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { _sg.features.image_clamp_to_border = false; _sg.features.mrt_independent_blend_state = false; _sg.features.mrt_independent_write_mask = false; + _sg.features.storage_buffer = false; bool has_s3tc = false; // BC1..BC3 bool has_rgtc = false; // BC4 and BC5 @@ -7174,6 +7732,7 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { #else bool has_etc2 = true; #endif + bool has_astc = false; bool has_colorbuffer_float = false; bool has_colorbuffer_half_float = false; bool has_texture_float_linear = false; @@ -7197,6 +7756,8 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { has_pvrtc = true; } else if (strstr(ext, "_compressed_texture_etc")) { has_etc2 = true; + } else if (strstr(ext, "_compressed_texture_astc")) { + has_astc = true; } else if (strstr(ext, "_color_buffer_float")) { has_colorbuffer_float = true; } else if (strstr(ext, "_color_buffer_half_float")) { @@ -7243,10 +7804,19 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { if (has_etc2) { _sg_gl_init_pixelformats_etc2(); } + if (has_astc) { + _sg_gl_init_pixelformats_astc(); + } } #endif //-- state cache implementation ------------------------------------------------ +_SOKOL_PRIVATE GLuint _sg_gl_storagebuffer_bind_index(int stage, int slot) { + SOKOL_ASSERT((stage >= 0) && (stage < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((slot >= 0) && (slot < SG_MAX_SHADERSTAGE_STORAGEBUFFERS)); + return (GLuint) (stage * _SG_GL_STORAGEBUFFER_STAGE_INDEX_PITCH + slot); +} + _SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { if (force || (_sg.gl.cache.vertex_buffer != 0)) { glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -7258,30 +7828,77 @@ _SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { _sg.gl.cache.index_buffer = 0; _sg_stats_add(gl.num_bind_buffer, 1); } + if (force || (_sg.gl.cache.storage_buffer != 0)) { + if (_sg.features.storage_buffer) { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + } + _sg.gl.cache.storage_buffer = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + for (int stage = 0; stage < SG_NUM_SHADER_STAGES; stage++) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + if (force || (_sg.gl.cache.stage_storage_buffers[stage][i] != 0)) { + const GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, i); + if (_sg.features.storage_buffer) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, 0); + } + _sg.gl.cache.stage_storage_buffers[stage][i] = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + } + } } _SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { - SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target)); + SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target) || (GL_SHADER_STORAGE_BUFFER == target)); if (target == GL_ARRAY_BUFFER) { if (_sg.gl.cache.vertex_buffer != buffer) { _sg.gl.cache.vertex_buffer = buffer; glBindBuffer(target, buffer); _sg_stats_add(gl.num_bind_buffer, 1); } - } else { + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { if (_sg.gl.cache.index_buffer != buffer) { _sg.gl.cache.index_buffer = buffer; glBindBuffer(target, buffer); _sg_stats_add(gl.num_bind_buffer, 1); } + } else if (target == GL_SHADER_STORAGE_BUFFER) { + if (_sg.gl.cache.storage_buffer != buffer) { + _sg.gl.cache.storage_buffer = buffer; + if (_sg.features.storage_buffer) { + glBindBuffer(target, buffer); + } + _sg_stats_add(gl.num_bind_buffer, 1); + } + } else { + SOKOL_UNREACHABLE; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_storage_buffer(int stage, int slot, GLuint buffer) { + SOKOL_ASSERT((stage >= 0) && (stage < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((slot >= 0) && (slot < SG_MAX_SHADERSTAGE_STORAGEBUFFERS)); + if (_sg.gl.cache.stage_storage_buffers[stage][slot] != buffer) { + _sg.gl.cache.stage_storage_buffers[stage][slot] = buffer; + _sg.gl.cache.storage_buffer = buffer; // not a bug + GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, slot); + if (_sg.features.storage_buffer) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, buffer); + } + _sg_stats_add(gl.num_bind_buffer, 1); } } _SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) { if (target == GL_ARRAY_BUFFER) { _sg.gl.cache.stored_vertex_buffer = _sg.gl.cache.vertex_buffer; - } else { + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer; + } else if (target == GL_SHADER_STORAGE_BUFFER) { + _sg.gl.cache.stored_storage_buffer = _sg.gl.cache.storage_buffer; + } else { + SOKOL_UNREACHABLE; } } @@ -7292,12 +7909,20 @@ _SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); _sg.gl.cache.stored_vertex_buffer = 0; } - } else { + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { if (_sg.gl.cache.stored_index_buffer != 0) { // we only care about restoring valid ids _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_index_buffer); _sg.gl.cache.stored_index_buffer = 0; } + } else if (target == GL_SHADER_STORAGE_BUFFER) { + if (_sg.gl.cache.stored_storage_buffer != 0) { + // we only care about restoring valid ids + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_storage_buffer); + _sg.gl.cache.stored_storage_buffer = 0; + } + } else { + SOKOL_UNREACHABLE; } } @@ -7313,12 +7938,31 @@ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); _sg_stats_add(gl.num_bind_buffer, 1); } + if (buf == _sg.gl.cache.storage_buffer) { + _sg.gl.cache.storage_buffer = 0; + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + for (int stage = 0; stage < SG_NUM_SHADER_STAGES; stage++) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + if (buf == _sg.gl.cache.stage_storage_buffers[stage][i]) { + _sg.gl.cache.stage_storage_buffers[stage][i] = 0; + _sg.gl.cache.storage_buffer = 0; // not a bug! + const GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, i); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + } + } if (buf == _sg.gl.cache.stored_vertex_buffer) { _sg.gl.cache.stored_vertex_buffer = 0; } if (buf == _sg.gl.cache.stored_index_buffer) { _sg.gl.cache.stored_index_buffer = 0; } + if (buf == _sg.gl.cache.stored_storage_buffer) { + _sg.gl.cache.stored_storage_buffer = 0; + } for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { if (buf == _sg.gl.cache.attrs[i].gl_vbuf) { _sg.gl.cache.attrs[i].gl_vbuf = 0; @@ -7456,90 +8100,87 @@ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_pipeline(_sg_pipeline_t* pip) { } _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { - if (_sg.gl.cur_context) { - _SG_GL_CHECK_ERROR(); - glBindVertexArray(_sg.gl.cur_context->vao); - _SG_GL_CHECK_ERROR(); - _sg_clear(&_sg.gl.cache, sizeof(_sg.gl.cache)); - _sg_gl_cache_clear_buffer_bindings(true); - _SG_GL_CHECK_ERROR(); - _sg_gl_cache_clear_texture_sampler_bindings(true); + _SG_GL_CHECK_ERROR(); + glBindVertexArray(_sg.gl.vao); + _SG_GL_CHECK_ERROR(); + _sg_clear(&_sg.gl.cache, sizeof(_sg.gl.cache)); + _sg_gl_cache_clear_buffer_bindings(true); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_clear_texture_sampler_bindings(true); + _SG_GL_CHECK_ERROR(); + for (int i = 0; i < _sg.limits.max_vertex_attrs; i++) { + _sg_gl_attr_t* attr = &_sg.gl.cache.attrs[i].gl_attr; + attr->vb_index = -1; + attr->divisor = -1; + glDisableVertexAttribArray((GLuint)i); _SG_GL_CHECK_ERROR(); - for (int i = 0; i < _sg.limits.max_vertex_attrs; i++) { - _sg_gl_attr_t* attr = &_sg.gl.cache.attrs[i].gl_attr; - attr->vb_index = -1; - attr->divisor = -1; - glDisableVertexAttribArray((GLuint)i); - _SG_GL_CHECK_ERROR(); - _sg_stats_add(gl.num_disable_vertex_attrib_array, 1); - } - _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; + _sg_stats_add(gl.num_disable_vertex_attrib_array, 1); + } + _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; - // shader program - glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog); - _SG_GL_CHECK_ERROR(); + // shader program + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog); + _SG_GL_CHECK_ERROR(); - // depth and stencil state - _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; - _sg.gl.cache.stencil.front.compare = SG_COMPAREFUNC_ALWAYS; - _sg.gl.cache.stencil.front.fail_op = SG_STENCILOP_KEEP; - _sg.gl.cache.stencil.front.depth_fail_op = SG_STENCILOP_KEEP; - _sg.gl.cache.stencil.front.pass_op = SG_STENCILOP_KEEP; - _sg.gl.cache.stencil.back.compare = SG_COMPAREFUNC_ALWAYS; - _sg.gl.cache.stencil.back.fail_op = SG_STENCILOP_KEEP; - _sg.gl.cache.stencil.back.depth_fail_op = SG_STENCILOP_KEEP; - _sg.gl.cache.stencil.back.pass_op = SG_STENCILOP_KEEP; - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - glDepthMask(GL_FALSE); - glDisable(GL_STENCIL_TEST); - glStencilFunc(GL_ALWAYS, 0, 0); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilMask(0); - _sg_stats_add(gl.num_render_state, 7); - - // blend state - _sg.gl.cache.blend.src_factor_rgb = SG_BLENDFACTOR_ONE; - _sg.gl.cache.blend.dst_factor_rgb = SG_BLENDFACTOR_ZERO; - _sg.gl.cache.blend.op_rgb = SG_BLENDOP_ADD; - _sg.gl.cache.blend.src_factor_alpha = SG_BLENDFACTOR_ONE; - _sg.gl.cache.blend.dst_factor_alpha = SG_BLENDFACTOR_ZERO; - _sg.gl.cache.blend.op_alpha = SG_BLENDOP_ADD; - glDisable(GL_BLEND); - glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); - glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); - glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); - _sg_stats_add(gl.num_render_state, 4); - - // standalone state - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; - } - _sg.gl.cache.cull_mode = SG_CULLMODE_NONE; - _sg.gl.cache.face_winding = SG_FACEWINDING_CW; - _sg.gl.cache.sample_count = 1; - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glPolygonOffset(0.0f, 0.0f); - glDisable(GL_POLYGON_OFFSET_FILL); - glDisable(GL_CULL_FACE); - glFrontFace(GL_CW); - glCullFace(GL_BACK); - glEnable(GL_SCISSOR_TEST); - glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); - glEnable(GL_DITHER); - glDisable(GL_POLYGON_OFFSET_FILL); - _sg_stats_add(gl.num_render_state, 10); - #if defined(SOKOL_GLCORE33) - glEnable(GL_MULTISAMPLE); - glEnable(GL_PROGRAM_POINT_SIZE); - _sg_stats_add(gl.num_render_state, 2); - #endif - } + // depth and stencil state + _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.pass_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.back.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.pass_op = SG_STENCILOP_KEEP; + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_FALSE); + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilMask(0); + _sg_stats_add(gl.num_render_state, 7); + + // blend state + _sg.gl.cache.blend.src_factor_rgb = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_rgb = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_rgb = SG_BLENDOP_ADD; + _sg.gl.cache.blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_alpha = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_alpha = SG_BLENDOP_ADD; + glDisable(GL_BLEND); + glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); + _sg_stats_add(gl.num_render_state, 4); + + // standalone state + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; + } + _sg.gl.cache.cull_mode = SG_CULLMODE_NONE; + _sg.gl.cache.face_winding = SG_FACEWINDING_CW; + _sg.gl.cache.sample_count = 1; + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_CULL_FACE); + glFrontFace(GL_CW); + glCullFace(GL_BACK); + glEnable(GL_SCISSOR_TEST); + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + glEnable(GL_DITHER); + glDisable(GL_POLYGON_OFFSET_FILL); + _sg_stats_add(gl.num_render_state, 10); + #if defined(SOKOL_GLCORE) + glEnable(GL_MULTISAMPLE); + glEnable(GL_PROGRAM_POINT_SIZE); + _sg_stats_add(gl.num_render_state, 2); + #endif } _SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { _SOKOL_UNUSED(desc); - SOKOL_ASSERT(desc->context.gl.default_framebuffer_cb == 0 || desc->context.gl.default_framebuffer_userdata_cb == 0); // assumes that _sg.gl is already zero-initialized _sg.gl.valid = true; @@ -7552,56 +8193,36 @@ _SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { #if defined(SOKOL_DEBUG) while (glGetError() != GL_NO_ERROR); #endif - #if defined(SOKOL_GLCORE33) - _sg_gl_init_caps_glcore33(); + #if defined(SOKOL_GLCORE) + _sg_gl_init_caps_glcore(); #elif defined(SOKOL_GLES3) _sg_gl_init_caps_gles3(); #endif -} -_SOKOL_PRIVATE void _sg_gl_discard_backend(void) { - SOKOL_ASSERT(_sg.gl.valid); - _sg.gl.valid = false; - #if defined(_SOKOL_USE_WIN32_GL_LOADER) - _sg_gl_unload_opengl(); - #endif -} - -_SOKOL_PRIVATE void _sg_gl_activate_context(_sg_context_t* ctx) { - SOKOL_ASSERT(_sg.gl.valid); - // NOTE: ctx can be 0 to unset the current context - _sg.gl.cur_context = ctx; - _sg_gl_reset_state_cache(); -} - -//-- GL backend resource creation and destruction ------------------------------ -_SOKOL_PRIVATE sg_resource_state _sg_gl_create_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - SOKOL_ASSERT(0 == ctx->default_framebuffer); - _SG_GL_CHECK_ERROR(); - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&ctx->default_framebuffer); - _SG_GL_CHECK_ERROR(); - SOKOL_ASSERT(0 == ctx->vao); - glGenVertexArrays(1, &ctx->vao); - glBindVertexArray(ctx->vao); + glGenVertexArrays(1, &_sg.gl.vao); + glBindVertexArray(_sg.gl.vao); _SG_GL_CHECK_ERROR(); // incoming texture data is generally expected to be packed tightly glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) // enable seamless cubemap sampling (only desktop GL) glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); #endif - return SG_RESOURCESTATE_VALID; + _sg_gl_reset_state_cache(); } -_SOKOL_PRIVATE void _sg_gl_discard_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - if (ctx->vao) { - glDeleteVertexArrays(1, &ctx->vao); +_SOKOL_PRIVATE void _sg_gl_discard_backend(void) { + SOKOL_ASSERT(_sg.gl.valid); + if (_sg.gl.vao) { + glDeleteVertexArrays(1, &_sg.gl.vao); } - _SG_GL_CHECK_ERROR(); + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + _sg_gl_unload_opengl(); + #endif + _sg.gl.valid = false; } +//-- GL backend resource creation and destruction ------------------------------ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); _SG_GL_CHECK_ERROR(); @@ -7689,40 +8310,58 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ SOKOL_ASSERT(img->gl.tex[slot]); _sg_gl_cache_store_texture_sampler_binding(0); _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[slot], 0); - const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; - int data_index = 0; - for (int face_index = 0; face_index < num_faces; face_index++) { - for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) { - GLenum gl_img_target = img->gl.target; - if (SG_IMAGETYPE_CUBE == img->cmn.type) { - gl_img_target = _sg_gl_cubeface_target(face_index); - } - const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr; - const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); - const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + glTexParameteri(img->gl.target, GL_TEXTURE_MAX_LEVEL, img->cmn.num_mipmaps - 1); + + // NOTE: workaround for https://issues.chromium.org/issues/355605685 + // FIXME: on GLES3 and GL 4.3 (e.g. not macOS) the texture initialization + // should be rewritten to use glTexStorage + glTexSubImage + bool tex_storage_allocated = false; + #if defined(__EMSCRIPTEN__) + if (desc->data.subimage[0][0].ptr == 0) { + tex_storage_allocated = true; if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { - if (is_compressed) { - const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; - glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, - mip_width, mip_height, 0, data_size, data_ptr); - } else { - const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, - mip_width, mip_height, 0, gl_format, gl_type, data_ptr); - } + glTexStorage2D(img->gl.target, img->cmn.num_mipmaps, gl_internal_format, img->cmn.width, img->cmn.height); } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { - int mip_depth = img->cmn.num_slices; - if (SG_IMAGETYPE_3D == img->cmn.type) { - mip_depth = _sg_miplevel_dim(mip_depth, mip_index); + glTexStorage3D(img->gl.target, img->cmn.num_mipmaps, gl_internal_format, img->cmn.width, img->cmn.height, img->cmn.num_slices); + } + } + #endif + if (!tex_storage_allocated) { + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; + int data_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + gl_img_target = _sg_gl_cubeface_target(face_index); } - if (is_compressed) { - const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; - glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, - mip_width, mip_height, mip_depth, 0, data_size, data_ptr); - } else { - const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, - mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); + const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, 0, data_size, data_ptr); + } else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, 0, gl_format, gl_type, data_ptr); + } + } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { + int mip_depth = img->cmn.num_slices; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mip_depth = _sg_miplevel_dim(mip_depth, mip_index); + } + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, mip_depth, 0, data_size, data_ptr); + } else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); + } } } } @@ -7773,7 +8412,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_sampler(_sg_sampler_t* smp, const glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_S, (GLint)_sg_gl_wrap(smp->cmn.wrap_u)); glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(smp->cmn.wrap_v)); glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(smp->cmn.wrap_w)); - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) float border[4]; switch (smp->cmn.border_color) { case SG_BORDERCOLOR_TRANSPARENT_BLACK: @@ -8030,7 +8669,7 @@ _SOKOL_PRIVATE void _sg_gl_discard_pipeline(_sg_pipeline_t* pip) { _sg_gl_cache_invalidate_pipeline(pip); } -_SOKOL_PRIVATE void _sg_gl_fb_attach_texture(const _sg_gl_attachment_t* gl_att, const _sg_pass_attachment_common_t* cmn_att, GLenum gl_att_type) { +_SOKOL_PRIVATE void _sg_gl_fb_attach_texture(const _sg_gl_attachment_t* gl_att, const _sg_attachment_common_t* cmn_att, GLenum gl_att_type) { const _sg_image_t* img = gl_att->image; SOKOL_ASSERT(img); const GLuint gl_tex = img->gl.tex[0]; @@ -8062,35 +8701,35 @@ _SOKOL_PRIVATE GLenum _sg_gl_depth_stencil_attachment_type(const _sg_gl_attachme } } -_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_image, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && desc); +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_image, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); SOKOL_ASSERT(color_images && resolve_images); _SG_GL_CHECK_ERROR(); // copy image pointers - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - const sg_pass_attachment_desc* color_desc = &desc->color_attachments[i]; + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; _SOKOL_UNUSED(color_desc); SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); - SOKOL_ASSERT(0 == pass->gl.color_atts[i].image); + SOKOL_ASSERT(0 == atts->gl.colors[i].image); SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); - pass->gl.color_atts[i].image = color_images[i]; + atts->gl.colors[i].image = color_images[i]; - const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; if (resolve_desc->image.id != SG_INVALID_ID) { - SOKOL_ASSERT(0 == pass->gl.resolve_atts[i].image); + SOKOL_ASSERT(0 == atts->gl.resolves[i].image); SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); - pass->gl.resolve_atts[i].image = resolve_images[i]; + atts->gl.resolves[i].image = resolve_images[i]; } } - SOKOL_ASSERT(0 == pass->gl.ds_att.image); - const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + SOKOL_ASSERT(0 == atts->gl.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; if (ds_desc->image.id != SG_INVALID_ID) { SOKOL_ASSERT(ds_image && (ds_image->slot.id == ds_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_image->cmn.pixel_format)); - pass->gl.ds_att.image = ds_image; + atts->gl.depth_stencil.image = ds_image; } // store current framebuffer binding (restored at end of function) @@ -8098,38 +8737,60 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_ glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); // create a framebuffer object - glGenFramebuffers(1, &pass->gl.fb); - glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); + glGenFramebuffers(1, &atts->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.fb); // attach color attachments to framebuffer - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - const _sg_image_t* color_img = pass->gl.color_atts[i].image; + for (int i = 0; i < atts->cmn.num_colors; i++) { + const _sg_image_t* color_img = atts->gl.colors[i].image; SOKOL_ASSERT(color_img); const GLuint gl_msaa_render_buffer = color_img->gl.msaa_render_buffer; if (gl_msaa_render_buffer) { glFramebufferRenderbuffer(GL_FRAMEBUFFER, (GLenum)(GL_COLOR_ATTACHMENT0+i), GL_RENDERBUFFER, gl_msaa_render_buffer); } else { const GLenum gl_att_type = (GLenum)(GL_COLOR_ATTACHMENT0 + i); - _sg_gl_fb_attach_texture(&pass->gl.color_atts[i], &pass->cmn.color_atts[i], gl_att_type); + _sg_gl_fb_attach_texture(&atts->gl.colors[i], &atts->cmn.colors[i], gl_att_type); } } - // attach depth-stencil attachement - if (pass->gl.ds_att.image) { - const GLenum gl_att = _sg_gl_depth_stencil_attachment_type(&pass->gl.ds_att); - const _sg_image_t* ds_img = pass->gl.ds_att.image; + // attach depth-stencil attachment + if (atts->gl.depth_stencil.image) { + const GLenum gl_att = _sg_gl_depth_stencil_attachment_type(&atts->gl.depth_stencil); + const _sg_image_t* ds_img = atts->gl.depth_stencil.image; const GLuint gl_msaa_render_buffer = ds_img->gl.msaa_render_buffer; if (gl_msaa_render_buffer) { glFramebufferRenderbuffer(GL_FRAMEBUFFER, gl_att, GL_RENDERBUFFER, gl_msaa_render_buffer); } else { - const GLenum gl_att_type = _sg_gl_depth_stencil_attachment_type(&pass->gl.ds_att); - _sg_gl_fb_attach_texture(&pass->gl.ds_att, &pass->cmn.ds_att, gl_att_type); + const GLenum gl_att_type = _sg_gl_depth_stencil_attachment_type(&atts->gl.depth_stencil); + _sg_gl_fb_attach_texture(&atts->gl.depth_stencil, &atts->cmn.depth_stencil, gl_att_type); } } // check if framebuffer is complete - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - _SG_ERROR(GL_FRAMEBUFFER_INCOMPLETE); - return SG_RESOURCESTATE_FAILED; + { + const GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + switch (fb_status) { + case GL_FRAMEBUFFER_UNDEFINED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNDEFINED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNSUPPORTED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE); + break; + default: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNKNOWN); + break; + } + return SG_RESOURCESTATE_FAILED; + } } // setup color attachments for the framebuffer @@ -8139,20 +8800,40 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_ GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 }; - glDrawBuffers(pass->cmn.num_color_atts, gl_draw_bufs); + glDrawBuffers(atts->cmn.num_colors, gl_draw_bufs); // create MSAA resolve framebuffers if necessary - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - _sg_gl_attachment_t* gl_resolve_att = &pass->gl.resolve_atts[i]; + for (int i = 0; i < atts->cmn.num_colors; i++) { + _sg_gl_attachment_t* gl_resolve_att = &atts->gl.resolves[i]; if (gl_resolve_att->image) { - _sg_pass_attachment_t* cmn_resolve_att = &pass->cmn.resolve_atts[i]; - SOKOL_ASSERT(0 == pass->gl.msaa_resolve_framebuffer[i]); - glGenFramebuffers(1, &pass->gl.msaa_resolve_framebuffer[i]); - glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.msaa_resolve_framebuffer[i]); + _sg_attachment_common_t* cmn_resolve_att = &atts->cmn.resolves[i]; + SOKOL_ASSERT(0 == atts->gl.msaa_resolve_framebuffer[i]); + glGenFramebuffers(1, &atts->gl.msaa_resolve_framebuffer[i]); + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.msaa_resolve_framebuffer[i]); _sg_gl_fb_attach_texture(gl_resolve_att, cmn_resolve_att, GL_COLOR_ATTACHMENT0); // check if framebuffer is complete - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - _SG_ERROR(GL_MSAA_FRAMEBUFFER_INCOMPLETE); + const GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + switch (fb_status) { + case GL_FRAMEBUFFER_UNDEFINED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNDEFINED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNSUPPORTED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE); + break; + default: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNKNOWN); + break; + } return SG_RESOURCESTATE_FAILED; } // setup color attachments for the framebuffer @@ -8166,51 +8847,43 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_ return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_gl_discard_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - SOKOL_ASSERT(pass != _sg.gl.cur_pass); +_SOKOL_PRIVATE void _sg_gl_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); _SG_GL_CHECK_ERROR(); - if (0 != pass->gl.fb) { - glDeleteFramebuffers(1, &pass->gl.fb); + if (0 != atts->gl.fb) { + glDeleteFramebuffers(1, &atts->gl.fb); } for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->gl.msaa_resolve_framebuffer[i]) { - glDeleteFramebuffers(1, &pass->gl.msaa_resolve_framebuffer[i]); + if (atts->gl.msaa_resolve_framebuffer[i]) { + glDeleteFramebuffers(1, &atts->gl.msaa_resolve_framebuffer[i]); } } _SG_GL_CHECK_ERROR(); } -_SOKOL_PRIVATE _sg_image_t* _sg_gl_pass_color_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - return pass->gl.color_atts[index].image; +_SOKOL_PRIVATE _sg_image_t* _sg_gl_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->gl.colors[index].image; } -_SOKOL_PRIVATE _sg_image_t* _sg_gl_pass_resolve_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - return pass->gl.resolve_atts[index].image; +_SOKOL_PRIVATE _sg_image_t* _sg_gl_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->gl.resolves[index].image; } -_SOKOL_PRIVATE _sg_image_t* _sg_gl_pass_ds_image(const _sg_pass_t* pass) { - SOKOL_ASSERT(pass); - return pass->gl.ds_att.image; +_SOKOL_PRIVATE _sg_image_t* _sg_gl_attachments_ds_image(const _sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + return atts->gl.depth_stencil.image; } -_SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { +_SOKOL_PRIVATE void _sg_gl_begin_pass(const sg_pass* pass) { // FIXME: what if a texture used as render target is still bound, should we // unbind all currently bound textures in begin pass? - SOKOL_ASSERT(action); - SOKOL_ASSERT(!_sg.gl.in_pass); + SOKOL_ASSERT(pass); _SG_GL_CHECK_ERROR(); - _sg.gl.in_pass = true; - _sg.gl.cur_pass = pass; // can be 0 - if (pass) { - _sg.gl.cur_pass_id.id = pass->slot.id; - } else { - _sg.gl.cur_pass_id.id = SG_INVALID_ID; - } - _sg.gl.cur_pass_width = w; - _sg.gl.cur_pass_height = h; + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; // bind the render pass framebuffer // @@ -8220,32 +8893,28 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac // // This will need a cleaner solution (e.g. allowing to configure // sokol_app.h with an sRGB or RGB framebuffer. - if (pass) { + if (atts) { // offscreen pass - SOKOL_ASSERT(pass->gl.fb); - #if defined(SOKOL_GLCORE33) + SOKOL_ASSERT(atts->gl.fb); + #if defined(SOKOL_GLCORE) glEnable(GL_FRAMEBUFFER_SRGB); #endif - glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.fb); } else { // default pass - SOKOL_ASSERT(_sg.gl.cur_context); - #if defined(SOKOL_GLCORE33) + #if defined(SOKOL_GLCORE) glDisable(GL_FRAMEBUFFER_SRGB); #endif - if (_sg.desc.context.gl.default_framebuffer_userdata_cb) { - _sg.gl.cur_context->default_framebuffer = _sg.desc.context.gl.default_framebuffer_userdata_cb(_sg.desc.context.gl.user_data); - } else if (_sg.desc.context.gl.default_framebuffer_cb) { - _sg.gl.cur_context->default_framebuffer = _sg.desc.context.gl.default_framebuffer_cb(); - } - - glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); + // NOTE: on some platforms, the default framebuffer of a context + // is null, so we can't actually assert here that the + // framebuffer has been provided + glBindFramebuffer(GL_FRAMEBUFFER, swapchain->gl.framebuffer); } - glViewport(0, 0, w, h); - glScissor(0, 0, w, h); + glViewport(0, 0, _sg.cur_pass.width, _sg.cur_pass.height); + glScissor(0, 0, _sg.cur_pass.width, _sg.cur_pass.height); // number of color attachments - const int num_color_atts = pass ? pass->cmn.num_color_atts : 1; + const int num_color_atts = atts ? atts->cmn.num_colors : 1; // clear color and depth-stencil attachments if needed bool clear_any_color = false; @@ -8303,7 +8972,7 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac glClearBufferfv(GL_COLOR, i, &action->colors[i].clear_value.r); } } - if ((pass == 0) || (pass->gl.ds_att.image)) { + if ((atts == 0) || (atts->gl.depth_stencil.image)) { if (clear_depth && clear_stencil) { glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.clear_value, action->stencil.clear_value); } else if (clear_depth) { @@ -8324,26 +8993,25 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac } _SOKOL_PRIVATE void _sg_gl_end_pass(void) { - SOKOL_ASSERT(_sg.gl.in_pass); _SG_GL_CHECK_ERROR(); - if (_sg.gl.cur_pass) { - const _sg_pass_t* pass = _sg.gl.cur_pass; - SOKOL_ASSERT(pass->slot.id == _sg.gl.cur_pass_id.id); + if (_sg.cur_pass.atts) { + const _sg_attachments_t* atts = _sg.cur_pass.atts; + SOKOL_ASSERT(atts->slot.id == _sg.cur_pass.atts_id.id); bool fb_read_bound = false; bool fb_draw_bound = false; - const int num_atts = pass->cmn.num_color_atts; - for (int i = 0; i < num_atts; i++) { + const int num_color_atts = atts->cmn.num_colors; + for (int i = 0; i < num_color_atts; i++) { // perform MSAA resolve if needed - if (pass->gl.msaa_resolve_framebuffer[i] != 0) { + if (atts->gl.msaa_resolve_framebuffer[i] != 0) { if (!fb_read_bound) { - SOKOL_ASSERT(pass->gl.fb); - glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->gl.fb); + SOKOL_ASSERT(atts->gl.fb); + glBindFramebuffer(GL_READ_FRAMEBUFFER, atts->gl.fb); fb_read_bound = true; } - const int w = pass->gl.color_atts[i].image->cmn.width; - const int h = pass->gl.color_atts[i].image->cmn.height; - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pass->gl.msaa_resolve_framebuffer[i]); + const int w = atts->gl.colors[i].image->cmn.width; + const int h = atts->gl.colors[i].image->cmn.height; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, atts->gl.msaa_resolve_framebuffer[i]); glReadBuffer((GLenum)(GL_COLOR_ATTACHMENT0 + i)); glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); fb_draw_bound = true; @@ -8355,19 +9023,19 @@ _SOKOL_PRIVATE void _sg_gl_end_pass(void) { #if defined(SOKOL_GLES3) // need to restore framebuffer binding before invalidate if the MSAA resolve had changed the binding if (fb_draw_bound) { - glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.fb); } GLenum invalidate_atts[SG_MAX_COLOR_ATTACHMENTS + 2] = { 0 }; int att_index = 0; - for (int i = 0; i < num_atts; i++) { + for (int i = 0; i < num_color_atts; i++) { if (_sg.gl.color_store_actions[i] == SG_STOREACTION_DONTCARE) { invalidate_atts[att_index++] = (GLenum)(GL_COLOR_ATTACHMENT0 + i); } } - if (_sg.gl.depth_store_action == SG_STOREACTION_DONTCARE) { + if ((_sg.gl.depth_store_action == SG_STOREACTION_DONTCARE) && (_sg.cur_pass.atts->cmn.depth_stencil.image_id.id != SG_INVALID_ID)) { invalidate_atts[att_index++] = GL_DEPTH_ATTACHMENT; } - if (_sg.gl.stencil_store_action == SG_STOREACTION_DONTCARE) { + if ((_sg.gl.stencil_store_action == SG_STOREACTION_DONTCARE) && (_sg.cur_pass.atts->cmn.depth_stencil.image_id.id != SG_INVALID_ID)) { invalidate_atts[att_index++] = GL_STENCIL_ATTACHMENT; } if (att_index > 0) { @@ -8375,27 +9043,16 @@ _SOKOL_PRIVATE void _sg_gl_end_pass(void) { } #endif } - - _sg.gl.cur_pass = 0; - _sg.gl.cur_pass_id.id = SG_INVALID_ID; - _sg.gl.cur_pass_width = 0; - _sg.gl.cur_pass_height = 0; - - SOKOL_ASSERT(_sg.gl.cur_context); - glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); - _sg.gl.in_pass = false; _SG_GL_CHECK_ERROR(); } _SOKOL_PRIVATE void _sg_gl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.gl.in_pass); - y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; + y = origin_top_left ? (_sg.cur_pass.height - (y+h)) : y; glViewport(x, y, w, h); } _SOKOL_PRIVATE void _sg_gl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.gl.in_pass); - y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; + y = origin_top_left ? (_sg.cur_pass.height - (y+h)) : y; glScissor(x, y, w, h); } @@ -8545,7 +9202,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { if (pip->gl.color_write_mask[i] != _sg.gl.cache.color_write_mask[i]) { const sg_color_mask cm = pip->gl.color_write_mask[i]; _sg.gl.cache.color_write_mask[i] = cm; - #ifdef SOKOL_GLCORE33 + #ifdef SOKOL_GLCORE glColorMaski(i, (cm & SG_COLORMASK_R) != 0, (cm & SG_COLORMASK_G) != 0, @@ -8602,7 +9259,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { } _sg_stats_add(gl.num_render_state, 1); } - #ifdef SOKOL_GLCORE33 + #ifdef SOKOL_GLCORE if (pip->gl.sample_count != _sg.gl.cache.sample_count) { _sg.gl.cache.sample_count = pip->gl.sample_count; if (pip->gl.sample_count > 1) { @@ -8658,6 +9315,18 @@ _SOKOL_PRIVATE bool _sg_gl_apply_bindings(_sg_bindings_t* bnd) { } _SG_GL_CHECK_ERROR(); + // bind storage buffers + for (int slot = 0; slot < bnd->num_vs_sbufs; slot++) { + _sg_buffer_t* sb = bnd->vs_sbufs[slot]; + GLuint gl_sb = sb->gl.buf[sb->cmn.active_slot]; + _sg_gl_cache_bind_storage_buffer(SG_SHADERSTAGE_VS, slot, gl_sb); + } + for (int slot = 0; slot < bnd->num_fs_sbufs; slot++) { + _sg_buffer_t* sb = bnd->fs_sbufs[slot]; + GLuint gl_sb = sb->gl.buf[sb->cmn.active_slot]; + _sg_gl_cache_bind_storage_buffer(SG_SHADERSTAGE_FS, slot, gl_sb); + } + // index buffer (can be 0) const GLuint gl_ib = bnd->ib ? bnd->ib->gl.buf[bnd->ib->cmn.active_slot] : 0; _sg_gl_cache_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); @@ -8773,19 +9442,20 @@ _SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_inst SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); const GLenum i_type = _sg.gl.cache.cur_index_type; const GLenum p_type = _sg.gl.cache.cur_primitive_type; + const bool use_instanced_draw = (num_instances > 1) || (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw); if (0 != i_type) { // indexed rendering const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; const int ib_offset = _sg.gl.cache.cur_ib_offset; const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); - if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { + if (use_instanced_draw) { glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); } else { glDrawElements(p_type, num_elements, i_type, indices); } } else { // non-indexed rendering - if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { + if (use_instanced_draw) { glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); } else { glDrawArrays(p_type, base_element, num_elements); @@ -8794,7 +9464,6 @@ _SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_inst } _SOKOL_PRIVATE void _sg_gl_commit(void) { - SOKOL_ASSERT(!_sg.gl.in_pass); // "soft" clear bindings (only those that are actually bound) _sg_gl_cache_clear_buffer_bindings(false); _sg_gl_cache_clear_texture_sampler_bindings(false); @@ -8892,6 +9561,10 @@ _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* d // >>d3d11 backend #elif defined(SOKOL_D3D11) +#define _SG_D3D11_MAX_SHADERSTAGE_SRVS (32) +#define _SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET (0) +#define _SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET (16) + #if defined(__cplusplus) #define _sg_d3d11_AddRef(self) (self)->AddRef() #else @@ -8904,6 +9577,29 @@ _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* d #define _sg_d3d11_Release(self) (self)->lpVtbl->Release(self) #endif +// NOTE: This needs to be a macro since we can't use the polymorphism in C. It's called on many kinds of resources. +// NOTE: Based on microsoft docs, it's fine to call this with pData=NULL if DataSize is also zero. +#if defined(__cplusplus) +#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->SetPrivateData(guid, DataSize, pData) +#else +#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->lpVtbl->SetPrivateData(self, guid, DataSize, pData) +#endif + +#if defined(__cplusplus) +#define _sg_win32_refguid(guid) guid +#else +#define _sg_win32_refguid(guid) &guid +#endif + +static const GUID _sg_d3d11_WKPDID_D3DDebugObjectName = { 0x429b8c22,0x9188,0x4b0c, {0x87,0x42,0xac,0xb0,0xbf,0x85,0xc2,0x00} }; + +#if defined(SOKOL_DEBUG) +#define _sg_d3d11_setlabel(self, label) _sg_d3d11_SetPrivateData(self, _sg_win32_refguid(_sg_d3d11_WKPDID_D3DDebugObjectName), label ? (UINT)strlen(label) : 0, label) +#else +#define _sg_d3d11_setlabel(self, label) +#endif + + //-- D3D11 C/C++ wrappers ------------------------------------------------------ static inline HRESULT _sg_d3d11_CheckFormatSupport(ID3D11Device* self, DXGI_FORMAT Format, UINT* pFormatSupport) { #if defined(__cplusplus) @@ -9287,6 +9983,34 @@ _SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_usage(sg_usage usg) { } } +_SOKOL_PRIVATE UINT _sg_d3d11_buffer_bind_flags(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: + return D3D11_BIND_VERTEX_BUFFER; + case SG_BUFFERTYPE_INDEXBUFFER: + return D3D11_BIND_INDEX_BUFFER; + case SG_BUFFERTYPE_STORAGEBUFFER: + // FIXME: for compute shaders we'd want UNORDERED_ACCESS? + return D3D11_BIND_SHADER_RESOURCE; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_buffer_misc_flags(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: + case SG_BUFFERTYPE_INDEXBUFFER: + return 0; + case SG_BUFFERTYPE_STORAGEBUFFER: + return D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + _SOKOL_PRIVATE UINT _sg_d3d11_cpu_access_flags(sg_usage usg) { switch (usg) { case SG_USAGE_IMMUTABLE: @@ -9344,10 +10068,11 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_RGBA32SI: return DXGI_FORMAT_R32G32B32A32_SINT; case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_R32_TYPELESS; - case SG_PIXELFORMAT_DEPTH_STENCIL: return DXGI_FORMAT_D24_UNORM_S8_UINT; + case SG_PIXELFORMAT_DEPTH_STENCIL: return DXGI_FORMAT_R24G8_TYPELESS; case SG_PIXELFORMAT_BC1_RGBA: return DXGI_FORMAT_BC1_UNORM; case SG_PIXELFORMAT_BC2_RGBA: return DXGI_FORMAT_BC2_UNORM; case SG_PIXELFORMAT_BC3_RGBA: return DXGI_FORMAT_BC3_UNORM; + case SG_PIXELFORMAT_BC3_SRGBA: return DXGI_FORMAT_BC3_UNORM_SRGB; case SG_PIXELFORMAT_BC4_R: return DXGI_FORMAT_BC4_UNORM; case SG_PIXELFORMAT_BC4_RSN: return DXGI_FORMAT_BC4_SNORM; case SG_PIXELFORMAT_BC5_RG: return DXGI_FORMAT_BC5_UNORM; @@ -9355,6 +10080,7 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_BC6H_RGBF: return DXGI_FORMAT_BC6H_SF16; case SG_PIXELFORMAT_BC6H_RGBUF: return DXGI_FORMAT_BC6H_UF16; case SG_PIXELFORMAT_BC7_RGBA: return DXGI_FORMAT_BC7_UNORM; + case SG_PIXELFORMAT_BC7_SRGBA: return DXGI_FORMAT_BC7_UNORM_SRGB; default: return DXGI_FORMAT_UNKNOWN; }; } @@ -9362,6 +10088,8 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) { _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_srv_pixel_format(sg_pixel_format fmt) { if (fmt == SG_PIXELFORMAT_DEPTH) { return DXGI_FORMAT_R32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; } else { return _sg_d3d11_texture_pixel_format(fmt); } @@ -9370,6 +10098,8 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_srv_pixel_format(sg_pixel_format fmt) { _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_dsv_pixel_format(sg_pixel_format fmt) { if (fmt == SG_PIXELFORMAT_DEPTH) { return DXGI_FORMAT_D32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_D24_UNORM_S8_UINT; } else { return _sg_d3d11_texture_pixel_format(fmt); } @@ -9378,6 +10108,8 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_dsv_pixel_format(sg_pixel_format fmt) { _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_rtv_pixel_format(sg_pixel_format fmt) { if (fmt == SG_PIXELFORMAT_DEPTH) { return DXGI_FORMAT_R32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; } else { return _sg_d3d11_texture_pixel_format(fmt); } @@ -9587,6 +10319,7 @@ _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { _sg.features.image_clamp_to_border = true; _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; _sg.limits.max_image_size_2d = 16 * 1024; _sg.limits.max_image_size_cube = 16 * 1024; @@ -9600,31 +10333,26 @@ _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { const UINT srv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_srv_pixel_format((sg_pixel_format)fmt)); const UINT rtv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_rtv_pixel_format((sg_pixel_format)fmt)); const UINT dsv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_dsv_pixel_format((sg_pixel_format)fmt)); - sg_pixelformat_info* info = &_sg.formats[fmt]; + _sg_pixelformat_info_t* info = &_sg.formats[fmt]; + const bool render = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); + const bool depth = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); info->sample = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D); info->filter = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE); - info->render = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); + info->render = render || depth; info->blend = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); info->msaa = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); - info->depth = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); + info->depth = depth; } } _SOKOL_PRIVATE void _sg_d3d11_setup_backend(const sg_desc* desc) { // assume _sg.d3d11 already is zero-initialized SOKOL_ASSERT(desc); - SOKOL_ASSERT(desc->context.d3d11.device); - SOKOL_ASSERT(desc->context.d3d11.device_context); - SOKOL_ASSERT(desc->context.d3d11.render_target_view_cb || desc->context.d3d11.render_target_view_userdata_cb); - SOKOL_ASSERT(desc->context.d3d11.depth_stencil_view_cb || desc->context.d3d11.depth_stencil_view_userdata_cb); + SOKOL_ASSERT(desc->environment.d3d11.device); + SOKOL_ASSERT(desc->environment.d3d11.device_context); _sg.d3d11.valid = true; - _sg.d3d11.dev = (ID3D11Device*) desc->context.d3d11.device; - _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->context.d3d11.device_context; - _sg.d3d11.rtv_cb = desc->context.d3d11.render_target_view_cb; - _sg.d3d11.rtv_userdata_cb = desc->context.d3d11.render_target_view_userdata_cb; - _sg.d3d11.dsv_cb = desc->context.d3d11.depth_stencil_view_cb; - _sg.d3d11.dsv_userdata_cb = desc->context.d3d11.depth_stencil_view_userdata_cb; - _sg.d3d11.user_data = desc->context.d3d11.user_data; + _sg.d3d11.dev = (ID3D11Device*) desc->environment.d3d11.device; + _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->environment.d3d11.device_context; _sg_d3d11_init_caps(); } @@ -9643,23 +10371,6 @@ _SOKOL_PRIVATE void _sg_d3d11_reset_state_cache(void) { _sg_d3d11_clear_state(); } -_SOKOL_PRIVATE void _sg_d3d11_activate_context(_sg_context_t* ctx) { - _SOKOL_UNUSED(ctx); - _sg_d3d11_clear_state(); -} - -_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_d3d11_discard_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - // empty -} - _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); SOKOL_ASSERT(!buf->d3d11.buf); @@ -9667,13 +10378,15 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, cons if (injected) { buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer; _sg_d3d11_AddRef(buf->d3d11.buf); + // FIXME: for storage buffers also need to inject resource view } else { - D3D11_BUFFER_DESC d3d11_desc; - _sg_clear(&d3d11_desc, sizeof(d3d11_desc)); - d3d11_desc.ByteWidth = (UINT)buf->cmn.size; - d3d11_desc.Usage = _sg_d3d11_usage(buf->cmn.usage); - d3d11_desc.BindFlags = buf->cmn.type == SG_BUFFERTYPE_VERTEXBUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER; - d3d11_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->cmn.usage); + D3D11_BUFFER_DESC d3d11_buf_desc; + _sg_clear(&d3d11_buf_desc, sizeof(d3d11_buf_desc)); + d3d11_buf_desc.ByteWidth = (UINT)buf->cmn.size; + d3d11_buf_desc.Usage = _sg_d3d11_usage(buf->cmn.usage); + d3d11_buf_desc.BindFlags = _sg_d3d11_buffer_bind_flags(buf->cmn.type); + d3d11_buf_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->cmn.usage); + d3d11_buf_desc.MiscFlags = _sg_d3d11_buffer_misc_flags(buf->cmn.type); D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; D3D11_SUBRESOURCE_DATA init_data; _sg_clear(&init_data, sizeof(init_data)); @@ -9682,11 +10395,31 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, cons init_data.pSysMem = desc->data.ptr; init_data_ptr = &init_data; } - HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11.buf); + HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_buf_desc, init_data_ptr, &buf->d3d11.buf); if (!(SUCCEEDED(hr) && buf->d3d11.buf)) { _SG_ERROR(D3D11_CREATE_BUFFER_FAILED); return SG_RESOURCESTATE_FAILED; } + + // for storage buffers need to create a view object + if (buf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER) { + // FIXME: currently only shader-resource-view, in future also UAV + // storage buffer size must be multiple of 4 + SOKOL_ASSERT(_sg_multiple_u64(buf->cmn.size, 4)); + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = DXGI_FORMAT_R32_TYPELESS; + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX; + d3d11_srv_desc.BufferEx.FirstElement = 0; + d3d11_srv_desc.BufferEx.NumElements = buf->cmn.size / 4; + d3d11_srv_desc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW; + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)buf->d3d11.buf, &d3d11_srv_desc, &buf->d3d11.srv); + if (!(SUCCEEDED(hr) && buf->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_BUFFER_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + _sg_d3d11_setlabel(buf->d3d11.buf, desc->label); } return SG_RESOURCESTATE_VALID; } @@ -9696,6 +10429,9 @@ _SOKOL_PRIVATE void _sg_d3d11_discard_buffer(_sg_buffer_t* buf) { if (buf->d3d11.buf) { _sg_d3d11_Release(buf->d3d11.buf); } + if (buf->d3d11.srv) { + _sg_d3d11_Release(buf->d3d11.srv); + } } _SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_data* data) { @@ -9731,7 +10467,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const SOKOL_ASSERT((0 == img->d3d11.tex2d) && (0 == img->d3d11.tex3d) && (0 == img->d3d11.res) && (0 == img->d3d11.srv)); HRESULT hr; - const bool injected = (0 != desc->d3d11_texture) || (0 != desc->d3d11_shader_resource_view); + const bool injected = (0 != desc->d3d11_texture); const bool msaa = (img->cmn.sample_count > 1); img->d3d11.format = _sg_d3d11_texture_pixel_format(img->cmn.pixel_format); if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { @@ -9750,23 +10486,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const // first check for injected texture and/or resource view if (injected) { img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; + _sg_d3d11_AddRef(img->d3d11.tex2d); img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; - if (img->d3d11.tex2d) { - _sg_d3d11_AddRef(img->d3d11.tex2d); - } else { - // if only a shader-resource-view was provided, but no texture, lookup - // the texture from the shader-resource-view, this also bumps the refcount - SOKOL_ASSERT(img->d3d11.srv); - _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex2d); - SOKOL_ASSERT(img->d3d11.tex2d); - } if (img->d3d11.srv) { _sg_d3d11_AddRef(img->d3d11.srv); } - } - - if (0 == img->d3d11.tex2d) { - // if not injected, create texture + } else { + // if not injected, create 2D texture D3D11_TEXTURE2D_DESC d3d11_tex_desc; _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = (UINT)img->cmn.width; @@ -9803,58 +10529,53 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const _SG_ERROR(D3D11_CREATE_2D_TEXTURE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(img->d3d11.tex2d, desc->label); + + // create shader-resource-view for 2D texture + // FIXME: currently we don't support setting MSAA texture as shader resource + if (!msaa) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_CUBE: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_ARRAY: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; + break; + default: + SOKOL_UNREACHABLE; break; + } + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.srv, desc->label); + } } SOKOL_ASSERT(img->d3d11.tex2d); img->d3d11.res = (ID3D11Resource*)img->d3d11.tex2d; _sg_d3d11_AddRef(img->d3d11.res); - - // ...and similar, if not injected, create shader-resource-view - // FIXME: currently we don't support setting MSAA texture as shader resource - if ((0 == img->d3d11.srv) && !msaa) { - D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); - d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); - switch (img->cmn.type) { - case SG_IMAGETYPE_2D: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; - break; - case SG_IMAGETYPE_CUBE: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; - break; - case SG_IMAGETYPE_ARRAY: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; - d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; - break; - default: - SOKOL_UNREACHABLE; break; - } - hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, img->d3d11.res, &d3d11_srv_desc, &img->d3d11.srv); - if (!(SUCCEEDED(hr) && img->d3d11.srv)) { - _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); - return SG_RESOURCESTATE_FAILED; - } - } } else { // 3D texture - same procedure, first check if injected, than create non-injected if (injected) { img->d3d11.tex3d = (ID3D11Texture3D*) desc->d3d11_texture; + _sg_d3d11_AddRef(img->d3d11.tex3d); img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; - if (img->d3d11.tex3d) { - _sg_d3d11_AddRef(img->d3d11.tex3d); - } else { - SOKOL_ASSERT(img->d3d11.srv); - _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex3d); - SOKOL_ASSERT(img->d3d11.tex3d); - } if (img->d3d11.srv) { _sg_d3d11_AddRef(img->d3d11.srv); } - } - - if (0 == img->d3d11.tex3d) { + } else { + // not injected, create 3d texture D3D11_TEXTURE3D_DESC d3d11_tex_desc; _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = (UINT)img->cmn.width; @@ -9880,23 +10601,26 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const _SG_ERROR(D3D11_CREATE_3D_TEXTURE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(img->d3d11.tex3d, desc->label); + + // create shader-resource-view for 3D texture + if (!msaa) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex3d, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_3D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.srv, desc->label); + } } SOKOL_ASSERT(img->d3d11.tex3d); img->d3d11.res = (ID3D11Resource*)img->d3d11.tex3d; _sg_d3d11_AddRef(img->d3d11.res); - - if ((0 == img->d3d11.srv) && !msaa) { - D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); - d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; - hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, img->d3d11.res, &d3d11_srv_desc, &img->d3d11.srv); - if (!(SUCCEEDED(hr) && img->d3d11.srv)) { - _SG_ERROR(D3D11_CREATE_3D_SRV_FAILED); - return SG_RESOURCESTATE_FAILED; - } - } } return SG_RESOURCESTATE_VALID; } @@ -9955,6 +10679,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_sampler(_sg_sampler_t* smp, co _SG_ERROR(D3D11_CREATE_SAMPLER_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(smp->d3d11.smp, desc->label); } return SG_RESOURCESTATE_VALID; } @@ -9987,6 +10712,12 @@ _SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* st return NULL; } SOKOL_ASSERT(stage_desc->d3d11_target); + UINT flags1 = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR; + if (_sg.desc.d3d11_shader_debugging) { + flags1 |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; + } else { + flags1 |= D3DCOMPILE_OPTIMIZATION_LEVEL3; + } ID3DBlob* output = NULL; ID3DBlob* errors_or_warnings = NULL; HRESULT hr = _sg.d3d11.D3DCompile_func( @@ -9997,7 +10728,7 @@ _SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* st NULL, // pInclude stage_desc->entry ? stage_desc->entry : "main", // pEntryPoint stage_desc->d3d11_target, // pTarget - D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, // Flags1 + flags1, // Flags1 0, // Flags2 &output, // ppCode &errors_or_warnings); // ppErrorMsgs @@ -10049,6 +10780,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons _SG_ERROR(D3D11_CREATE_CONSTANT_BUFFER_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(d3d11_stage->cbufs[ub_index], desc->label); } } @@ -10087,6 +10819,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons SOKOL_ASSERT(shd->d3d11.vs_blob); memcpy(shd->d3d11.vs_blob, vs_ptr, vs_length); result = SG_RESOURCESTATE_VALID; + _sg_d3d11_setlabel(shd->d3d11.vs, desc->label); + _sg_d3d11_setlabel(shd->d3d11.fs, desc->label); } } if (vs_blob) { @@ -10168,15 +10902,18 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, pip->d3d11.vb_strides[layout_index] = 0; } } - hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev, - d3d11_comps, // pInputElementDesc - (UINT)attr_index, // NumElements - shd->d3d11.vs_blob, // pShaderByteCodeWithInputSignature - shd->d3d11.vs_blob_length, // BytecodeLength - &pip->d3d11.il); - if (!(SUCCEEDED(hr) && pip->d3d11.il)) { - _SG_ERROR(D3D11_CREATE_INPUT_LAYOUT_FAILED); - return SG_RESOURCESTATE_FAILED; + if (attr_index > 0) { + hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev, + d3d11_comps, // pInputElementDesc + (UINT)attr_index, // NumElements + shd->d3d11.vs_blob, // pShaderByteCodeWithInputSignature + shd->d3d11.vs_blob_length, // BytecodeLength + &pip->d3d11.il); + if (!(SUCCEEDED(hr) && pip->d3d11.il)) { + _SG_ERROR(D3D11_CREATE_INPUT_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.il, desc->label); } // create rasterizer state @@ -10197,6 +10934,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _SG_ERROR(D3D11_CREATE_RASTERIZER_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(pip->d3d11.rs, desc->label); // create depth-stencil state D3D11_DEPTH_STENCIL_DESC dss_desc; @@ -10222,6 +10960,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _SG_ERROR(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(pip->d3d11.dss, desc->label); // create blend state D3D11_BLEND_DESC bs_desc; @@ -10256,6 +10995,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _SG_ERROR(D3D11_CREATE_BLEND_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(pip->d3d11.bs, desc->label); return SG_RESOURCESTATE_VALID; } @@ -10279,42 +11019,42 @@ _SOKOL_PRIVATE void _sg_d3d11_discard_pipeline(_sg_pipeline_t* pip) { } } -_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && desc); +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); SOKOL_ASSERT(color_images && resolve_images); SOKOL_ASSERT(_sg.d3d11.dev); // copy image pointers - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - const sg_pass_attachment_desc* color_desc = &desc->color_attachments[i]; + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; _SOKOL_UNUSED(color_desc); SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); - SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].image); + SOKOL_ASSERT(0 == atts->d3d11.colors[i].image); SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); - pass->d3d11.color_atts[i].image = color_images[i]; + atts->d3d11.colors[i].image = color_images[i]; - const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; if (resolve_desc->image.id != SG_INVALID_ID) { - SOKOL_ASSERT(0 == pass->d3d11.resolve_atts[i].image); + SOKOL_ASSERT(0 == atts->d3d11.resolves[i].image); SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); - pass->d3d11.resolve_atts[i].image = resolve_images[i]; + atts->d3d11.resolves[i].image = resolve_images[i]; } } - SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); - const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + SOKOL_ASSERT(0 == atts->d3d11.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; if (ds_desc->image.id != SG_INVALID_ID) { SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); - pass->d3d11.ds_att.image = ds_img; + atts->d3d11.depth_stencil.image = ds_img; } // create render-target views - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - const _sg_pass_attachment_t* cmn_color_att = &pass->cmn.color_atts[i]; + for (int i = 0; i < atts->cmn.num_colors; i++) { + const _sg_attachment_common_t* cmn_color_att = &atts->cmn.colors[i]; const _sg_image_t* color_img = color_images[i]; - SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].view.rtv); + SOKOL_ASSERT(0 == atts->d3d11.colors[i].view.rtv); const bool msaa = color_img->cmn.sample_count > 1; D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; _sg_clear(&d3d11_rtv_desc, sizeof(d3d11_rtv_desc)); @@ -10346,15 +11086,16 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_ima d3d11_rtv_desc.Texture3D.WSize = 1; } SOKOL_ASSERT(color_img->d3d11.res); - HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, color_img->d3d11.res, &d3d11_rtv_desc, &pass->d3d11.color_atts[i].view.rtv); - if (!(SUCCEEDED(hr) && pass->d3d11.color_atts[i].view.rtv)) { + HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, color_img->d3d11.res, &d3d11_rtv_desc, &atts->d3d11.colors[i].view.rtv); + if (!(SUCCEEDED(hr) && atts->d3d11.colors[i].view.rtv)) { _SG_ERROR(D3D11_CREATE_RTV_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(atts->d3d11.colors[i].view.rtv, desc->label); } - SOKOL_ASSERT(0 == pass->d3d11.ds_att.view.dsv); + SOKOL_ASSERT(0 == atts->d3d11.depth_stencil.view.dsv); if (ds_desc->image.id != SG_INVALID_ID) { - const _sg_pass_attachment_t* cmn_ds_att = &pass->cmn.ds_att; + const _sg_attachment_common_t* cmn_ds_att = &atts->cmn.depth_stencil; const bool msaa = ds_img->cmn.sample_count > 1; D3D11_DEPTH_STENCIL_VIEW_DESC d3d11_dsv_desc; _sg_clear(&d3d11_dsv_desc, sizeof(d3d11_dsv_desc)); @@ -10380,107 +11121,95 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_ima } } SOKOL_ASSERT(ds_img->d3d11.res); - HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, ds_img->d3d11.res, &d3d11_dsv_desc, &pass->d3d11.ds_att.view.dsv); - if (!(SUCCEEDED(hr) && pass->d3d11.ds_att.view.dsv)) { + HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, ds_img->d3d11.res, &d3d11_dsv_desc, &atts->d3d11.depth_stencil.view.dsv); + if (!(SUCCEEDED(hr) && atts->d3d11.depth_stencil.view.dsv)) { _SG_ERROR(D3D11_CREATE_DSV_FAILED); return SG_RESOURCESTATE_FAILED; } + _sg_d3d11_setlabel(atts->d3d11.depth_stencil.view.dsv, desc->label); } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_d3d11_discard_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - SOKOL_ASSERT(pass != _sg.d3d11.cur_pass); +_SOKOL_PRIVATE void _sg_d3d11_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->d3d11.color_atts[i].view.rtv) { - _sg_d3d11_Release(pass->d3d11.color_atts[i].view.rtv); + if (atts->d3d11.colors[i].view.rtv) { + _sg_d3d11_Release(atts->d3d11.colors[i].view.rtv); } - if (pass->d3d11.resolve_atts[i].view.rtv) { - _sg_d3d11_Release(pass->d3d11.resolve_atts[i].view.rtv); + if (atts->d3d11.resolves[i].view.rtv) { + _sg_d3d11_Release(atts->d3d11.resolves[i].view.rtv); } } - if (pass->d3d11.ds_att.view.dsv) { - _sg_d3d11_Release(pass->d3d11.ds_att.view.dsv); + if (atts->d3d11.depth_stencil.view.dsv) { + _sg_d3d11_Release(atts->d3d11.depth_stencil.view.dsv); } } -_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_color_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - return pass->d3d11.color_atts[index].image; +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->d3d11.colors[index].image; } -_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_resolve_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - return pass->d3d11.resolve_atts[index].image; +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->d3d11.resolves[index].image; } -_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_ds_image(const _sg_pass_t* pass) { - SOKOL_ASSERT(pass); - return pass->d3d11.ds_att.image; +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_attachments_ds_image(const _sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + return atts->d3d11.depth_stencil.image; } -_SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { - SOKOL_ASSERT(action); - SOKOL_ASSERT(!_sg.d3d11.in_pass); - SOKOL_ASSERT(_sg.d3d11.rtv_cb || _sg.d3d11.rtv_userdata_cb); - SOKOL_ASSERT(_sg.d3d11.dsv_cb || _sg.d3d11.dsv_userdata_cb); - _sg.d3d11.in_pass = true; - _sg.d3d11.cur_width = w; - _sg.d3d11.cur_height = h; - if (pass) { - _sg.d3d11.cur_pass = pass; - _sg.d3d11.cur_pass_id.id = pass->slot.id; - _sg.d3d11.num_rtvs = 0; +_SOKOL_PRIVATE void _sg_d3d11_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); + + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + + int num_rtvs = 0; + ID3D11RenderTargetView* rtvs[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; + ID3D11DepthStencilView* dsv = 0; + _sg.d3d11.cur_pass.render_view = 0; + _sg.d3d11.cur_pass.resolve_view = 0; + if (atts) { + num_rtvs = atts->cmn.num_colors; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg.d3d11.cur_rtvs[i] = pass->d3d11.color_atts[i].view.rtv; - if (_sg.d3d11.cur_rtvs[i]) { - _sg.d3d11.num_rtvs++; - } + rtvs[i] = atts->d3d11.colors[i].view.rtv; } - _sg.d3d11.cur_dsv = pass->d3d11.ds_att.view.dsv; + dsv = atts->d3d11.depth_stencil.view.dsv; } else { - // render to default frame buffer - _sg.d3d11.cur_pass = 0; - _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; - _sg.d3d11.num_rtvs = 1; - if (_sg.d3d11.rtv_cb) { - _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_cb(); - } else { - _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_userdata_cb(_sg.d3d11.user_data); - } - for (int i = 1; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg.d3d11.cur_rtvs[i] = 0; - } - if (_sg.d3d11.dsv_cb) { - _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_cb(); - } else { - _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_userdata_cb(_sg.d3d11.user_data); - } - SOKOL_ASSERT(_sg.d3d11.cur_rtvs[0] && _sg.d3d11.cur_dsv); + // NOTE: depth-stencil-view is optional + SOKOL_ASSERT(swapchain->d3d11.render_view); + num_rtvs = 1; + rtvs[0] = (ID3D11RenderTargetView*) swapchain->d3d11.render_view; + dsv = (ID3D11DepthStencilView*) swapchain->d3d11.depth_stencil_view; + _sg.d3d11.cur_pass.render_view = (ID3D11RenderTargetView*) swapchain->d3d11.render_view; + _sg.d3d11.cur_pass.resolve_view = (ID3D11RenderTargetView*) swapchain->d3d11.resolve_view; } // apply the render-target- and depth-stencil-views - _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, _sg.d3d11.cur_rtvs, _sg.d3d11.cur_dsv); + _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, rtvs, dsv); _sg_stats_add(d3d11.pass.num_om_set_render_targets, 1); // set viewport and scissor rect to cover whole screen D3D11_VIEWPORT vp; _sg_clear(&vp, sizeof(vp)); - vp.Width = (FLOAT) w; - vp.Height = (FLOAT) h; + vp.Width = (FLOAT) _sg.cur_pass.width; + vp.Height = (FLOAT) _sg.cur_pass.height; vp.MaxDepth = 1.0f; _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); D3D11_RECT rect; rect.left = 0; rect.top = 0; - rect.right = w; - rect.bottom = h; + rect.right = _sg.cur_pass.width; + rect.bottom = _sg.cur_pass.height; _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); // perform clear action - for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { + for (int i = 0; i < num_rtvs; i++) { if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { - _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, _sg.d3d11.cur_rtvs[i], &action->colors[i].clear_value.r); + _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, rtvs[i], (float*)&action->colors[i].clear_value); _sg_stats_add(d3d11.pass.num_clear_render_target_view, 1); } } @@ -10491,8 +11220,8 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* if (action->stencil.load_action == SG_LOADACTION_CLEAR) { ds_flags |= D3D11_CLEAR_STENCIL; } - if ((0 != ds_flags) && _sg.d3d11.cur_dsv) { - _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, _sg.d3d11.cur_dsv, ds_flags, action->depth.clear_value, action->stencil.clear_value); + if ((0 != ds_flags) && dsv) { + _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, dsv, ds_flags, action->depth.clear_value, action->stencil.clear_value); _sg_stats_add(d3d11.pass.num_clear_depth_stencil_view, 1); } } @@ -10503,18 +11232,18 @@ _SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, } _SOKOL_PRIVATE void _sg_d3d11_end_pass(void) { - SOKOL_ASSERT(_sg.d3d11.in_pass && _sg.d3d11.ctx); - _sg.d3d11.in_pass = false; + SOKOL_ASSERT(_sg.d3d11.ctx); // need to resolve MSAA render attachments into texture? - if (_sg.d3d11.cur_pass) { - SOKOL_ASSERT(_sg.d3d11.cur_pass->slot.id == _sg.d3d11.cur_pass_id.id); - for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { - const _sg_image_t* resolve_img = _sg.d3d11.cur_pass->d3d11.resolve_atts[i].image; + if (_sg.cur_pass.atts_id.id != SG_INVALID_ID) { + // ...for offscreen pass... + SOKOL_ASSERT(_sg.cur_pass.atts && _sg.cur_pass.atts->slot.id == _sg.cur_pass.atts_id.id); + for (int i = 0; i < _sg.cur_pass.atts->cmn.num_colors; i++) { + const _sg_image_t* resolve_img = _sg.cur_pass.atts->d3d11.resolves[i].image; if (resolve_img) { - const _sg_image_t* color_img = _sg.d3d11.cur_pass->d3d11.color_atts[i].image; - const _sg_pass_attachment_t* cmn_color_att = &_sg.d3d11.cur_pass->cmn.color_atts[i]; - const _sg_pass_attachment_t* cmn_resolve_att = &_sg.d3d11.cur_pass->cmn.resolve_atts[i]; + const _sg_image_t* color_img = _sg.cur_pass.atts->d3d11.colors[i].image; + const _sg_attachment_common_t* cmn_color_att = &_sg.cur_pass.atts->cmn.colors[i]; + const _sg_attachment_common_t* cmn_resolve_att = &_sg.cur_pass.atts->cmn.resolves[i]; SOKOL_ASSERT(resolve_img->slot.id == cmn_resolve_att->image_id.id); SOKOL_ASSERT(color_img && (color_img->slot.id == cmn_color_att->image_id.id)); SOKOL_ASSERT(color_img->cmn.sample_count > 1); @@ -10536,25 +11265,37 @@ _SOKOL_PRIVATE void _sg_d3d11_end_pass(void) { _sg_stats_add(d3d11.pass.num_resolve_subresource, 1); } } - } - - _sg.d3d11.cur_pass = 0; - _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; + } else { + // ...for swapchain pass... + if (_sg.d3d11.cur_pass.resolve_view) { + SOKOL_ASSERT(_sg.d3d11.cur_pass.render_view); + SOKOL_ASSERT(_sg.cur_pass.swapchain.sample_count > 1); + SOKOL_ASSERT(_sg.cur_pass.swapchain.color_fmt > SG_PIXELFORMAT_NONE); + ID3D11Resource* d3d11_render_res = 0; + ID3D11Resource* d3d11_resolve_res = 0; + _sg_d3d11_GetResource((ID3D11View*)_sg.d3d11.cur_pass.render_view, &d3d11_render_res); + _sg_d3d11_GetResource((ID3D11View*)_sg.d3d11.cur_pass.resolve_view, &d3d11_resolve_res); + SOKOL_ASSERT(d3d11_render_res); + SOKOL_ASSERT(d3d11_resolve_res); + const sg_pixel_format color_fmt = _sg.cur_pass.swapchain.color_fmt; + _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, d3d11_resolve_res, 0, d3d11_render_res, 0, _sg_d3d11_rtv_pixel_format(color_fmt)); + _sg_d3d11_Release(d3d11_render_res); + _sg_d3d11_Release(d3d11_resolve_res); + _sg_stats_add(d3d11.pass.num_resolve_subresource, 1); + } + } + _sg.d3d11.cur_pass.render_view = 0; + _sg.d3d11.cur_pass.resolve_view = 0; _sg.d3d11.cur_pipeline = 0; _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg.d3d11.cur_rtvs[i] = 0; - } - _sg.d3d11.cur_dsv = 0; _sg_d3d11_clear_state(); } _SOKOL_PRIVATE void _sg_d3d11_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(_sg.d3d11.in_pass); D3D11_VIEWPORT vp; vp.TopLeftX = (FLOAT) x; - vp.TopLeftY = (FLOAT) (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); + vp.TopLeftY = (FLOAT) (origin_top_left ? y : (_sg.cur_pass.height - (y + h))); vp.Width = (FLOAT) w; vp.Height = (FLOAT) h; vp.MinDepth = 0.0f; @@ -10564,12 +11305,11 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_viewport(int x, int y, int w, int h, bool or _SOKOL_PRIVATE void _sg_d3d11_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(_sg.d3d11.in_pass); D3D11_RECT rect; rect.left = x; - rect.top = (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); + rect.top = (origin_top_left ? y : (_sg.cur_pass.height - (y + h))); rect.right = x + w; - rect.bottom = origin_top_left ? (y + h) : (_sg.d3d11.cur_height - y); + rect.bottom = origin_top_left ? (y + h) : (_sg.cur_pass.height - y); _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); } @@ -10577,8 +11317,7 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(_sg.d3d11.in_pass); - SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss && pip->d3d11.il); + SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss); _sg.d3d11.cur_pipeline = pip; _sg.d3d11.cur_pipeline_id.id = pip->slot.id; @@ -10587,7 +11326,7 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { _sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs); _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref); - _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, &pip->cmn.blend_color.r, 0xFFFFFFFF); + _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, (float*)&pip->cmn.blend_color, 0xFFFFFFFF); _sg_d3d11_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11.topology); _sg_d3d11_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11.il); _sg_d3d11_VSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.vs, NULL, 0); @@ -10609,14 +11348,13 @@ _SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { SOKOL_ASSERT(bnd); SOKOL_ASSERT(bnd->pip); SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(_sg.d3d11.in_pass); // gather all the D3D11 resources into arrays ID3D11Buffer* d3d11_ib = bnd->ib ? bnd->ib->d3d11.buf : 0; ID3D11Buffer* d3d11_vbs[SG_MAX_VERTEX_BUFFERS] = {0}; UINT d3d11_vb_offsets[SG_MAX_VERTEX_BUFFERS] = {0}; - ID3D11ShaderResourceView* d3d11_vs_srvs[SG_MAX_SHADERSTAGE_IMAGES] = {0}; - ID3D11ShaderResourceView* d3d11_fs_srvs[SG_MAX_SHADERSTAGE_IMAGES] = {0}; + ID3D11ShaderResourceView* d3d11_vs_srvs[_SG_D3D11_MAX_SHADERSTAGE_SRVS] = {0}; + ID3D11ShaderResourceView* d3d11_fs_srvs[_SG_D3D11_MAX_SHADERSTAGE_SRVS] = {0}; ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; for (int i = 0; i < bnd->num_vbs; i++) { @@ -10626,11 +11364,19 @@ _SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { } for (int i = 0; i < bnd->num_vs_imgs; i++) { SOKOL_ASSERT(bnd->vs_imgs[i]->d3d11.srv); - d3d11_vs_srvs[i] = bnd->vs_imgs[i]->d3d11.srv; + d3d11_vs_srvs[_SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET + i] = bnd->vs_imgs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + SOKOL_ASSERT(bnd->vs_sbufs[i]->d3d11.srv); + d3d11_vs_srvs[_SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET + i] = bnd->vs_sbufs[i]->d3d11.srv; } for (int i = 0; i < bnd->num_fs_imgs; i++) { SOKOL_ASSERT(bnd->fs_imgs[i]->d3d11.srv); - d3d11_fs_srvs[i] = bnd->fs_imgs[i]->d3d11.srv; + d3d11_fs_srvs[_SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET + i] = bnd->fs_imgs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + SOKOL_ASSERT(bnd->fs_sbufs[i]->d3d11.srv); + d3d11_fs_srvs[_SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET + i] = bnd->fs_sbufs[i]->d3d11.srv; } for (int i = 0; i < bnd->num_vs_smps; i++) { SOKOL_ASSERT(bnd->vs_smps[i]->d3d11.smp); @@ -10642,8 +11388,8 @@ _SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { } _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_VERTEX_BUFFERS, d3d11_vbs, bnd->pip->d3d11.vb_strides, d3d11_vb_offsets); _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, bnd->pip->d3d11.index_format, (UINT)bnd->ib_offset); - _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_srvs); - _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_srvs); + _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_SHADERSTAGE_SRVS, d3d11_vs_srvs); + _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_SHADERSTAGE_SRVS, d3d11_fs_srvs); _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_vs_smps); _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_fs_smps); _sg_stats_add(d3d11.bindings.num_ia_set_vertex_buffers, 1); @@ -10656,7 +11402,7 @@ _SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { } _SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { - SOKOL_ASSERT(_sg.d3d11.ctx && _sg.d3d11.in_pass); + SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(_sg.d3d11.cur_pipeline && _sg.d3d11.cur_pipeline->slot.id == _sg.d3d11.cur_pipeline_id.id); SOKOL_ASSERT(_sg.d3d11.cur_pipeline->shader && _sg.d3d11.cur_pipeline->shader->slot.id == _sg.d3d11.cur_pipeline->cmn.shader_id.id); SOKOL_ASSERT(ub_index < _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); @@ -10668,9 +11414,9 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub } _SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) { - SOKOL_ASSERT(_sg.d3d11.in_pass); + const bool use_instanced_draw = (num_instances > 1) || (_sg.d3d11.use_instanced_draw); if (_sg.d3d11.use_indexed_draw) { - if (_sg.d3d11.use_instanced_draw) { + if (use_instanced_draw) { _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); _sg_stats_add(d3d11.draw.num_draw_indexed_instanced, 1); } else { @@ -10678,7 +11424,7 @@ _SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_i _sg_stats_add(d3d11.draw.num_draw_indexed, 1); } } else { - if (_sg.d3d11.use_instanced_draw) { + if (use_instanced_draw) { _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); _sg_stats_add(d3d11.draw.num_draw_instanced, 1); } else { @@ -10689,7 +11435,7 @@ _SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_i } _SOKOL_PRIVATE void _sg_d3d11_commit(void) { - SOKOL_ASSERT(!_sg.d3d11.in_pass); + // empty } _SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* data) { @@ -10726,12 +11472,15 @@ _SOKOL_PRIVATE void _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* d } } +// see: https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-subresources +// also see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11calcsubresource _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) { SOKOL_ASSERT(img && data); SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(img->d3d11.res); const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; + const int num_depth_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? img->cmn.num_slices:1; UINT subres_index = 0; HRESULT hr; D3D11_MAPPED_SUBRESOURCE d3d11_msr; @@ -10741,26 +11490,35 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); - const int src_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const int src_row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const int src_depth_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); const size_t slice_size = subimg_data->size / (size_t)num_slices; + SOKOL_ASSERT(slice_size == (size_t)(src_depth_pitch * num_depth_slices)); const size_t slice_offset = slice_size * (size_t)slice_index; const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset; hr = _sg_d3d11_Map(_sg.d3d11.ctx, img->d3d11.res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); _sg_stats_add(d3d11.num_map, 1); if (SUCCEEDED(hr)) { - // FIXME: need to handle difference in depth-pitch for 3D textures as well! - if (src_pitch == (int)d3d11_msr.RowPitch) { - memcpy(d3d11_msr.pData, slice_ptr, slice_size); - } else { - SOKOL_ASSERT(src_pitch < (int)d3d11_msr.RowPitch); - const uint8_t* src_ptr = slice_ptr; - uint8_t* dst_ptr = (uint8_t*) d3d11_msr.pData; - for (int row_index = 0; row_index < mip_height; row_index++) { - memcpy(dst_ptr, src_ptr, (size_t)src_pitch); - src_ptr += src_pitch; - dst_ptr += d3d11_msr.RowPitch; + const uint8_t* src_ptr = slice_ptr; + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData; + for (int depth_index = 0; depth_index < num_depth_slices; depth_index++) { + if (src_row_pitch == (int)d3d11_msr.RowPitch) { + const size_t copy_size = slice_size / (size_t)num_depth_slices; + SOKOL_ASSERT((copy_size * (size_t)num_depth_slices) == slice_size); + memcpy(dst_ptr, src_ptr, copy_size); + } else { + SOKOL_ASSERT(src_row_pitch < (int)d3d11_msr.RowPitch); + const uint8_t* src_row_ptr = src_ptr; + uint8_t* dst_row_ptr = dst_ptr; + for (int row_index = 0; row_index < mip_height; row_index++) { + memcpy(dst_row_ptr, src_row_ptr, (size_t)src_row_pitch); + src_row_ptr += src_row_pitch; + dst_row_ptr += d3d11_msr.RowPitch; + } } + src_ptr += src_depth_pitch; + dst_ptr += d3d11_msr.DepthPitch; } _sg_d3d11_Unmap(_sg.d3d11.ctx, img->d3d11.res, subres_index); _sg_stats_add(d3d11.num_unmap, 1); @@ -10936,6 +11694,7 @@ _SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_BC1_RGBA: return MTLPixelFormatBC1_RGBA; case SG_PIXELFORMAT_BC2_RGBA: return MTLPixelFormatBC2_RGBA; case SG_PIXELFORMAT_BC3_RGBA: return MTLPixelFormatBC3_RGBA; + case SG_PIXELFORMAT_BC3_SRGBA: return MTLPixelFormatBC3_RGBA_sRGB; case SG_PIXELFORMAT_BC4_R: return MTLPixelFormatBC4_RUnorm; case SG_PIXELFORMAT_BC4_RSN: return MTLPixelFormatBC4_RSnorm; case SG_PIXELFORMAT_BC5_RG: return MTLPixelFormatBC5_RGUnorm; @@ -10943,16 +11702,23 @@ _SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_BC6H_RGBF: return MTLPixelFormatBC6H_RGBFloat; case SG_PIXELFORMAT_BC6H_RGBUF: return MTLPixelFormatBC6H_RGBUfloat; case SG_PIXELFORMAT_BC7_RGBA: return MTLPixelFormatBC7_RGBAUnorm; + case SG_PIXELFORMAT_BC7_SRGBA: return MTLPixelFormatBC7_RGBAUnorm_sRGB; #else case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return MTLPixelFormatPVRTC_RGB_2BPP; case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return MTLPixelFormatPVRTC_RGB_4BPP; case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return MTLPixelFormatPVRTC_RGBA_2BPP; case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return MTLPixelFormatPVRTC_RGBA_4BPP; case SG_PIXELFORMAT_ETC2_RGB8: return MTLPixelFormatETC2_RGB8; + case SG_PIXELFORMAT_ETC2_SRGB8: return MTLPixelFormatETC2_RGB8_sRGB; case SG_PIXELFORMAT_ETC2_RGB8A1: return MTLPixelFormatETC2_RGB8A1; case SG_PIXELFORMAT_ETC2_RGBA8: return MTLPixelFormatEAC_RGBA8; - case SG_PIXELFORMAT_ETC2_RG11: return MTLPixelFormatEAC_RG11Unorm; - case SG_PIXELFORMAT_ETC2_RG11SN: return MTLPixelFormatEAC_RG11Snorm; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return MTLPixelFormatEAC_RGBA8_sRGB; + case SG_PIXELFORMAT_EAC_R11: return MTLPixelFormatEAC_R11Unorm; + case SG_PIXELFORMAT_EAC_R11SN: return MTLPixelFormatEAC_R11Snorm; + case SG_PIXELFORMAT_EAC_RG11: return MTLPixelFormatEAC_RG11Unorm; + case SG_PIXELFORMAT_EAC_RG11SN: return MTLPixelFormatEAC_RG11Snorm; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return MTLPixelFormatASTC_4x4_LDR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return MTLPixelFormatASTC_4x4_sRGB; #endif default: return MTLPixelFormatInvalid; } @@ -11134,8 +11900,6 @@ _SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) { _SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mipmap_filter(sg_filter f) { switch (f) { - case SG_FILTER_NONE: - return MTLSamplerMipFilterNotMipmapped; case SG_FILTER_NEAREST: return MTLSamplerMipFilterNearest; case SG_FILTER_LINEAR: @@ -11154,7 +11918,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { 1 * desc->sampler_pool_size + 4 * desc->shader_pool_size + 2 * desc->pipeline_pool_size + - desc->pass_pool_size + + desc->attachments_pool_size + 128 ); _sg.mtl.idpool.pool = [NSMutableArray arrayWithCapacity:(NSUInteger)_sg.mtl.idpool.num_slots]; @@ -11290,6 +12054,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg.features.origin_top_left = true; _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; _sg.features.image_clamp_to_border = false; #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) @@ -11404,6 +12169,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); @@ -11411,16 +12177,24 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); #else _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + #endif } @@ -11428,21 +12202,14 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { // assume already zero-initialized SOKOL_ASSERT(desc); - SOKOL_ASSERT(desc->context.metal.device); - SOKOL_ASSERT(desc->context.metal.renderpass_descriptor_cb || desc->context.metal.renderpass_descriptor_userdata_cb); - SOKOL_ASSERT(desc->context.metal.drawable_cb || desc->context.metal.drawable_userdata_cb); + SOKOL_ASSERT(desc->environment.metal.device); SOKOL_ASSERT(desc->uniform_buffer_size > 0); _sg_mtl_init_pool(desc); _sg_mtl_clear_state_cache(); _sg.mtl.valid = true; - _sg.mtl.renderpass_descriptor_cb = desc->context.metal.renderpass_descriptor_cb; - _sg.mtl.renderpass_descriptor_userdata_cb = desc->context.metal.renderpass_descriptor_userdata_cb; - _sg.mtl.drawable_cb = desc->context.metal.drawable_cb; - _sg.mtl.drawable_userdata_cb = desc->context.metal.drawable_userdata_cb; - _sg.mtl.user_data = desc->context.metal.user_data; _sg.mtl.ub_size = desc->uniform_buffer_size; _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); - _sg.mtl.device = (__bridge id) desc->context.metal.device; + _sg.mtl.device = (__bridge id) desc->environment.metal.device; _sg.mtl.cmd_queue = [_sg.mtl.device newCommandQueue]; for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { @@ -11523,23 +12290,6 @@ _SOKOL_PRIVATE void _sg_mtl_reset_state_cache(void) { } } -_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_mtl_discard_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - // empty -} - -_SOKOL_PRIVATE void _sg_mtl_activate_context(_sg_context_t* ctx) { - _SOKOL_UNUSED(ctx); - _sg_mtl_clear_state_cache(); -} - _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); const bool injected = (0 != desc->mtl_buffers[0]); @@ -11628,7 +12378,7 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unr } } -// initialize MTLTextureDescritor with common attributes +// initialize MTLTextureDescriptor with common attributes _SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { mtl_desc.textureType = _sg_mtl_texture_type(img->cmn.type); mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->cmn.pixel_format); @@ -11659,7 +12409,7 @@ _SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, return true; } -// initialize MTLTextureDescritor with rendertarget attributes +// initialize MTLTextureDescriptor with rendertarget attributes _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { SOKOL_ASSERT(img->cmn.render_target); _SOKOL_UNUSED(img); @@ -11667,7 +12417,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_ mtl_desc.resourceOptions = MTLResourceStorageModePrivate; } -// initialize MTLTextureDescritor with MSAA attributes +// initialize MTLTextureDescriptor with MSAA attributes _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt_msaa(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { SOKOL_ASSERT(img->cmn.sample_count > 1); mtl_desc.usage = MTLTextureUsageRenderTarget; @@ -12031,72 +12781,72 @@ _SOKOL_PRIVATE void _sg_mtl_discard_pipeline(_sg_pipeline_t* pip) { _sg_mtl_release_resource(_sg.frame_index, pip->mtl.dss); } -_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pass(_sg_pass_t* pass, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && desc); +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); SOKOL_ASSERT(color_images && resolve_images); // copy image pointers - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - const sg_pass_attachment_desc* color_desc = &desc->color_attachments[i]; + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; _SOKOL_UNUSED(color_desc); SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); - SOKOL_ASSERT(0 == pass->mtl.color_atts[i].image); + SOKOL_ASSERT(0 == atts->mtl.colors[i].image); SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); - pass->mtl.color_atts[i].image = color_images[i]; + atts->mtl.colors[i].image = color_images[i]; - const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; if (resolve_desc->image.id != SG_INVALID_ID) { - SOKOL_ASSERT(0 == pass->mtl.resolve_atts[i].image); + SOKOL_ASSERT(0 == atts->mtl.resolves[i].image); SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); - pass->mtl.resolve_atts[i].image = resolve_images[i]; + atts->mtl.resolves[i].image = resolve_images[i]; } } - SOKOL_ASSERT(0 == pass->mtl.ds_att.image); - const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + SOKOL_ASSERT(0 == atts->mtl.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; if (ds_desc->image.id != SG_INVALID_ID) { SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); - pass->mtl.ds_att.image = ds_img; + atts->mtl.depth_stencil.image = ds_img; } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_mtl_discard_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - _SOKOL_UNUSED(pass); +_SOKOL_PRIVATE void _sg_mtl_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + _SOKOL_UNUSED(atts); } -_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_color_image(const _sg_pass_t* pass, int index) { +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_attachments_color_image(const _sg_attachments_t* atts, int index) { // NOTE: may return null - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - return pass->mtl.color_atts[index].image; + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->mtl.colors[index].image; } -_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_resolve_image(const _sg_pass_t* pass, int index) { +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_attachments_resolve_image(const _sg_attachments_t* atts, int index) { // NOTE: may return null - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - return pass->mtl.resolve_atts[index].image; + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->mtl.resolves[index].image; } -_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_ds_image(const _sg_pass_t* pass) { +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_attachments_ds_image(const _sg_attachments_t* atts) { // NOTE: may return null - SOKOL_ASSERT(pass); - return pass->mtl.ds_att.image; + SOKOL_ASSERT(atts); + return atts->mtl.depth_stencil.image; } -_SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { - SOKOL_ASSERT(action); - SOKOL_ASSERT(!_sg.mtl.in_pass); +_SOKOL_PRIVATE void _sg_mtl_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); SOKOL_ASSERT(_sg.mtl.cmd_queue); SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); - SOKOL_ASSERT(_sg.mtl.renderpass_descriptor_cb || _sg.mtl.renderpass_descriptor_userdata_cb); - _sg.mtl.in_pass = true; - _sg.mtl.cur_width = w; - _sg.mtl.cur_height = h; + SOKOL_ASSERT(nil == _sg.mtl.cur_drawable); _sg_mtl_clear_state_cache(); + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + /* if this is the first pass in the frame, create command buffers @@ -12111,7 +12861,11 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a if (nil == _sg.mtl.cmd_buffer) { // block until the oldest frame in flight has finished dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); - _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences]; + if (_sg.desc.mtl_use_command_buffer_with_retained_references) { + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBuffer]; + } else { + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences]; + } [_sg.mtl.cmd_buffer enqueue]; [_sg.mtl.cmd_buffer addCompletedHandler:^(id cmd_buf) { // NOTE: this code is called on a different thread! @@ -12125,39 +12879,17 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; } - // initialize a render pass descriptor - MTLRenderPassDescriptor* pass_desc = nil; - if (pass) { - // offscreen render pass - pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; - } else { - // default render pass, call user-provided callback to provide render pass descriptor - if (_sg.mtl.renderpass_descriptor_cb) { - pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_cb(); - } else { - pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_userdata_cb(_sg.mtl.user_data); - } - // pin the swapchain resources into memory so that they outlive their command buffer - // (this is necessary because the command buffer doesn't retain references) - int default_pass_desc_ref = _sg_mtl_add_resource(pass_desc); - _sg_mtl_release_resource(_sg.frame_index, default_pass_desc_ref); - } - if (pass_desc) { - _sg.mtl.pass_valid = true; - } else { - // default pass descriptor will not be valid if window is minimized, don't do any rendering in this case - _sg.mtl.pass_valid = false; - return; - } - if (pass) { + MTLRenderPassDescriptor* pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; + SOKOL_ASSERT(pass_desc); + if (atts) { // setup pass descriptor for offscreen rendering - SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); - for (NSUInteger i = 0; i < (NSUInteger)pass->cmn.num_color_atts; i++) { - const _sg_pass_attachment_t* cmn_color_att = &pass->cmn.color_atts[i]; - const _sg_mtl_attachment_t* mtl_color_att = &pass->mtl.color_atts[i]; + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_VALID); + for (NSUInteger i = 0; i < (NSUInteger)atts->cmn.num_colors; i++) { + const _sg_attachment_common_t* cmn_color_att = &atts->cmn.colors[i]; + const _sg_mtl_attachment_t* mtl_color_att = &atts->mtl.colors[i]; const _sg_image_t* color_att_img = mtl_color_att->image; - const _sg_pass_attachment_t* cmn_resolve_att = &pass->cmn.resolve_atts[i]; - const _sg_mtl_attachment_t* mtl_resolve_att = &pass->mtl.resolve_atts[i]; + const _sg_attachment_common_t* cmn_resolve_att = &atts->cmn.resolves[i]; + const _sg_mtl_attachment_t* mtl_resolve_att = &atts->mtl.resolves[i]; const _sg_image_t* resolve_att_img = mtl_resolve_att->image; SOKOL_ASSERT(color_att_img->slot.state == SG_RESOURCESTATE_VALID); SOKOL_ASSERT(color_att_img->slot.id == cmn_color_att->image_id.id); @@ -12196,16 +12928,16 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a } } } - const _sg_image_t* ds_att_img = pass->mtl.ds_att.image; + const _sg_image_t* ds_att_img = atts->mtl.depth_stencil.image; if (0 != ds_att_img) { SOKOL_ASSERT(ds_att_img->slot.state == SG_RESOURCESTATE_VALID); - SOKOL_ASSERT(ds_att_img->slot.id == pass->cmn.ds_att.image_id.id); + SOKOL_ASSERT(ds_att_img->slot.id == atts->cmn.depth_stencil.image_id.id); SOKOL_ASSERT(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); pass_desc.depthAttachment.texture = _sg_mtl_id(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot]); pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action); pass_desc.depthAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false); pass_desc.depthAttachment.clearDepth = action->depth.clear_value; - const _sg_pass_attachment_t* cmn_ds_att = &pass->cmn.ds_att; + const _sg_attachment_common_t* cmn_ds_att = &atts->cmn.depth_stencil; switch (ds_att_img->cmn.type) { case SG_IMAGETYPE_CUBE: case SG_IMAGETYPE_ARRAY: @@ -12234,55 +12966,93 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a } } } else { - // setup pass descriptor for default rendering + // setup pass descriptor for swapchain rendering + // + // NOTE: at least in macOS Sonoma this no longer seems to be the case, the + // current drawable is also valid in a minimized window + // === + // an MTKView current_drawable will not be valid if window is minimized, don't do any rendering in this case + if (0 == swapchain->metal.current_drawable) { + _sg.cur_pass.valid = false; + return; + } + // pin the swapchain resources into memory so that they outlive their command buffer + // (this is necessary because the command buffer doesn't retain references) + int pass_desc_ref = _sg_mtl_add_resource(pass_desc); + _sg_mtl_release_resource(_sg.frame_index, pass_desc_ref); + + _sg.mtl.cur_drawable = (__bridge id) swapchain->metal.current_drawable; + if (swapchain->sample_count > 1) { + // multi-sampling: render into msaa texture, resolve into drawable texture + id msaa_tex = (__bridge id) swapchain->metal.msaa_color_texture; + SOKOL_ASSERT(msaa_tex != nil); + pass_desc.colorAttachments[0].texture = msaa_tex; + pass_desc.colorAttachments[0].resolveTexture = _sg.mtl.cur_drawable.texture; + pass_desc.colorAttachments[0].storeAction = MTLStoreActionMultisampleResolve; + } else { + // non-msaa: render into current_drawable + pass_desc.colorAttachments[0].texture = _sg.mtl.cur_drawable.texture; + pass_desc.colorAttachments[0].storeAction = MTLStoreActionStore; + } pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].load_action); - sg_color c = action->colors[0].clear_value; + const sg_color c = action->colors[0].clear_value; pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); - pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action); - pass_desc.depthAttachment.clearDepth = action->depth.clear_value; - pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action); - pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value; + + // optional depth-stencil texture + if (swapchain->metal.depth_stencil_texture) { + id ds_tex = (__bridge id) swapchain->metal.depth_stencil_texture; + SOKOL_ASSERT(ds_tex != nil); + pass_desc.depthAttachment.texture = ds_tex; + pass_desc.depthAttachment.storeAction = MTLStoreActionDontCare; + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action); + pass_desc.depthAttachment.clearDepth = action->depth.clear_value; + if (_sg_is_depth_stencil_format(swapchain->depth_format)) { + pass_desc.stencilAttachment.texture = ds_tex; + pass_desc.stencilAttachment.storeAction = MTLStoreActionDontCare; + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action); + pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value; + } + } } + // NOTE: at least in macOS Sonoma, the following is no longer the case, a valid + // render command encoder is also returned in a minimized window + // === // create a render command encoder, this might return nil if window is minimized _sg.mtl.cmd_encoder = [_sg.mtl.cmd_buffer renderCommandEncoderWithDescriptor:pass_desc]; if (nil == _sg.mtl.cmd_encoder) { - _sg.mtl.pass_valid = false; + _sg.cur_pass.valid = false; return; } + #if defined(SOKOL_DEBUG) + if (pass->label) { + _sg.mtl.cmd_encoder.label = [NSString stringWithUTF8String:pass->label]; + } + #endif + // bind the global uniform buffer, this only happens once per pass _sg_mtl_bind_uniform_buffers(); } _SOKOL_PRIVATE void _sg_mtl_end_pass(void) { - SOKOL_ASSERT(_sg.mtl.in_pass); - _sg.mtl.in_pass = false; - _sg.mtl.pass_valid = false; if (nil != _sg.mtl.cmd_encoder) { [_sg.mtl.cmd_encoder endEncoding]; // NOTE: MTLRenderCommandEncoder is autoreleased _sg.mtl.cmd_encoder = nil; } + // if this is a swapchain pass, present the drawable + if (nil != _sg.mtl.cur_drawable) { + [_sg.mtl.cmd_buffer presentDrawable:_sg.mtl.cur_drawable]; + _sg.mtl.cur_drawable = nil; + } } _SOKOL_PRIVATE void _sg_mtl_commit(void) { - SOKOL_ASSERT(!_sg.mtl.in_pass); - SOKOL_ASSERT(!_sg.mtl.pass_valid); - SOKOL_ASSERT(_sg.mtl.drawable_cb || _sg.mtl.drawable_userdata_cb); SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); - // present, commit and signal semaphore when done - id cur_drawable = nil; - if (_sg.mtl.drawable_cb) { - cur_drawable = (__bridge id) _sg.mtl.drawable_cb(); - } else { - cur_drawable = (__bridge id) _sg.mtl.drawable_userdata_cb(_sg.mtl.user_data); - } - if (nil != cur_drawable) { - [_sg.mtl.cmd_buffer presentDrawable:cur_drawable]; - } + // commit the frame's command buffer [_sg.mtl.cmd_buffer commit]; // garbage-collect resources pending for release @@ -12299,14 +13069,11 @@ _SOKOL_PRIVATE void _sg_mtl_commit(void) { } _SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return; - } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.cur_pass.height > 0); MTLViewport vp; vp.originX = (double) x; - vp.originY = (double) (origin_top_left ? y : (_sg.mtl.cur_height - (y + h))); + vp.originY = (double) (origin_top_left ? y : (_sg.cur_pass.height - (y + h))); vp.width = (double) w; vp.height = (double) h; vp.znear = 0.0; @@ -12315,16 +13082,14 @@ _SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool orig } _SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return; - } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.cur_pass.width > 0); + SOKOL_ASSERT(_sg.cur_pass.height > 0); // clip against framebuffer rect - const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.mtl.cur_width, _sg.mtl.cur_height); + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.width, _sg.cur_pass.height); MTLScissorRect r; r.x = (NSUInteger)clip.x; - r.y = (NSUInteger) (origin_top_left ? clip.y : (_sg.mtl.cur_height - (clip.y + clip.h))); + r.y = (NSUInteger) (origin_top_left ? clip.y : (_sg.cur_pass.height - (clip.y + clip.h))); r.width = (NSUInteger)clip.w; r.height = (NSUInteger)clip.h; [_sg.mtl.cmd_encoder setScissorRect:r]; @@ -12333,10 +13098,6 @@ _SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool _SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return; - } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); if (_sg.mtl.state_cache.cur_pipeline_id.id != pip->slot.id) { @@ -12365,10 +13126,6 @@ _SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { _SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { SOKOL_ASSERT(bnd); SOKOL_ASSERT(bnd->pip); - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return false; - } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); // store index buffer binding, this will be needed later in sg_draw() @@ -12389,23 +13146,25 @@ _SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { if ((_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id) || (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offset)) { - _sg.mtl.state_cache.cur_vertexbuffers[slot] = vb; _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offset; - _sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id = vb->slot.id; const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; - SOKOL_ASSERT(vb->mtl.buf[vb->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(vb->mtl.buf[vb->cmn.active_slot]) - offset:(NSUInteger)vb_offset - atIndex:mtl_slot]; + if (_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id) { + _sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id = vb->slot.id; + SOKOL_ASSERT(vb->mtl.buf[vb->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(vb->mtl.buf[vb->cmn.active_slot]) + offset:(NSUInteger)vb_offset + atIndex:mtl_slot]; + } else { + [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)vb_offset atIndex:mtl_slot]; + } _sg_stats_add(metal.bindings.num_set_vertex_buffer, 1); } } - // apply vertex shader images + // apply vertex stage images for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_imgs; slot++) { const _sg_image_t* img = bnd->vs_imgs[slot]; if (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id) { - _sg.mtl.state_cache.cur_vs_images[slot] = img; _sg.mtl.state_cache.cur_vs_image_ids[slot].id = img->slot.id; SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setVertexTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; @@ -12413,11 +13172,10 @@ _SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { } } - // apply vertex shader samplers + // apply vertex stage samplers for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_smps; slot++) { const _sg_sampler_t* smp = bnd->vs_smps[slot]; if (_sg.mtl.state_cache.cur_vs_sampler_ids[slot].id != smp->slot.id) { - _sg.mtl.state_cache.cur_vs_samplers[slot] = smp; _sg.mtl.state_cache.cur_vs_sampler_ids[slot].id = smp->slot.id; SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setVertexSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:slot]; @@ -12425,11 +13183,22 @@ _SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { } } - // apply fragment shader images + // apply vertex stage storage buffers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_sbufs; slot++) { + const _sg_buffer_t* sbuf = bnd->vs_sbufs[slot]; + if (_sg.mtl.state_cache.cur_vs_storagebuffer_ids[slot].id != sbuf->slot.id) { + _sg.mtl.state_cache.cur_vs_storagebuffer_ids[slot].id = sbuf->slot.id; + SOKOL_ASSERT(sbuf->mtl.buf[sbuf->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + SG_MAX_VERTEX_BUFFERS + slot; + [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(sbuf->mtl.buf[sbuf->cmn.active_slot]) offset:0 atIndex:mtl_slot]; + _sg_stats_add(metal.bindings.num_set_vertex_buffer, 1); + } + } + + // apply fragment stage images for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_imgs; slot++) { const _sg_image_t* img = bnd->fs_imgs[slot]; if (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id) { - _sg.mtl.state_cache.cur_fs_images[slot] = img; _sg.mtl.state_cache.cur_fs_image_ids[slot].id = img->slot.id; SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setFragmentTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; @@ -12437,25 +13206,33 @@ _SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { } } - // apply fragment shader samplers + // apply fragment stage samplers for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_smps; slot++) { const _sg_sampler_t* smp = bnd->fs_smps[slot]; if (_sg.mtl.state_cache.cur_fs_sampler_ids[slot].id != smp->slot.id) { - _sg.mtl.state_cache.cur_fs_samplers[slot] = smp; _sg.mtl.state_cache.cur_fs_sampler_ids[slot].id = smp->slot.id; SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setFragmentSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:slot]; _sg_stats_add(metal.bindings.num_set_fragment_sampler_state, 1); } } + + // apply fragment stage storage buffers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_sbufs; slot++) { + const _sg_buffer_t* sbuf = bnd->fs_sbufs[slot]; + if (_sg.mtl.state_cache.cur_fs_storagebuffer_ids[slot].id != sbuf->slot.id) { + _sg.mtl.state_cache.cur_fs_storagebuffer_ids[slot].id = sbuf->slot.id; + SOKOL_ASSERT(sbuf->mtl.buf[sbuf->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; + [_sg.mtl.cmd_encoder setFragmentBuffer:_sg_mtl_id(sbuf->mtl.buf[sbuf->cmn.active_slot]) offset:0 atIndex:mtl_slot]; + _sg_stats_add(metal.bindings.num_set_fragment_buffer, 1); + } + } + return true; } _SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return; - } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); SOKOL_ASSERT(((size_t)_sg.mtl.cur_ub_offset + data->size) <= (size_t)_sg.mtl.ub_size); SOKOL_ASSERT((_sg.mtl.cur_ub_offset & (_SG_MTL_UB_ALIGN-1)) == 0); @@ -12479,10 +13256,6 @@ _SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_i } _SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_instances) { - SOKOL_ASSERT(_sg.mtl.in_pass); - if (!_sg.mtl.pass_valid) { - return; - } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && (_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id)); if (SG_INDEXTYPE_NONE != _sg.mtl.state_cache.cur_pipeline->cmn.index_type) { @@ -12515,7 +13288,7 @@ _SOKOL_PRIVATE void _sg_mtl_update_buffer(_sg_buffer_t* buf, const sg_range* dat void* dst_ptr = [mtl_buf contents]; memcpy(dst_ptr, data->ptr, data->size); #if defined(_SG_TARGET_MACOS) - if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLStorageModeManaged) { + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) { [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; } #endif @@ -12533,7 +13306,7 @@ _SOKOL_PRIVATE void _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* dat dst_ptr += buf->cmn.append_pos; memcpy(dst_ptr, data->ptr, data->size); #if defined(_SG_TARGET_MACOS) - if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLStorageModeManaged) { + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) { [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; } #endif @@ -12574,9 +13347,11 @@ _SOKOL_PRIVATE void _sg_mtl_pop_debug_group(void) { _SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_usage u) { WGPUBufferUsageFlags res = 0; if (SG_BUFFERTYPE_VERTEXBUFFER == t) { - res |= WGPUBufferUsage_Vertex; + res = WGPUBufferUsage_Vertex; + } else if (SG_BUFFERTYPE_STORAGEBUFFER == t) { + res = WGPUBufferUsage_Storage; } else { - res |= WGPUBufferUsage_Index; + res = WGPUBufferUsage_Index; } if (SG_USAGE_IMMUTABLE != u) { res |= WGPUBufferUsage_CopyDst; @@ -12680,7 +13455,6 @@ _SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmag_filter(sg_filter f) { _SOKOL_PRIVATE WGPUMipmapFilterMode _sg_wgpu_sampler_mipmap_filter(sg_filter f) { switch (f) { - case SG_FILTER_NONE: case SG_FILTER_NEAREST: return WGPUMipmapFilterMode_Nearest; case SG_FILTER_LINEAR: @@ -12806,6 +13580,7 @@ _SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { case SG_PIXELFORMAT_BC1_RGBA: return WGPUTextureFormat_BC1RGBAUnorm; case SG_PIXELFORMAT_BC2_RGBA: return WGPUTextureFormat_BC2RGBAUnorm; case SG_PIXELFORMAT_BC3_RGBA: return WGPUTextureFormat_BC3RGBAUnorm; + case SG_PIXELFORMAT_BC3_SRGBA: return WGPUTextureFormat_BC3RGBAUnormSrgb; case SG_PIXELFORMAT_BC4_R: return WGPUTextureFormat_BC4RUnorm; case SG_PIXELFORMAT_BC4_RSN: return WGPUTextureFormat_BC4RSnorm; case SG_PIXELFORMAT_BC5_RG: return WGPUTextureFormat_BC5RGUnorm; @@ -12813,13 +13588,19 @@ _SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { case SG_PIXELFORMAT_BC6H_RGBF: return WGPUTextureFormat_BC6HRGBFloat; case SG_PIXELFORMAT_BC6H_RGBUF: return WGPUTextureFormat_BC6HRGBUfloat; case SG_PIXELFORMAT_BC7_RGBA: return WGPUTextureFormat_BC7RGBAUnorm; + case SG_PIXELFORMAT_BC7_SRGBA: return WGPUTextureFormat_BC7RGBAUnormSrgb; case SG_PIXELFORMAT_ETC2_RGB8: return WGPUTextureFormat_ETC2RGB8Unorm; case SG_PIXELFORMAT_ETC2_RGB8A1: return WGPUTextureFormat_ETC2RGB8A1Unorm; case SG_PIXELFORMAT_ETC2_RGBA8: return WGPUTextureFormat_ETC2RGBA8Unorm; - case SG_PIXELFORMAT_ETC2_RG11: return WGPUTextureFormat_EACR11Unorm; - case SG_PIXELFORMAT_ETC2_RG11SN: return WGPUTextureFormat_EACR11Snorm; + case SG_PIXELFORMAT_ETC2_SRGB8: return WGPUTextureFormat_ETC2RGB8UnormSrgb; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return WGPUTextureFormat_ETC2RGBA8UnormSrgb; + case SG_PIXELFORMAT_EAC_R11: return WGPUTextureFormat_EACR11Unorm; + case SG_PIXELFORMAT_EAC_R11SN: return WGPUTextureFormat_EACR11Snorm; + case SG_PIXELFORMAT_EAC_RG11: return WGPUTextureFormat_EACRG11Unorm; + case SG_PIXELFORMAT_EAC_RG11SN: return WGPUTextureFormat_EACRG11Snorm; case SG_PIXELFORMAT_RGB9E5: return WGPUTextureFormat_RGB9E5Ufloat; - + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return WGPUTextureFormat_ASTC4x4Unorm; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return WGPUTextureFormat_ASTC4x4UnormSrgb; // NOT SUPPORTED case SG_PIXELFORMAT_R16: case SG_PIXELFORMAT_R16SN: @@ -12928,15 +13709,17 @@ _SOKOL_PRIVATE WGPUColorWriteMaskFlags _sg_wgpu_colorwritemask(uint8_t m) { // - all images and sampler are in @group(1) // - vertex stage images start at @binding(0) // - vertex stage samplers start at @binding(16) -// - fragment stage images start at @binding(32) -// - fragment stage samplers start at @binding(48) +// - vertex stage storage buffers start at @binding(32) +// - fragment stage images start at @binding(48) +// - fragment stage samplers start at @binding(64) +// - fragment stage storage buffers start at @binding(80) // _SOKOL_PRIVATE uint32_t _sg_wgpu_image_binding(sg_shader_stage stage, int img_slot) { SOKOL_ASSERT((img_slot >= 0) && (img_slot < 16)); if (SG_SHADERSTAGE_VS == stage) { return 0 + (uint32_t)img_slot; } else { - return 32 + (uint32_t)img_slot; + return 48 + (uint32_t)img_slot; } } @@ -12945,13 +13728,22 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_sampler_binding(sg_shader_stage stage, int smp_ if (SG_SHADERSTAGE_VS == stage) { return 16 + (uint32_t)smp_slot; } else { - return 48 + (uint32_t)smp_slot; + return 64 + (uint32_t)smp_slot; } } -_SOKOL_PRIVATE WGPUShaderStage _sg_wgpu_shader_stage(sg_shader_stage stage) { - switch (stage) { - case SG_SHADERSTAGE_VS: return WGPUShaderStage_Vertex; +_SOKOL_PRIVATE uint32_t _sg_wgpu_storagebuffer_binding(sg_shader_stage stage, int sbuf_slot) { + SOKOL_ASSERT((sbuf_slot >= 0) && (sbuf_slot < 16)); + if (SG_SHADERSTAGE_VS == stage) { + return 32 + (uint32_t)sbuf_slot; + } else { + return 80 + (uint32_t)sbuf_slot; + } +} + +_SOKOL_PRIVATE WGPUShaderStage _sg_wgpu_shader_stage(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VS: return WGPUShaderStage_Vertex; case SG_SHADERSTAGE_FS: return WGPUShaderStage_Fragment; default: SOKOL_UNREACHABLE; return WGPUShaderStage_None; } @@ -12963,10 +13755,8 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg.features.image_clamp_to_border = false; _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; - // FIXME: in webgpu.h, wgpuDeviceGetLimits() has a boolean return value, but - // the JS shim doesn't actually return anything - // (see: https://github.com/emscripten-core/emscripten/issues/20278) wgpuDeviceGetLimits(_sg.wgpu.dev, &_sg.wgpu.limits); const WGPULimits* l = &_sg.wgpu.limits.limits; @@ -13016,10 +13806,15 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); - // FIXME: can be made filterable with extension - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_Float32Filterable)) { + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); @@ -13030,6 +13825,7 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); @@ -13037,13 +13833,23 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); } if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionETC2)) { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); + } + + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionASTC)) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); } } @@ -13243,37 +14049,70 @@ _SOKOL_PRIVATE uint64_t _sg_wgpu_hash(const void* key, int len, uint64_t seed) { return h; } +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_item(_sg_wgpu_bindgroups_cache_item_type_t type, uint32_t id) { + return (((uint64_t)type) << 32) | id; +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_pip_item(uint32_t id) { + return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE, id); +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_image_item(uint32_t id) { + return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_IMAGE, id); +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_sampler_item(uint32_t id) { + return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER, id); +} + +_SOKOL_PRIVATE uint64_t _sg_wgpu_bindgroups_cache_sbuf_item(uint32_t id) { + return _sg_wgpu_bindgroups_cache_item(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_STORAGEBUFFER, id); +} + _SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* key, const _sg_bindings_t* bnd) { SOKOL_ASSERT(bnd); SOKOL_ASSERT(bnd->pip); SOKOL_ASSERT(bnd->num_vs_imgs <= SG_MAX_SHADERSTAGE_IMAGES); SOKOL_ASSERT(bnd->num_vs_smps <= SG_MAX_SHADERSTAGE_SAMPLERS); + SOKOL_ASSERT(bnd->num_vs_sbufs <= SG_MAX_SHADERSTAGE_STORAGEBUFFERS); SOKOL_ASSERT(bnd->num_fs_imgs <= SG_MAX_SHADERSTAGE_IMAGES); SOKOL_ASSERT(bnd->num_fs_smps <= SG_MAX_SHADERSTAGE_SAMPLERS); + SOKOL_ASSERT(bnd->num_fs_sbufs <= SG_MAX_SHADERSTAGE_STORAGEBUFFERS); _sg_clear(key->items, sizeof(key->items)); - key->items[0] = bnd->pip->slot.id; - const int vs_imgs_offset = 1; - const int vs_smps_offset = vs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; - const int fs_imgs_offset = vs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS; - const int fs_smps_offset = fs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; - SOKOL_ASSERT((fs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS) == _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS); + int item_idx = 0; + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_pip_item(bnd->pip->slot.id); for (int i = 0; i < bnd->num_vs_imgs; i++) { SOKOL_ASSERT(bnd->vs_imgs[i]); - key->items[vs_imgs_offset + i] = bnd->vs_imgs[i]->slot.id; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_image_item(bnd->vs_imgs[i]->slot.id); } for (int i = 0; i < bnd->num_vs_smps; i++) { SOKOL_ASSERT(bnd->vs_smps[i]); - key->items[vs_smps_offset + i] = bnd->vs_smps[i]->slot.id; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sampler_item(bnd->vs_smps[i]->slot.id); + } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + SOKOL_ASSERT(bnd->vs_sbufs[i]); + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sbuf_item(bnd->vs_sbufs[i]->slot.id); } for (int i = 0; i < bnd->num_fs_imgs; i++) { SOKOL_ASSERT(bnd->fs_imgs[i]); - key->items[fs_imgs_offset + i] = bnd->fs_imgs[i]->slot.id; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_image_item(bnd->fs_imgs[i]->slot.id); } for (int i = 0; i < bnd->num_fs_smps; i++) { SOKOL_ASSERT(bnd->fs_smps[i]); - key->items[fs_smps_offset + i] = bnd->fs_smps[i]->slot.id; + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sampler_item(bnd->fs_smps[i]->slot.id); + } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + SOKOL_ASSERT(bnd->fs_sbufs[i]); + SOKOL_ASSERT(item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS); + key->items[item_idx++] = _sg_wgpu_bindgroups_cache_sbuf_item(bnd->fs_sbufs[i]->slot.id); } + SOKOL_ASSERT(item_idx == (1 + bnd->num_vs_imgs + bnd->num_vs_smps + bnd->num_vs_sbufs + bnd->num_fs_imgs + bnd->num_fs_smps + bnd->num_fs_sbufs)); key->hash = _sg_wgpu_hash(&key->items, (int)sizeof(key->items), 0x1234567887654321); } @@ -13304,7 +14143,7 @@ _SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_create_bindgroup(_sg_bindings_t* b // create wgpu bindgroup object WGPUBindGroupLayout bgl = bnd->pip->shader->wgpu.bind_group_layout; SOKOL_ASSERT(bgl); - WGPUBindGroupEntry wgpu_entries[SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS]; + WGPUBindGroupEntry wgpu_entries[_SG_WGPU_MAX_BINDGROUP_ENTRIES]; _sg_clear(&wgpu_entries, sizeof(wgpu_entries)); int bge_index = 0; for (int i = 0; i < bnd->num_vs_imgs; i++) { @@ -13317,6 +14156,12 @@ _SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_create_bindgroup(_sg_bindings_t* b wgpu_entry->binding = _sg_wgpu_sampler_binding(SG_SHADERSTAGE_VS, i); wgpu_entry->sampler = bnd->vs_smps[i]->wgpu.smp; } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_storagebuffer_binding(SG_SHADERSTAGE_VS, i); + wgpu_entry->buffer = bnd->vs_sbufs[i]->wgpu.buf; + wgpu_entry->size = (uint64_t) bnd->vs_sbufs[i]->cmn.size; + } for (int i = 0; i < bnd->num_fs_imgs; i++) { WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; wgpu_entry->binding = _sg_wgpu_image_binding(SG_SHADERSTAGE_FS, i); @@ -13327,6 +14172,12 @@ _SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_create_bindgroup(_sg_bindings_t* b wgpu_entry->binding = _sg_wgpu_sampler_binding(SG_SHADERSTAGE_FS, i); wgpu_entry->sampler = bnd->fs_smps[i]->wgpu.smp; } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_storagebuffer_binding(SG_SHADERSTAGE_FS, i); + wgpu_entry->buffer = bnd->fs_sbufs[i]->wgpu.buf; + wgpu_entry->size = (uint64_t) bnd->fs_sbufs[i]->cmn.size; + } WGPUBindGroupDescriptor bg_desc; _sg_clear(&bg_desc, sizeof(bg_desc)); bg_desc.layout = bgl; @@ -13413,6 +14264,33 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_bindgroups_cache_get(uint64_t hash) { return _sg.wgpu.bindgroups_cache.items[index].id; } +// called from wgpu resource destroy functions to also invalidate any +// bindgroups cache slot and bindgroup referencing that resource +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_invalidate(_sg_wgpu_bindgroups_cache_item_type_t type, uint32_t id) { + const uint64_t key_item = _sg_wgpu_bindgroups_cache_item(type, id); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items); + for (uint32_t cache_item_idx = 0; cache_item_idx < _sg.wgpu.bindgroups_cache.num; cache_item_idx++) { + const uint32_t bg_id = _sg.wgpu.bindgroups_cache.items[cache_item_idx].id; + if (bg_id != SG_INVALID_ID) { + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_lookup_bindgroup(bg_id); + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_VALID)); + // check if resource is in bindgroup, if yes discard bindgroup and invalidate cache slot + bool invalidate_cache_item = false; + for (int key_item_idx = 0; key_item_idx < _SG_WGPU_BINDGROUPSCACHEKEY_NUM_ITEMS; key_item_idx++) { + if (bg->key.items[key_item_idx] == key_item) { + invalidate_cache_item = true; + break; + } + } + if (invalidate_cache_item) { + _sg_wgpu_discard_bindgroup(bg); bg = 0; + _sg_wgpu_bindgroups_cache_set(cache_item_idx, SG_INVALID_ID); + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_invalidates, 1); + } + } + } +} + _SOKOL_PRIVATE void _sg_wgpu_bindings_cache_clear(void) { memset(&_sg.wgpu.bindings_cache, 0, sizeof(_sg.wgpu.bindings_cache)); } @@ -13473,7 +14351,7 @@ _SOKOL_PRIVATE void _sg_wgpu_bindings_cache_bg_update(const _sg_wgpu_bindgroup_t } } -_SOKOL_PRIVATE void _sg_wgpu_set_image_sampler_bindgroup(_sg_wgpu_bindgroup_t* bg) { +_SOKOL_PRIVATE void _sg_wgpu_set_bindgroup(_sg_wgpu_bindgroup_t* bg) { if (_sg_wgpu_bindings_cache_bg_dirty(bg)) { _sg_wgpu_bindings_cache_bg_update(bg); _sg_stats_add(wgpu.bindings.num_set_bindgroup, 1); @@ -13491,7 +14369,7 @@ _SOKOL_PRIVATE void _sg_wgpu_set_image_sampler_bindgroup(_sg_wgpu_bindgroup_t* b } _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { - if ((bnd->num_vs_imgs + bnd->num_vs_smps + bnd->num_fs_imgs + bnd->num_fs_smps) > 0) { + if ((bnd->num_vs_imgs + bnd->num_vs_smps + bnd->num_vs_sbufs + bnd->num_fs_imgs + bnd->num_fs_smps + bnd->num_fs_sbufs) > 0) { if (!_sg.desc.wgpu_disable_bindgroups_cache) { _sg_wgpu_bindgroup_t* bg = 0; _sg_wgpu_bindgroups_cache_key_t key; @@ -13519,7 +14397,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { _sg_wgpu_bindgroups_cache_set(key.hash, bg->slot.id); } if (bg && bg->slot.state == SG_RESOURCESTATE_VALID) { - _sg_wgpu_set_image_sampler_bindgroup(bg); + _sg_wgpu_set_bindgroup(bg); } else { return false; } @@ -13528,7 +14406,7 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { _sg_wgpu_bindgroup_t* bg = _sg_wgpu_create_bindgroup(bnd); if (bg) { if (bg->slot.state == SG_RESOURCESTATE_VALID) { - _sg_wgpu_set_image_sampler_bindgroup(bg); + _sg_wgpu_set_bindgroup(bg); } _sg_wgpu_discard_bindgroup(bg); } else { @@ -13536,10 +14414,11 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { } } } else { - _sg_wgpu_set_image_sampler_bindgroup(0); + _sg_wgpu_set_bindgroup(0); } return true; } + _SOKOL_PRIVATE bool _sg_wgpu_apply_index_buffer(_sg_bindings_t* bnd) { if (_sg_wgpu_bindings_cache_ib_dirty(bnd->ib, bnd->ib_offset)) { _sg_wgpu_bindings_cache_ib_update(bnd->ib, bnd->ib_offset); @@ -13579,21 +14458,11 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_vertex_buffers(_sg_bindings_t* bnd) { _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { SOKOL_ASSERT(desc); - SOKOL_ASSERT(desc->context.wgpu.device); - SOKOL_ASSERT(desc->context.wgpu.render_view_cb || desc->context.wgpu.render_view_userdata_cb); - SOKOL_ASSERT(desc->context.wgpu.resolve_view_cb || desc->context.wgpu.resolve_view_userdata_cb); - SOKOL_ASSERT(desc->context.wgpu.depth_stencil_view_cb || desc->context.wgpu.depth_stencil_view_userdata_cb); + SOKOL_ASSERT(desc->environment.wgpu.device); SOKOL_ASSERT(desc->uniform_buffer_size > 0); _sg.backend = SG_BACKEND_WGPU; _sg.wgpu.valid = true; - _sg.wgpu.dev = (WGPUDevice) desc->context.wgpu.device; - _sg.wgpu.render_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.render_view_cb; - _sg.wgpu.render_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.render_view_userdata_cb; - _sg.wgpu.resolve_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.resolve_view_cb; - _sg.wgpu.resolve_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.resolve_view_userdata_cb; - _sg.wgpu.depth_stencil_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.depth_stencil_view_cb; - _sg.wgpu.depth_stencil_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.depth_stencil_view_userdata_cb; - _sg.wgpu.user_data = desc->context.wgpu.user_data; + _sg.wgpu.dev = (WGPUDevice) desc->environment.wgpu.device; _sg.wgpu.queue = wgpuDeviceGetQueue(_sg.wgpu.dev); SOKOL_ASSERT(_sg.wgpu.queue); @@ -13640,22 +14509,6 @@ _SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) { _sg_wgpu_bindings_cache_clear(); } -_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); - return SG_RESOURCESTATE_VALID; -} - -_SOKOL_PRIVATE void _sg_wgpu_discard_context(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _SOKOL_UNUSED(ctx); -} - -_SOKOL_PRIVATE void _sg_wgpu_activate_context(_sg_context_t* ctx) { - (void)ctx; - // FIXME -} - _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); const bool injected = (0 != desc->wgpu_buffer); @@ -13694,8 +14547,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const _SOKOL_PRIVATE void _sg_wgpu_discard_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); + if (buf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER) { + _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_STORAGEBUFFER, buf->slot.id); + } if (buf->wgpu.buf) { - wgpuBufferDestroy(buf->wgpu.buf); wgpuBufferRelease(buf->wgpu.buf); } } @@ -13772,6 +14627,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s if (injected) { img->wgpu.tex = (WGPUTexture)desc->wgpu_texture; wgpuTextureReference(img->wgpu.tex); + img->wgpu.view = (WGPUTextureView)desc->wgpu_texture_view; + if (img->wgpu.view) { + wgpuTextureViewReference(img->wgpu.view); + } } else { WGPUTextureDescriptor wgpu_tex_desc; _sg_clear(&wgpu_tex_desc, sizeof(wgpu_tex_desc)); @@ -13799,40 +14658,40 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { _sg_wgpu_copy_image_data(img, img->wgpu.tex, &desc->data); } - } - WGPUTextureViewDescriptor wgpu_texview_desc; - _sg_clear(&wgpu_texview_desc, sizeof(wgpu_texview_desc)); - wgpu_texview_desc.label = desc->label; - wgpu_texview_desc.dimension = _sg_wgpu_texture_view_dimension(img->cmn.type); - wgpu_texview_desc.mipLevelCount = (uint32_t)img->cmn.num_mipmaps; - if (img->cmn.type == SG_IMAGETYPE_CUBE) { - wgpu_texview_desc.arrayLayerCount = 6; - } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { - wgpu_texview_desc.arrayLayerCount = (uint32_t)img->cmn.num_slices; - } else { - wgpu_texview_desc.arrayLayerCount = 1; - } - if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { - wgpu_texview_desc.aspect = WGPUTextureAspect_DepthOnly; - } else { - wgpu_texview_desc.aspect = WGPUTextureAspect_All; - } - img->wgpu.view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_texview_desc); - if (0 == img->wgpu.view) { - _SG_ERROR(WGPU_CREATE_TEXTURE_VIEW_FAILED); - return SG_RESOURCESTATE_FAILED; + WGPUTextureViewDescriptor wgpu_texview_desc; + _sg_clear(&wgpu_texview_desc, sizeof(wgpu_texview_desc)); + wgpu_texview_desc.label = desc->label; + wgpu_texview_desc.dimension = _sg_wgpu_texture_view_dimension(img->cmn.type); + wgpu_texview_desc.mipLevelCount = (uint32_t)img->cmn.num_mipmaps; + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + wgpu_texview_desc.arrayLayerCount = 6; + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + wgpu_texview_desc.arrayLayerCount = (uint32_t)img->cmn.num_slices; + } else { + wgpu_texview_desc.arrayLayerCount = 1; + } + if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { + wgpu_texview_desc.aspect = WGPUTextureAspect_DepthOnly; + } else { + wgpu_texview_desc.aspect = WGPUTextureAspect_All; + } + img->wgpu.view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_texview_desc); + if (0 == img->wgpu.view) { + _SG_ERROR(WGPU_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } } return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) { SOKOL_ASSERT(img); + _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_IMAGE, img->slot.id); if (img->wgpu.view) { wgpuTextureViewRelease(img->wgpu.view); img->wgpu.view = 0; } if (img->wgpu.tex) { - wgpuTextureDestroy(img->wgpu.tex); wgpuTextureRelease(img->wgpu.tex); img->wgpu.tex = 0; } @@ -13873,6 +14732,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_sampler(_sg_sampler_t* smp, con _SOKOL_PRIVATE void _sg_wgpu_discard_sampler(_sg_sampler_t* smp) { SOKOL_ASSERT(smp); + _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_SAMPLER, smp->slot.id); if (smp->wgpu.smp) { wgpuSamplerRelease(smp->wgpu.smp); smp->wgpu.smp = 0; @@ -13883,8 +14743,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const SOKOL_ASSERT(shd && desc); SOKOL_ASSERT(desc->vs.source && desc->fs.source); - #define _sg_wgpu_create_shader_max_bgl_entries (SG_NUM_SHADER_STAGES * (SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS)) - WGPUBindGroupLayoutEntry wgpu_bgl_entries[_sg_wgpu_create_shader_max_bgl_entries]; + WGPUBindGroupLayoutEntry wgpu_bgl_entries[_SG_WGPU_MAX_BINDGROUP_ENTRIES]; _sg_clear(wgpu_bgl_entries, sizeof(wgpu_bgl_entries)); int bgl_index = 0; for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { @@ -13920,8 +14779,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const _SG_ERROR(WGPU_SHADER_TOO_MANY_SAMPLERS); return SG_RESOURCESTATE_FAILED; } + const int num_sbufs = cmn_stage->num_storage_buffers; + if (num_sbufs > (int)_sg.wgpu.limits.limits.maxStorageBuffersPerShaderStage) { + _SG_ERROR(WGPU_SHADER_TOO_MANY_STORAGEBUFFERS); + return SG_RESOURCESTATE_FAILED; + } for (int img_index = 0; img_index < num_images; img_index++) { - SOKOL_ASSERT(bgl_index < _sg_wgpu_create_shader_max_bgl_entries); + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; wgpu_bgl_entry->binding = _sg_wgpu_image_binding((sg_shader_stage)stage_index, img_index); @@ -13931,13 +14795,21 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const wgpu_bgl_entry->texture.multisampled = img_desc->multisampled; } for (int smp_index = 0; smp_index < num_samplers; smp_index++) { - SOKOL_ASSERT(bgl_index < _sg_wgpu_create_shader_max_bgl_entries); + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; - wgpu_bgl_entry->binding =_sg_wgpu_sampler_binding((sg_shader_stage)stage_index, smp_index); + wgpu_bgl_entry->binding = _sg_wgpu_sampler_binding((sg_shader_stage)stage_index, smp_index); wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); wgpu_bgl_entry->sampler.type = _sg_wgpu_sampler_binding_type(smp_desc->sampler_type); } + for (int sbuf_index = 0; sbuf_index < num_sbufs; sbuf_index++) { + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); + WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + wgpu_bgl_entry->binding = _sg_wgpu_storagebuffer_binding((sg_shader_stage)stage_index, sbuf_index); + wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); + wgpu_bgl_entry->buffer.type = sbuf_desc->readonly ? WGPUBufferBindingType_ReadOnlyStorage : WGPUBufferBindingType_Storage; + } } WGPUBindGroupLayoutDescriptor wgpu_bgl_desc; @@ -13949,8 +14821,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const _SG_ERROR(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED); return SG_RESOURCESTATE_FAILED; } - - #undef _sg_wgpu_create_shader_max_bgl_entries return SG_RESOURCESTATE_VALID; } @@ -14100,6 +14970,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ _SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); + _sg_wgpu_bindgroups_cache_invalidate(_SG_WGPU_BINDGROUPSCACHEITEMTYPE_PIPELINE, pip->slot.id); if (pip == _sg.wgpu.cur_pipeline) { _sg.wgpu.cur_pipeline = 0; _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; @@ -14110,20 +14981,20 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) { } } -_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && desc); +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); SOKOL_ASSERT(color_images && resolve_images); // copy image pointers and create renderable wgpu texture views - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - const sg_pass_attachment_desc* color_desc = &desc->color_attachments[i]; + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; _SOKOL_UNUSED(color_desc); SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); - SOKOL_ASSERT(0 == pass->wgpu.color_atts[i].image); + SOKOL_ASSERT(0 == atts->wgpu.colors[i].image); SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); SOKOL_ASSERT(color_images[i]->wgpu.tex); - pass->wgpu.color_atts[i].image = color_images[i]; + atts->wgpu.colors[i].image = color_images[i]; WGPUTextureViewDescriptor wgpu_color_view_desc; _sg_clear(&wgpu_color_view_desc, sizeof(wgpu_color_view_desc)); @@ -14131,19 +15002,19 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag wgpu_color_view_desc.mipLevelCount = 1; wgpu_color_view_desc.baseArrayLayer = (uint32_t) color_desc->slice; wgpu_color_view_desc.arrayLayerCount = 1; - pass->wgpu.color_atts[i].view = wgpuTextureCreateView(color_images[i]->wgpu.tex, &wgpu_color_view_desc); - if (0 == pass->wgpu.color_atts[i].view) { - _SG_ERROR(WGPU_PASS_CREATE_TEXTURE_VIEW_FAILED); + atts->wgpu.colors[i].view = wgpuTextureCreateView(color_images[i]->wgpu.tex, &wgpu_color_view_desc); + if (0 == atts->wgpu.colors[i].view) { + _SG_ERROR(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED); return SG_RESOURCESTATE_FAILED; } - const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; if (resolve_desc->image.id != SG_INVALID_ID) { - SOKOL_ASSERT(0 == pass->wgpu.resolve_atts[i].image); + SOKOL_ASSERT(0 == atts->wgpu.resolves[i].image); SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); SOKOL_ASSERT(resolve_images[i]->wgpu.tex); - pass->wgpu.resolve_atts[i].image = resolve_images[i]; + atts->wgpu.resolves[i].image = resolve_images[i]; WGPUTextureViewDescriptor wgpu_resolve_view_desc; _sg_clear(&wgpu_resolve_view_desc, sizeof(wgpu_resolve_view_desc)); @@ -14151,20 +15022,20 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag wgpu_resolve_view_desc.mipLevelCount = 1; wgpu_resolve_view_desc.baseArrayLayer = (uint32_t) resolve_desc->slice; wgpu_resolve_view_desc.arrayLayerCount = 1; - pass->wgpu.resolve_atts[i].view = wgpuTextureCreateView(resolve_images[i]->wgpu.tex, &wgpu_resolve_view_desc); - if (0 == pass->wgpu.resolve_atts[i].view) { - _SG_ERROR(WGPU_PASS_CREATE_TEXTURE_VIEW_FAILED); + atts->wgpu.resolves[i].view = wgpuTextureCreateView(resolve_images[i]->wgpu.tex, &wgpu_resolve_view_desc); + if (0 == atts->wgpu.resolves[i].view) { + _SG_ERROR(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED); return SG_RESOURCESTATE_FAILED; } } } - SOKOL_ASSERT(0 == pass->wgpu.ds_att.image); - const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + SOKOL_ASSERT(0 == atts->wgpu.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; if (ds_desc->image.id != SG_INVALID_ID) { SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); SOKOL_ASSERT(ds_img->wgpu.tex); - pass->wgpu.ds_att.image = ds_img; + atts->wgpu.depth_stencil.image = ds_img; WGPUTextureViewDescriptor wgpu_ds_view_desc; _sg_clear(&wgpu_ds_view_desc, sizeof(wgpu_ds_view_desc)); @@ -14172,52 +15043,53 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag wgpu_ds_view_desc.mipLevelCount = 1; wgpu_ds_view_desc.baseArrayLayer = (uint32_t) ds_desc->slice; wgpu_ds_view_desc.arrayLayerCount = 1; - pass->wgpu.ds_att.view = wgpuTextureCreateView(ds_img->wgpu.tex, &wgpu_ds_view_desc); - if (0 == pass->wgpu.ds_att.view) { - _SG_ERROR(WGPU_PASS_CREATE_TEXTURE_VIEW_FAILED); + atts->wgpu.depth_stencil.view = wgpuTextureCreateView(ds_img->wgpu.tex, &wgpu_ds_view_desc); + if (0 == atts->wgpu.depth_stencil.view) { + _SG_ERROR(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED); return SG_RESOURCESTATE_FAILED; } } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_wgpu_discard_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - if (pass->wgpu.color_atts[i].view) { - wgpuTextureViewRelease(pass->wgpu.color_atts[i].view); - pass->wgpu.color_atts[i].view = 0; +_SOKOL_PRIVATE void _sg_wgpu_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + for (int i = 0; i < atts->cmn.num_colors; i++) { + if (atts->wgpu.colors[i].view) { + wgpuTextureViewRelease(atts->wgpu.colors[i].view); + atts->wgpu.colors[i].view = 0; } - if (pass->wgpu.resolve_atts[i].view) { - wgpuTextureViewRelease(pass->wgpu.resolve_atts[i].view); - pass->wgpu.resolve_atts[i].view = 0; + if (atts->wgpu.resolves[i].view) { + wgpuTextureViewRelease(atts->wgpu.resolves[i].view); + atts->wgpu.resolves[i].view = 0; } } - if (pass->wgpu.ds_att.view) { - wgpuTextureViewRelease(pass->wgpu.ds_att.view); - pass->wgpu.ds_att.view = 0; + if (atts->wgpu.depth_stencil.view) { + wgpuTextureViewRelease(atts->wgpu.depth_stencil.view); + atts->wgpu.depth_stencil.view = 0; } } -_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_color_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); // NOTE: may return null - return pass->wgpu.color_atts[index].image; + return atts->wgpu.colors[index].image; } -_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_resolve_image(const _sg_pass_t* pass, int index) { - SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); // NOTE: may return null - return pass->wgpu.resolve_atts[index].image; + return atts->wgpu.resolves[index].image; } -_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_ds_image(const _sg_pass_t* pass) { +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_attachments_ds_image(const _sg_attachments_t* atts) { // NOTE: may return null - SOKOL_ASSERT(pass); - return pass->wgpu.ds_att.image; + SOKOL_ASSERT(atts); + return atts->wgpu.depth_stencil.image; } _SOKOL_PRIVATE void _sg_wgpu_init_color_att(WGPURenderPassColorAttachment* wgpu_att, const sg_color_attachment_action* action, WGPUTextureView color_view, WGPUTextureView resolve_view) { + wgpu_att->depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; wgpu_att->view = color_view; wgpu_att->resolveTarget = resolve_view; wgpu_att->loadOp = _sg_wgpu_load_op(color_view, action->load_action); @@ -14245,17 +15117,15 @@ _SOKOL_PRIVATE void _sg_wgpu_init_ds_att(WGPURenderPassDepthStencilAttachment* w wgpu_att->stencilReadOnly = false; } -_SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { - SOKOL_ASSERT(action); - SOKOL_ASSERT(!_sg.wgpu.in_pass); +_SOKOL_PRIVATE void _sg_wgpu_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); SOKOL_ASSERT(_sg.wgpu.cmd_enc); SOKOL_ASSERT(_sg.wgpu.dev); - SOKOL_ASSERT(_sg.wgpu.render_view_cb || _sg.wgpu.render_view_userdata_cb); - SOKOL_ASSERT(_sg.wgpu.resolve_view_cb || _sg.wgpu.resolve_view_userdata_cb); - SOKOL_ASSERT(_sg.wgpu.depth_stencil_view_cb || _sg.wgpu.depth_stencil_view_userdata_cb); - _sg.wgpu.in_pass = true; - _sg.wgpu.cur_width = w; - _sg.wgpu.cur_height = h; + + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + _sg.wgpu.cur_pipeline = 0; _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; @@ -14265,28 +15135,30 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* _sg_clear(&wgpu_pass_desc, sizeof(wgpu_pass_desc)); _sg_clear(&wgpu_color_att, sizeof(wgpu_color_att)); _sg_clear(&wgpu_ds_att, sizeof(wgpu_ds_att)); - if (pass) { - SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - _sg_wgpu_init_color_att(&wgpu_color_att[i], &action->colors[i], pass->wgpu.color_atts[i].view, pass->wgpu.resolve_atts[i].view); + wgpu_pass_desc.label = pass->label; + if (atts) { + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_VALID); + for (int i = 0; i < atts->cmn.num_colors; i++) { + _sg_wgpu_init_color_att(&wgpu_color_att[i], &action->colors[i], atts->wgpu.colors[i].view, atts->wgpu.resolves[i].view); } - wgpu_pass_desc.colorAttachmentCount = (size_t)pass->cmn.num_color_atts; + wgpu_pass_desc.colorAttachmentCount = (size_t)atts->cmn.num_colors; wgpu_pass_desc.colorAttachments = &wgpu_color_att[0]; - if (pass->wgpu.ds_att.image) { - _sg_wgpu_init_ds_att(&wgpu_ds_att, action, pass->wgpu.ds_att.image->cmn.pixel_format, pass->wgpu.ds_att.view); + if (atts->wgpu.depth_stencil.image) { + _sg_wgpu_init_ds_att(&wgpu_ds_att, action, atts->wgpu.depth_stencil.image->cmn.pixel_format, atts->wgpu.depth_stencil.view); wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att; } } else { - WGPUTextureView wgpu_color_view = _sg.wgpu.render_view_cb ? _sg.wgpu.render_view_cb() : _sg.wgpu.render_view_userdata_cb(_sg.wgpu.user_data); - WGPUTextureView wgpu_resolve_view = _sg.wgpu.resolve_view_cb ? _sg.wgpu.resolve_view_cb() : _sg.wgpu.resolve_view_userdata_cb(_sg.wgpu.user_data); - WGPUTextureView wgpu_depth_stencil_view = _sg.wgpu.depth_stencil_view_cb ? _sg.wgpu.depth_stencil_view_cb() : _sg.wgpu.depth_stencil_view_userdata_cb(_sg.wgpu.user_data); + WGPUTextureView wgpu_color_view = (WGPUTextureView) swapchain->wgpu.render_view; + WGPUTextureView wgpu_resolve_view = (WGPUTextureView) swapchain->wgpu.resolve_view; + WGPUTextureView wgpu_depth_stencil_view = (WGPUTextureView) swapchain->wgpu.depth_stencil_view; _sg_wgpu_init_color_att(&wgpu_color_att[0], &action->colors[0], wgpu_color_view, wgpu_resolve_view); wgpu_pass_desc.colorAttachmentCount = 1; wgpu_pass_desc.colorAttachments = &wgpu_color_att[0]; if (wgpu_depth_stencil_view) { - _sg_wgpu_init_ds_att(&wgpu_ds_att, action, _sg.desc.context.depth_format, wgpu_depth_stencil_view); + SOKOL_ASSERT(swapchain->depth_format > SG_PIXELFORMAT_NONE); + _sg_wgpu_init_ds_att(&wgpu_ds_att, action, swapchain->depth_format, wgpu_depth_stencil_view); + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att; } - wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att; } _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.cmd_enc, &wgpu_pass_desc); SOKOL_ASSERT(_sg.wgpu.pass_enc); @@ -14301,16 +15173,14 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* } _SOKOL_PRIVATE void _sg_wgpu_end_pass(void) { - SOKOL_ASSERT(_sg.wgpu.in_pass); - SOKOL_ASSERT(_sg.wgpu.pass_enc); - _sg.wgpu.in_pass = false; - wgpuRenderPassEncoderEnd(_sg.wgpu.pass_enc); - wgpuRenderPassEncoderRelease(_sg.wgpu.pass_enc); - _sg.wgpu.pass_enc = 0; + if (_sg.wgpu.pass_enc) { + wgpuRenderPassEncoderEnd(_sg.wgpu.pass_enc); + wgpuRenderPassEncoderRelease(_sg.wgpu.pass_enc); + _sg.wgpu.pass_enc = 0; + } } _SOKOL_PRIVATE void _sg_wgpu_commit(void) { - SOKOL_ASSERT(!_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.cmd_enc); _sg_wgpu_uniform_buffer_on_commit(); @@ -14332,26 +15202,24 @@ _SOKOL_PRIVATE void _sg_wgpu_commit(void) { } _SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); // FIXME FIXME FIXME: CLIPPING THE VIEWPORT HERE IS WRONG!!! // (but currently required because WebGPU insists that the viewport rectangle must be // fully contained inside the framebuffer, but this doesn't make any sense, and also // isn't required by the backend APIs) - const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.wgpu.cur_width, _sg.wgpu.cur_height); + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.width, _sg.cur_pass.height); float xf = (float) clip.x; - float yf = (float) (origin_top_left ? clip.y : (_sg.wgpu.cur_height - (clip.y + clip.h))); + float yf = (float) (origin_top_left ? clip.y : (_sg.cur_pass.height - (clip.y + clip.h))); float wf = (float) clip.w; float hf = (float) clip.h; wgpuRenderPassEncoderSetViewport(_sg.wgpu.pass_enc, xf, yf, wf, hf, 0.0f, 1.0f); } _SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { - SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); - const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.wgpu.cur_width, _sg.wgpu.cur_height); + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.width, _sg.cur_pass.height); uint32_t sx = (uint32_t) clip.x; - uint32_t sy = (uint32_t) (origin_top_left ? clip.y : (_sg.wgpu.cur_height - (clip.y + clip.h))); + uint32_t sy = (uint32_t) (origin_top_left ? clip.y : (_sg.cur_pass.height - (clip.y + clip.h))); uint32_t sw = (uint32_t) clip.w; uint32_t sh = (uint32_t) clip.h; wgpuRenderPassEncoderSetScissorRect(_sg.wgpu.pass_enc, sx, sy, sw, sh); @@ -14360,7 +15228,6 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool _SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); SOKOL_ASSERT(pip->wgpu.pip); - SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); _sg.wgpu.use_indexed_draw = (pip->cmn.index_type != SG_INDEXTYPE_NONE); _sg.wgpu.cur_pipeline = pip; @@ -14371,7 +15238,6 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) { } _SOKOL_PRIVATE bool _sg_wgpu_apply_bindings(_sg_bindings_t* bnd) { - SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); SOKOL_ASSERT(bnd); SOKOL_ASSERT(bnd->pip->shader && (bnd->pip->cmn.shader_id.id == bnd->pip->shader->slot.id)); @@ -14384,7 +15250,6 @@ _SOKOL_PRIVATE bool _sg_wgpu_apply_bindings(_sg_bindings_t* bnd) { _SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { const uint32_t alignment = _sg.wgpu.limits.limits.minUniformBufferOffsetAlignment; - SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); SOKOL_ASSERT(_sg.wgpu.uniform.staging); SOKOL_ASSERT((_sg.wgpu.uniform.offset + data->size) <= _sg.wgpu.uniform.num_bytes); @@ -14408,7 +15273,6 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(sg_shader_stage stage_index, int ub_ } _SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_instances) { - SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); SOKOL_ASSERT(_sg.wgpu.cur_pipeline && (_sg.wgpu.cur_pipeline->slot.id == _sg.wgpu.cur_pipeline_id.id)); if (SG_INDEXTYPE_NONE != _sg.wgpu.cur_pipeline->cmn.index_type) { @@ -14491,54 +15355,6 @@ static inline void _sg_reset_state_cache(void) { #endif } -static inline void _sg_activate_context(_sg_context_t* ctx) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_activate_context(ctx); - #elif defined(SOKOL_METAL) - _sg_mtl_activate_context(ctx); - #elif defined(SOKOL_D3D11) - _sg_d3d11_activate_context(ctx); - #elif defined(SOKOL_WGPU) - _sg_wgpu_activate_context(ctx); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_activate_context(ctx); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline sg_resource_state _sg_create_context(_sg_context_t* ctx) { - #if defined(_SOKOL_ANY_GL) - return _sg_gl_create_context(ctx); - #elif defined(SOKOL_METAL) - return _sg_mtl_create_context(ctx); - #elif defined(SOKOL_D3D11) - return _sg_d3d11_create_context(ctx); - #elif defined(SOKOL_WGPU) - return _sg_wgpu_create_context(ctx); - #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_create_context(ctx); - #else - #error("INVALID BACKEND"); - #endif -} - -static inline void _sg_discard_context(_sg_context_t* ctx) { - #if defined(_SOKOL_ANY_GL) - _sg_gl_discard_context(ctx); - #elif defined(SOKOL_METAL) - _sg_mtl_discard_context(ctx); - #elif defined(SOKOL_D3D11) - _sg_d3d11_discard_context(ctx); - #elif defined(SOKOL_WGPU) - _sg_wgpu_discard_context(ctx); - #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_discard_context(ctx); - #else - #error("INVALID BACKEND"); - #endif -} - static inline sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { #if defined(_SOKOL_ANY_GL) return _sg_gl_create_buffer(buf, desc); @@ -14699,97 +15515,97 @@ static inline void _sg_discard_pipeline(_sg_pipeline_t* pip) { #endif } -static inline sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_image, const sg_pass_desc* desc) { +static inline sg_resource_state _sg_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_image, const sg_attachments_desc* desc) { #if defined(_SOKOL_ANY_GL) - return _sg_gl_create_pass(pass, color_images, resolve_images, ds_image, desc); + return _sg_gl_create_attachments(atts, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_METAL) - return _sg_mtl_create_pass(pass, color_images, resolve_images, ds_image, desc); + return _sg_mtl_create_attachments(atts, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_D3D11) - return _sg_d3d11_create_pass(pass, color_images, resolve_images, ds_image, desc); + return _sg_d3d11_create_attachments(atts, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_WGPU) - return _sg_wgpu_create_pass(pass, color_images, resolve_images, ds_image, desc); + return _sg_wgpu_create_attachments(atts, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_create_pass(pass, color_images, resolve_images, ds_image, desc); + return _sg_dummy_create_attachments(atts, color_images, resolve_images, ds_image, desc); #else #error("INVALID BACKEND"); #endif } -static inline void _sg_discard_pass(_sg_pass_t* pass) { +static inline void _sg_discard_attachments(_sg_attachments_t* atts) { #if defined(_SOKOL_ANY_GL) - _sg_gl_discard_pass(pass); + _sg_gl_discard_attachments(atts); #elif defined(SOKOL_METAL) - _sg_mtl_discard_pass(pass); + _sg_mtl_discard_attachments(atts); #elif defined(SOKOL_D3D11) - _sg_d3d11_discard_pass(pass); + _sg_d3d11_discard_attachments(atts); #elif defined(SOKOL_WGPU) - return _sg_wgpu_discard_pass(pass); + return _sg_wgpu_discard_attachments(atts); #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_discard_pass(pass); + _sg_dummy_discard_attachments(atts); #else #error("INVALID BACKEND"); #endif } -static inline _sg_image_t* _sg_pass_color_image(const _sg_pass_t* pass, int index) { +static inline _sg_image_t* _sg_attachments_color_image(const _sg_attachments_t* atts, int index) { #if defined(_SOKOL_ANY_GL) - return _sg_gl_pass_color_image(pass, index); + return _sg_gl_attachments_color_image(atts, index); #elif defined(SOKOL_METAL) - return _sg_mtl_pass_color_image(pass, index); + return _sg_mtl_attachments_color_image(atts, index); #elif defined(SOKOL_D3D11) - return _sg_d3d11_pass_color_image(pass, index); + return _sg_d3d11_attachments_color_image(atts, index); #elif defined(SOKOL_WGPU) - return _sg_wgpu_pass_color_image(pass, index); + return _sg_wgpu_attachments_color_image(atts, index); #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_pass_color_image(pass, index); + return _sg_dummy_attachments_color_image(atts, index); #else #error("INVALID BACKEND"); #endif } -static inline _sg_image_t* _sg_pass_resolve_image(const _sg_pass_t* pass, int index) { +static inline _sg_image_t* _sg_attachments_resolve_image(const _sg_attachments_t* atts, int index) { #if defined(_SOKOL_ANY_GL) - return _sg_gl_pass_resolve_image(pass, index); + return _sg_gl_attachments_resolve_image(atts, index); #elif defined(SOKOL_METAL) - return _sg_mtl_pass_resolve_image(pass, index); + return _sg_mtl_attachments_resolve_image(atts, index); #elif defined(SOKOL_D3D11) - return _sg_d3d11_pass_resolve_image(pass, index); + return _sg_d3d11_attachments_resolve_image(atts, index); #elif defined(SOKOL_WGPU) - return _sg_wgpu_pass_resolve_image(pass, index); + return _sg_wgpu_attachments_resolve_image(atts, index); #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_pass_resolve_image(pass, index); + return _sg_dummy_attachments_resolve_image(atts, index); #else #error("INVALID BACKEND"); #endif } -static inline _sg_image_t* _sg_pass_ds_image(const _sg_pass_t* pass) { +static inline _sg_image_t* _sg_attachments_ds_image(const _sg_attachments_t* atts) { #if defined(_SOKOL_ANY_GL) - return _sg_gl_pass_ds_image(pass); + return _sg_gl_attachments_ds_image(atts); #elif defined(SOKOL_METAL) - return _sg_mtl_pass_ds_image(pass); + return _sg_mtl_attachments_ds_image(atts); #elif defined(SOKOL_D3D11) - return _sg_d3d11_pass_ds_image(pass); + return _sg_d3d11_attachments_ds_image(atts); #elif defined(SOKOL_WGPU) - return _sg_wgpu_pass_ds_image(pass); + return _sg_wgpu_attachments_ds_image(atts); #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_pass_ds_image(pass); + return _sg_dummy_attachments_ds_image(atts); #else #error("INVALID BACKEND"); #endif } -static inline void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { +static inline void _sg_begin_pass(const sg_pass* pass) { #if defined(_SOKOL_ANY_GL) - _sg_gl_begin_pass(pass, action, w, h); + _sg_gl_begin_pass(pass); #elif defined(SOKOL_METAL) - _sg_mtl_begin_pass(pass, action, w, h); + _sg_mtl_begin_pass(pass); #elif defined(SOKOL_D3D11) - _sg_d3d11_begin_pass(pass, action, w, h); + _sg_d3d11_begin_pass(pass); #elif defined(SOKOL_WGPU) - _sg_wgpu_begin_pass(pass, action, w, h); + _sg_wgpu_begin_pass(pass); #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_begin_pass(pass, action, w, h); + _sg_dummy_begin_pass(pass); #else #error("INVALID BACKEND"); #endif @@ -15056,7 +15872,7 @@ _SOKOL_PRIVATE void _sg_reset_slot(_sg_slot_t* slot) { _SOKOL_PRIVATE void _sg_reset_buffer_to_alloc_state(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); _sg_slot_t slot = buf->slot; - _sg_clear(buf, sizeof(_sg_buffer_t)); + _sg_clear(buf, sizeof(*buf)); buf->slot = slot; buf->slot.state = SG_RESOURCESTATE_ALLOC; } @@ -15064,7 +15880,7 @@ _SOKOL_PRIVATE void _sg_reset_buffer_to_alloc_state(_sg_buffer_t* buf) { _SOKOL_PRIVATE void _sg_reset_image_to_alloc_state(_sg_image_t* img) { SOKOL_ASSERT(img); _sg_slot_t slot = img->slot; - _sg_clear(img, sizeof(_sg_image_t)); + _sg_clear(img, sizeof(*img)); img->slot = slot; img->slot.state = SG_RESOURCESTATE_ALLOC; } @@ -15072,7 +15888,7 @@ _SOKOL_PRIVATE void _sg_reset_image_to_alloc_state(_sg_image_t* img) { _SOKOL_PRIVATE void _sg_reset_sampler_to_alloc_state(_sg_sampler_t* smp) { SOKOL_ASSERT(smp); _sg_slot_t slot = smp->slot; - _sg_clear(smp, sizeof(_sg_sampler_t)); + _sg_clear(smp, sizeof(*smp)); smp->slot = slot; smp->slot.state = SG_RESOURCESTATE_ALLOC; } @@ -15080,7 +15896,7 @@ _SOKOL_PRIVATE void _sg_reset_sampler_to_alloc_state(_sg_sampler_t* smp) { _SOKOL_PRIVATE void _sg_reset_shader_to_alloc_state(_sg_shader_t* shd) { SOKOL_ASSERT(shd); _sg_slot_t slot = shd->slot; - _sg_clear(shd, sizeof(_sg_shader_t)); + _sg_clear(shd, sizeof(*shd)); shd->slot = slot; shd->slot.state = SG_RESOURCESTATE_ALLOC; } @@ -15088,25 +15904,17 @@ _SOKOL_PRIVATE void _sg_reset_shader_to_alloc_state(_sg_shader_t* shd) { _SOKOL_PRIVATE void _sg_reset_pipeline_to_alloc_state(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); _sg_slot_t slot = pip->slot; - _sg_clear(pip, sizeof(_sg_pipeline_t)); + _sg_clear(pip, sizeof(*pip)); pip->slot = slot; pip->slot.state = SG_RESOURCESTATE_ALLOC; } -_SOKOL_PRIVATE void _sg_reset_pass_to_alloc_state(_sg_pass_t* pass) { - SOKOL_ASSERT(pass); - _sg_slot_t slot = pass->slot; - _sg_clear(pass, sizeof(_sg_pass_t)); - pass->slot = slot; - pass->slot.state = SG_RESOURCESTATE_ALLOC; -} - -_SOKOL_PRIVATE void _sg_reset_context_to_alloc_state(_sg_context_t* ctx) { - SOKOL_ASSERT(ctx); - _sg_slot_t slot = ctx->slot; - _sg_clear(ctx, sizeof(_sg_context_t)); - ctx->slot = slot; - ctx->slot.state = SG_RESOURCESTATE_ALLOC; +_SOKOL_PRIVATE void _sg_reset_attachments_to_alloc_state(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + _sg_slot_t slot = atts->slot; + _sg_clear(atts, sizeof(*atts)); + atts->slot = slot; + atts->slot.state = SG_RESOURCESTATE_ALLOC; } _SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { @@ -15138,28 +15946,21 @@ _SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { size_t pipeline_pool_byte_size = sizeof(_sg_pipeline_t) * (size_t)p->pipeline_pool.size; p->pipelines = (_sg_pipeline_t*) _sg_malloc_clear(pipeline_pool_byte_size); - SOKOL_ASSERT((desc->pass_pool_size > 0) && (desc->pass_pool_size < _SG_MAX_POOL_SIZE)); - _sg_init_pool(&p->pass_pool, desc->pass_pool_size); - size_t pass_pool_byte_size = sizeof(_sg_pass_t) * (size_t)p->pass_pool.size; - p->passes = (_sg_pass_t*) _sg_malloc_clear(pass_pool_byte_size); - - SOKOL_ASSERT((desc->context_pool_size > 0) && (desc->context_pool_size < _SG_MAX_POOL_SIZE)); - _sg_init_pool(&p->context_pool, desc->context_pool_size); - size_t context_pool_byte_size = sizeof(_sg_context_t) * (size_t)p->context_pool.size; - p->contexts = (_sg_context_t*) _sg_malloc_clear(context_pool_byte_size); + SOKOL_ASSERT((desc->attachments_pool_size > 0) && (desc->attachments_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->attachments_pool, desc->attachments_pool_size); + size_t attachments_pool_byte_size = sizeof(_sg_attachments_t) * (size_t)p->attachments_pool.size; + p->attachments = (_sg_attachments_t*) _sg_malloc_clear(attachments_pool_byte_size); } _SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { SOKOL_ASSERT(p); - _sg_free(p->contexts); p->contexts = 0; - _sg_free(p->passes); p->passes = 0; + _sg_free(p->attachments); p->attachments = 0; _sg_free(p->pipelines); p->pipelines = 0; _sg_free(p->shaders); p->shaders = 0; _sg_free(p->samplers); p->samplers = 0; _sg_free(p->images); p->images = 0; _sg_free(p->buffers); p->buffers = 0; - _sg_discard_pool(&p->context_pool); - _sg_discard_pool(&p->pass_pool); + _sg_discard_pool(&p->attachments_pool); _sg_discard_pool(&p->pipeline_pool); _sg_discard_pool(&p->shader_pool); _sg_discard_pool(&p->sampler_pool); @@ -15232,18 +16033,11 @@ _SOKOL_PRIVATE _sg_pipeline_t* _sg_pipeline_at(const _sg_pools_t* p, uint32_t pi return &p->pipelines[slot_index]; } -_SOKOL_PRIVATE _sg_pass_t* _sg_pass_at(const _sg_pools_t* p, uint32_t pass_id) { - SOKOL_ASSERT(p && (SG_INVALID_ID != pass_id)); - int slot_index = _sg_slot_index(pass_id); - SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pass_pool.size)); - return &p->passes[slot_index]; -} - -_SOKOL_PRIVATE _sg_context_t* _sg_context_at(const _sg_pools_t* p, uint32_t context_id) { - SOKOL_ASSERT(p && (SG_INVALID_ID != context_id)); - int slot_index = _sg_slot_index(context_id); - SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->context_pool.size)); - return &p->contexts[slot_index]; +_SOKOL_PRIVATE _sg_attachments_t* _sg_attachments_at(const _sg_pools_t* p, uint32_t atts_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != atts_id)); + int slot_index = _sg_slot_index(atts_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->attachments_pool.size)); + return &p->attachments[slot_index]; } // returns pointer to resource with matching id check, may return 0 @@ -15299,29 +16093,18 @@ _SOKOL_PRIVATE _sg_pipeline_t* _sg_lookup_pipeline(const _sg_pools_t* p, uint32_ return 0; } -_SOKOL_PRIVATE _sg_pass_t* _sg_lookup_pass(const _sg_pools_t* p, uint32_t pass_id) { +_SOKOL_PRIVATE _sg_attachments_t* _sg_lookup_attachments(const _sg_pools_t* p, uint32_t atts_id) { SOKOL_ASSERT(p); - if (SG_INVALID_ID != pass_id) { - _sg_pass_t* pass = _sg_pass_at(p, pass_id); - if (pass->slot.id == pass_id) { - return pass; + if (SG_INVALID_ID != atts_id) { + _sg_attachments_t* atts = _sg_attachments_at(p, atts_id); + if (atts->slot.id == atts_id) { + return atts; } } return 0; } -_SOKOL_PRIVATE _sg_context_t* _sg_lookup_context(const _sg_pools_t* p, uint32_t ctx_id) { - SOKOL_ASSERT(p); - if (SG_INVALID_ID != ctx_id) { - _sg_context_t* ctx = _sg_context_at(p, ctx_id); - if (ctx->slot.id == ctx_id) { - return ctx; - } - } - return 0; -} - -_SOKOL_PRIVATE void _sg_discard_all_resources(_sg_pools_t* p, uint32_t ctx_id) { +_SOKOL_PRIVATE void _sg_discard_all_resources(_sg_pools_t* p) { /* this is a bit dumb since it loops over all pool slots to find the occupied slots, on the other hand it is only ever executed at shutdown @@ -15330,51 +16113,39 @@ _SOKOL_PRIVATE void _sg_discard_all_resources(_sg_pools_t* p, uint32_t ctx_id) { and the resource slots not be cleared! */ for (int i = 1; i < p->buffer_pool.size; i++) { - if (p->buffers[i].slot.ctx_id == ctx_id) { - sg_resource_state state = p->buffers[i].slot.state; - if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_discard_buffer(&p->buffers[i]); - } + sg_resource_state state = p->buffers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_buffer(&p->buffers[i]); } } for (int i = 1; i < p->image_pool.size; i++) { - if (p->images[i].slot.ctx_id == ctx_id) { - sg_resource_state state = p->images[i].slot.state; - if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_discard_image(&p->images[i]); - } + sg_resource_state state = p->images[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_image(&p->images[i]); } } for (int i = 1; i < p->sampler_pool.size; i++) { - if (p->samplers[i].slot.ctx_id == ctx_id) { - sg_resource_state state = p->samplers[i].slot.state; - if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_discard_sampler(&p->samplers[i]); - } + sg_resource_state state = p->samplers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_sampler(&p->samplers[i]); } } for (int i = 1; i < p->shader_pool.size; i++) { - if (p->shaders[i].slot.ctx_id == ctx_id) { - sg_resource_state state = p->shaders[i].slot.state; - if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_discard_shader(&p->shaders[i]); - } + sg_resource_state state = p->shaders[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_shader(&p->shaders[i]); } } for (int i = 1; i < p->pipeline_pool.size; i++) { - if (p->pipelines[i].slot.ctx_id == ctx_id) { - sg_resource_state state = p->pipelines[i].slot.state; - if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_discard_pipeline(&p->pipelines[i]); - } + sg_resource_state state = p->pipelines[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_pipeline(&p->pipelines[i]); } } - for (int i = 1; i < p->pass_pool.size; i++) { - if (p->passes[i].slot.ctx_id == ctx_id) { - sg_resource_state state = p->passes[i].slot.state; - if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { - _sg_discard_pass(&p->passes[i]); - } + for (int i = 1; i < p->attachments_pool.size; i++) { + sg_resource_state state = p->attachments[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_attachments(&p->attachments[i]); } } } @@ -15428,6 +16199,10 @@ _SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { } else { _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_BUFFERDESC_NO_DATA); } + if (desc->type == SG_BUFFERTYPE_STORAGEBUFFER) { + _SG_VALIDATE(_sg.features.storage_buffer, VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED); + _SG_VALIDATE(_sg_multiple_u64(desc->size, 4), VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4); + } return _sg_validate_end(); #endif } @@ -15540,8 +16315,6 @@ _SOKOL_PRIVATE bool _sg_validate_sampler_desc(const sg_sampler_desc* desc) { _sg_validate_begin(); _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SAMPLERDESC_CANARY); _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SAMPLERDESC_CANARY); - _SG_VALIDATE(desc->min_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MINFILTER_NONE); - _SG_VALIDATE(desc->mag_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MAGFILTER_NONE); // restriction from WebGPU: when anisotropy > 1, all filters must be linear if (desc->max_anisotropy > 1) { _SG_VALIDATE((desc->min_filter == SG_FILTER_LINEAR) @@ -15565,10 +16338,7 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { _sg_validate_begin(); _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SHADERDESC_CANARY); _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SHADERDESC_CANARY); - #if defined(SOKOL_D3D11) - _SG_VALIDATE(0 != desc->attrs[0].sem_name, VALIDATE_SHADERDESC_ATTR_SEMANTICS); - #endif - #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) || defined(SOKOL_WGPU) + #if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) || defined(SOKOL_WGPU) // on GL or WebGPU, must provide shader source code _SG_VALIDATE(0 != desc->vs.source, VALIDATE_SHADERDESC_SOURCE); _SG_VALIDATE(0 != desc->fs.source, VALIDATE_SHADERDESC_SOURCE); @@ -15639,6 +16409,16 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { uniform_blocks_continuous = false; } } + bool storage_buffers_continuous = true; + for (int sbuf_index = 0; sbuf_index < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; sbuf_index++) { + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + if (sbuf_desc->used) { + _SG_VALIDATE(storage_buffers_continuous, VALIDATE_SHADERDESC_NO_CONT_STORAGEBUFFERS); + _SG_VALIDATE(sbuf_desc->readonly, VALIDATE_SHADERDESC_STORAGEBUFFER_READONLY); + } else { + storage_buffers_continuous = false; + } + } bool images_continuous = true; int num_images = 0; for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { @@ -15733,7 +16513,6 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { } _SG_VALIDATE(_sg_multiple_u64((uint64_t)l_state->stride, 4), VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); } - _SG_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, VALIDATE_PIPELINEDESC_NO_ATTRS); const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); _SG_VALIDATE(0 != shd, VALIDATE_PIPELINEDESC_SHADER); if (shd) { @@ -15745,7 +16524,7 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { attrs_cont = false; continue; } - _SG_VALIDATE(attrs_cont, VALIDATE_PIPELINEDESC_NO_ATTRS); + _SG_VALIDATE(attrs_cont, VALIDATE_PIPELINEDESC_NO_CONT_ATTRS); SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); #if defined(SOKOL_D3D11) // on D3D11, semantic names (and semantic indices) must be provided @@ -15757,7 +16536,7 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { #endif } -_SOKOL_PRIVATE bool _sg_validate_pass_desc(const sg_pass_desc* desc) { +_SOKOL_PRIVATE bool _sg_validate_attachments_desc(const sg_attachments_desc* desc) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(desc); return true; @@ -15767,99 +16546,99 @@ _SOKOL_PRIVATE bool _sg_validate_pass_desc(const sg_pass_desc* desc) { } SOKOL_ASSERT(desc); _sg_validate_begin(); - _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_PASSDESC_CANARY); - _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_PASSDESC_CANARY); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_ATTACHMENTSDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_ATTACHMENTSDESC_CANARY); bool atts_cont = true; int color_width = -1, color_height = -1, color_sample_count = -1; bool has_color_atts = false; for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { - const sg_pass_attachment_desc* att = &desc->color_attachments[att_index]; + const sg_attachment_desc* att = &desc->colors[att_index]; if (att->image.id == SG_INVALID_ID) { atts_cont = false; continue; } - _SG_VALIDATE(atts_cont, VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); + _SG_VALIDATE(atts_cont, VALIDATE_ATTACHMENTSDESC_NO_CONT_COLOR_ATTS); has_color_atts = true; const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); - _SG_VALIDATE(img, VALIDATE_PASSDESC_IMAGE); + _SG_VALIDATE(img, VALIDATE_ATTACHMENTSDESC_IMAGE); if (0 != img) { - _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PASSDESC_IMAGE); - _SG_VALIDATE(img->cmn.render_target, VALIDATE_PASSDESC_IMAGE_NO_RT); - _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_PASSDESC_MIPLEVEL); + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ATTACHMENTSDESC_IMAGE); + _SG_VALIDATE(img->cmn.render_target, VALIDATE_ATTACHMENTSDESC_IMAGE_NO_RT); + _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_ATTACHMENTSDESC_MIPLEVEL); if (img->cmn.type == SG_IMAGETYPE_CUBE) { - _SG_VALIDATE(att->slice < 6, VALIDATE_PASSDESC_FACE); + _SG_VALIDATE(att->slice < 6, VALIDATE_ATTACHMENTSDESC_FACE); } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { - _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_LAYER); + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_LAYER); } else if (img->cmn.type == SG_IMAGETYPE_3D) { - _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_SLICE); + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_SLICE); } if (att_index == 0) { color_width = _sg_miplevel_dim(img->cmn.width, att->mip_level); color_height = _sg_miplevel_dim(img->cmn.height, att->mip_level); color_sample_count = img->cmn.sample_count; } else { - _SG_VALIDATE(color_width == _sg_miplevel_dim(img->cmn.width, att->mip_level), VALIDATE_PASSDESC_IMAGE_SIZES); - _SG_VALIDATE(color_height == _sg_miplevel_dim(img->cmn.height, att->mip_level), VALIDATE_PASSDESC_IMAGE_SIZES); - _SG_VALIDATE(color_sample_count == img->cmn.sample_count, VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + _SG_VALIDATE(color_width == _sg_miplevel_dim(img->cmn.width, att->mip_level), VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES); + _SG_VALIDATE(color_height == _sg_miplevel_dim(img->cmn.height, att->mip_level), VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES); + _SG_VALIDATE(color_sample_count == img->cmn.sample_count, VALIDATE_ATTACHMENTSDESC_IMAGE_SAMPLE_COUNTS); } - _SG_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); + _SG_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), VALIDATE_ATTACHMENTSDESC_COLOR_INV_PIXELFORMAT); // check resolve attachment - const sg_pass_attachment_desc* res_att = &desc->resolve_attachments[att_index]; + const sg_attachment_desc* res_att = &desc->resolves[att_index]; if (res_att->image.id != SG_INVALID_ID) { // associated color attachment must be MSAA - _SG_VALIDATE(img->cmn.sample_count > 1, VALIDATE_PASSDESC_RESOLVE_COLOR_IMAGE_MSAA); + _SG_VALIDATE(img->cmn.sample_count > 1, VALIDATE_ATTACHMENTSDESC_RESOLVE_COLOR_IMAGE_MSAA); const _sg_image_t* res_img = _sg_lookup_image(&_sg.pools, res_att->image.id); - _SG_VALIDATE(res_img, VALIDATE_PASSDESC_RESOLVE_IMAGE); + _SG_VALIDATE(res_img, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE); if (res_img != 0) { - _SG_VALIDATE(res_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PASSDESC_RESOLVE_IMAGE); - _SG_VALIDATE(res_img->cmn.render_target, VALIDATE_PASSDESC_RESOLVE_IMAGE_NO_RT); - _SG_VALIDATE(res_img->cmn.sample_count == 1, VALIDATE_PASSDESC_RESOLVE_SAMPLE_COUNT); - _SG_VALIDATE(res_att->mip_level < res_img->cmn.num_mipmaps, VALIDATE_PASSDESC_RESOLVE_MIPLEVEL); + _SG_VALIDATE(res_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE); + _SG_VALIDATE(res_img->cmn.render_target, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_NO_RT); + _SG_VALIDATE(res_img->cmn.sample_count == 1, VALIDATE_ATTACHMENTSDESC_RESOLVE_SAMPLE_COUNT); + _SG_VALIDATE(res_att->mip_level < res_img->cmn.num_mipmaps, VALIDATE_ATTACHMENTSDESC_RESOLVE_MIPLEVEL); if (res_img->cmn.type == SG_IMAGETYPE_CUBE) { - _SG_VALIDATE(res_att->slice < 6, VALIDATE_PASSDESC_RESOLVE_FACE); + _SG_VALIDATE(res_att->slice < 6, VALIDATE_ATTACHMENTSDESC_RESOLVE_FACE); } else if (res_img->cmn.type == SG_IMAGETYPE_ARRAY) { - _SG_VALIDATE(res_att->slice < res_img->cmn.num_slices, VALIDATE_PASSDESC_RESOLVE_LAYER); + _SG_VALIDATE(res_att->slice < res_img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_RESOLVE_LAYER); } else if (res_img->cmn.type == SG_IMAGETYPE_3D) { - _SG_VALIDATE(res_att->slice < res_img->cmn.num_slices, VALIDATE_PASSDESC_RESOLVE_SLICE); + _SG_VALIDATE(res_att->slice < res_img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_RESOLVE_SLICE); } - _SG_VALIDATE(img->cmn.pixel_format == res_img->cmn.pixel_format, VALIDATE_PASSDESC_RESOLVE_IMAGE_FORMAT); - _SG_VALIDATE(color_width == _sg_miplevel_dim(res_img->cmn.width, res_att->mip_level), VALIDATE_PASSDESC_RESOLVE_IMAGE_SIZES); - _SG_VALIDATE(color_height == _sg_miplevel_dim(res_img->cmn.height, res_att->mip_level), VALIDATE_PASSDESC_RESOLVE_IMAGE_SIZES); + _SG_VALIDATE(img->cmn.pixel_format == res_img->cmn.pixel_format, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_FORMAT); + _SG_VALIDATE(color_width == _sg_miplevel_dim(res_img->cmn.width, res_att->mip_level), VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES); + _SG_VALIDATE(color_height == _sg_miplevel_dim(res_img->cmn.height, res_att->mip_level), VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES); } } } } bool has_depth_stencil_att = false; - if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { - const sg_pass_attachment_desc* att = &desc->depth_stencil_attachment; + if (desc->depth_stencil.image.id != SG_INVALID_ID) { + const sg_attachment_desc* att = &desc->depth_stencil; const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); - _SG_VALIDATE(img, VALIDATE_PASSDESC_DEPTH_IMAGE); + _SG_VALIDATE(img, VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE); has_depth_stencil_att = true; if (img) { - _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PASSDESC_DEPTH_IMAGE); - _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_PASSDESC_DEPTH_MIPLEVEL); + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE); + _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_ATTACHMENTSDESC_DEPTH_MIPLEVEL); if (img->cmn.type == SG_IMAGETYPE_CUBE) { - _SG_VALIDATE(att->slice < 6, VALIDATE_PASSDESC_DEPTH_FACE); + _SG_VALIDATE(att->slice < 6, VALIDATE_ATTACHMENTSDESC_DEPTH_FACE); } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { - _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_DEPTH_LAYER); + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_DEPTH_LAYER); } else if (img->cmn.type == SG_IMAGETYPE_3D) { // NOTE: this can't actually happen because of VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE - _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_DEPTH_SLICE); + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_DEPTH_SLICE); } - _SG_VALIDATE(img->cmn.render_target, VALIDATE_PASSDESC_DEPTH_IMAGE_NO_RT); - _SG_VALIDATE((color_width == -1) || (color_width == _sg_miplevel_dim(img->cmn.width, att->mip_level)), VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES); - _SG_VALIDATE((color_height == -1) || (color_height == _sg_miplevel_dim(img->cmn.height, att->mip_level)), VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES); - _SG_VALIDATE((color_sample_count == -1) || (color_sample_count == img->cmn.sample_count), VALIDATE_PASSDESC_DEPTH_IMAGE_SAMPLE_COUNT); - _SG_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT); + _SG_VALIDATE(img->cmn.render_target, VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_NO_RT); + _SG_VALIDATE((color_width == -1) || (color_width == _sg_miplevel_dim(img->cmn.width, att->mip_level)), VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES); + _SG_VALIDATE((color_height == -1) || (color_height == _sg_miplevel_dim(img->cmn.height, att->mip_level)), VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES); + _SG_VALIDATE((color_sample_count == -1) || (color_sample_count == img->cmn.sample_count), VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SAMPLE_COUNT); + _SG_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), VALIDATE_ATTACHMENTSDESC_DEPTH_INV_PIXELFORMAT); } } - _SG_VALIDATE(has_color_atts || has_depth_stencil_att, VALIDATE_PASSDESC_NO_ATTACHMENTS); + _SG_VALIDATE(has_color_atts || has_depth_stencil_att, VALIDATE_ATTACHMENTSDESC_NO_ATTACHMENTS); return _sg_validate_end(); #endif } -_SOKOL_PRIVATE bool _sg_validate_begin_pass(_sg_pass_t* pass) { +_SOKOL_PRIVATE bool _sg_validate_begin_pass(const sg_pass* pass) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(pass); return true; @@ -15868,27 +16647,102 @@ _SOKOL_PRIVATE bool _sg_validate_begin_pass(_sg_pass_t* pass) { return true; } _sg_validate_begin(); - _SG_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_PASS); - - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - const _sg_pass_attachment_t* color_att = &pass->cmn.color_atts[i]; - const _sg_image_t* color_img = _sg_pass_color_image(pass, i); - if (color_img) { - _SG_VALIDATE(color_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE); - _SG_VALIDATE(color_img->slot.id == color_att->image_id.id, VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE); - } - const _sg_pass_attachment_t* resolve_att = &pass->cmn.resolve_atts[i]; - const _sg_image_t* resolve_img = _sg_pass_resolve_image(pass, i); - if (resolve_img) { - _SG_VALIDATE(resolve_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE); - _SG_VALIDATE(resolve_img->slot.id == resolve_att->image_id.id, VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE); + _SG_VALIDATE(pass->_start_canary == 0, VALIDATE_BEGINPASS_CANARY); + _SG_VALIDATE(pass->_end_canary == 0, VALIDATE_BEGINPASS_CANARY); + if (pass->attachments.id == SG_INVALID_ID) { + // this is a swapchain pass + _SG_VALIDATE(pass->swapchain.width > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH); + _SG_VALIDATE(pass->swapchain.height > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT); + _SG_VALIDATE(pass->swapchain.sample_count > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT); + _SG_VALIDATE(pass->swapchain.color_format > SG_PIXELFORMAT_NONE, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT); + // NOTE: depth buffer is optional, so depth_format is allowed to be invalid + // NOTE: the GL framebuffer handle may actually be 0 + #if defined(SOKOL_METAL) + _SG_VALIDATE(pass->swapchain.metal.current_drawable != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE); + } else { + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET); + } + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(pass->swapchain.d3d11.render_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW); + } else { + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET); + } + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(pass->swapchain.wgpu.render_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW); + } else { + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET); + } + #endif + } else { + // this is an 'offscreen pass' + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, pass->attachments.id); + if (atts) { + _SG_VALIDATE(atts->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_ATTACHMENTS_VALID); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_attachment_common_t* color_att = &atts->cmn.colors[i]; + const _sg_image_t* color_img = _sg_attachments_color_image(atts, i); + if (color_img) { + _SG_VALIDATE(color_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE); + _SG_VALIDATE(color_img->slot.id == color_att->image_id.id, VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE); + } + const _sg_attachment_common_t* resolve_att = &atts->cmn.resolves[i]; + const _sg_image_t* resolve_img = _sg_attachments_resolve_image(atts, i); + if (resolve_img) { + _SG_VALIDATE(resolve_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE); + _SG_VALIDATE(resolve_img->slot.id == resolve_att->image_id.id, VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE); + } + } + const _sg_image_t* ds_img = _sg_attachments_ds_image(atts); + if (ds_img) { + const _sg_attachment_common_t* att = &atts->cmn.depth_stencil; + _SG_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE); + _SG_VALIDATE(ds_img->slot.id == att->image_id.id, VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE); + } + } else { + _SG_VALIDATE(atts != 0, VALIDATE_BEGINPASS_ATTACHMENTS_EXISTS); } - } - const _sg_image_t* ds_img = _sg_pass_ds_image(pass); - if (ds_img) { - const _sg_pass_attachment_t* att = &pass->cmn.ds_att; - _SG_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE); - _SG_VALIDATE(ds_img->slot.id == att->image_id.id, VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE); + // swapchain params must be all zero! + _SG_VALIDATE(pass->swapchain.width == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH_NOTSET); + _SG_VALIDATE(pass->swapchain.height == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT_NOTSET); + _SG_VALIDATE(pass->swapchain.sample_count == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT_NOTSET); + _SG_VALIDATE(pass->swapchain.color_format == _SG_PIXELFORMAT_DEFAULT, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT_NOTSET); + _SG_VALIDATE(pass->swapchain.depth_format == _SG_PIXELFORMAT_DEFAULT, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_DEPTHFORMAT_NOTSET); + #if defined(SOKOL_METAL) + _SG_VALIDATE(pass->swapchain.metal.current_drawable == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE_NOTSET); + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET); + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET); + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(pass->swapchain.d3d11.render_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET); + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(pass->swapchain.wgpu.render_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET); + #elif defined(_SOKOL_ANY_GL) + _SG_VALIDATE(pass->swapchain.gl.framebuffer == 0, VALIDATE_BEGINPASS_SWAPCHAIN_GL_EXPECT_FRAMEBUFFER_NOTSET); + #endif } return _sg_validate_end(); #endif @@ -15916,16 +16770,20 @@ _SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { _SG_VALIDATE(pip->shader->slot.id == pip->cmn.shader_id.id, VALIDATE_APIP_SHADER_EXISTS); _SG_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_SHADER_VALID); // check that pipeline attributes match current pass attributes - const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, _sg.cur_pass.id); - if (pass) { + if (_sg.cur_pass.atts_id.id != SG_INVALID_ID) { // an offscreen pass - _SG_VALIDATE(pip->cmn.color_count == pass->cmn.num_color_atts, VALIDATE_APIP_ATT_COUNT); + const _sg_attachments_t* atts = _sg.cur_pass.atts; + SOKOL_ASSERT(atts); + _SG_VALIDATE(atts->slot.id == _sg.cur_pass.atts_id.id, VALIDATE_APIP_CURPASS_ATTACHMENTS_EXISTS); + _SG_VALIDATE(atts->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_CURPASS_ATTACHMENTS_VALID); + + _SG_VALIDATE(pip->cmn.color_count == atts->cmn.num_colors, VALIDATE_APIP_ATT_COUNT); for (int i = 0; i < pip->cmn.color_count; i++) { - const _sg_image_t* att_img = _sg_pass_color_image(pass, i); + const _sg_image_t* att_img = _sg_attachments_color_image(atts, i); _SG_VALIDATE(pip->cmn.colors[i].pixel_format == att_img->cmn.pixel_format, VALIDATE_APIP_COLOR_FORMAT); _SG_VALIDATE(pip->cmn.sample_count == att_img->cmn.sample_count, VALIDATE_APIP_SAMPLE_COUNT); } - const _sg_image_t* att_dsimg = _sg_pass_ds_image(pass); + const _sg_image_t* att_dsimg = _sg_attachments_ds_image(atts); if (att_dsimg) { _SG_VALIDATE(pip->cmn.depth.pixel_format == att_dsimg->cmn.pixel_format, VALIDATE_APIP_DEPTH_FORMAT); } else { @@ -15934,9 +16792,9 @@ _SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { } else { // default pass _SG_VALIDATE(pip->cmn.color_count == 1, VALIDATE_APIP_ATT_COUNT); - _SG_VALIDATE(pip->cmn.colors[0].pixel_format == _sg.desc.context.color_format, VALIDATE_APIP_COLOR_FORMAT); - _SG_VALIDATE(pip->cmn.depth.pixel_format == _sg.desc.context.depth_format, VALIDATE_APIP_DEPTH_FORMAT); - _SG_VALIDATE(pip->cmn.sample_count == _sg.desc.context.sample_count, VALIDATE_APIP_SAMPLE_COUNT); + _SG_VALIDATE(pip->cmn.colors[0].pixel_format == _sg.cur_pass.swapchain.color_fmt, VALIDATE_APIP_COLOR_FORMAT); + _SG_VALIDATE(pip->cmn.depth.pixel_format == _sg.cur_pass.swapchain.depth_fmt, VALIDATE_APIP_DEPTH_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == _sg.cur_pass.swapchain.sample_count, VALIDATE_APIP_SAMPLE_COUNT); } return _sg_validate_end(); #endif @@ -16008,7 +16866,7 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { if (img && img->slot.state == SG_RESOURCESTATE_VALID) { _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_VS_IMAGE_TYPE_MISMATCH); _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_VS_IMAGE_MSAA); - const sg_pixelformat_info* info = &_sg.formats[img->cmn.pixel_format]; + const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format]; switch (stage->images[i].sample_type) { case SG_IMAGESAMPLETYPE_FLOAT: _SG_VALIDATE(info->filter, VALIDATE_ABND_VS_EXPECTED_FILTERABLE_IMAGE); @@ -16053,6 +16911,23 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { } } + // has expected vertex shader storage buffers + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->storage_buffers[i].used) { + _SG_VALIDATE(bindings->vs.storage_buffers[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_STORAGEBUFFER_BINDING); + if (bindings->vs.storage_buffers[i].id != SG_INVALID_ID) { + const _sg_buffer_t* sbuf = _sg_lookup_buffer(&_sg.pools, bindings->vs.storage_buffers[i].id); + _SG_VALIDATE(sbuf != 0, VALIDATE_ABND_VS_STORAGEBUFFER_EXISTS); + if (sbuf) { + _SG_VALIDATE(sbuf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER, VALIDATE_ABND_VS_STORAGEBUFFER_BINDING_BUFFERTYPE); + } + } + } else { + _SG_VALIDATE(bindings->vs.storage_buffers[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_STORAGEBUFFER_BINDING); + } + } + // has expected fragment shader images for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; @@ -16064,7 +16939,7 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { if (img && img->slot.state == SG_RESOURCESTATE_VALID) { _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH); _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_FS_IMAGE_MSAA); - const sg_pixelformat_info* info = &_sg.formats[img->cmn.pixel_format]; + const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format]; switch (stage->images[i].sample_type) { case SG_IMAGESAMPLETYPE_FLOAT: _SG_VALIDATE(info->filter, VALIDATE_ABND_FS_EXPECTED_FILTERABLE_IMAGE); @@ -16109,33 +16984,23 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { } } - // if image-sampler-pair info was provided in shader desc, check that that the mipmap filter matches image num mipmaps - for (int img_smp_index = 0; img_smp_index < pip->shader->cmn.stage[SG_SHADERSTAGE_VS].num_image_samplers; img_smp_index++) { - const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; - const int img_index = stage->image_samplers[img_smp_index].image_slot; - const int smp_index = stage->image_samplers[img_smp_index].sampler_slot; - const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs.images[img_index].id); - const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[smp_index].id); - if (img && smp) { - if (img->cmn.num_mipmaps == 1) { - _SG_VALIDATE(smp->cmn.mipmap_filter == SG_FILTER_NONE, VALIDATE_ABND_VS_IMG_SMP_MIPMAPS); - } - } - } - for (int img_smp_index = 0; img_smp_index < pip->shader->cmn.stage[SG_SHADERSTAGE_FS].num_image_samplers; img_smp_index++) { + // has expected fragment shader storage buffers + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; - const int img_index = stage->image_samplers[img_smp_index].image_slot; - const int smp_index = stage->image_samplers[img_smp_index].sampler_slot; - SOKOL_ASSERT(img_index < stage->num_images); - SOKOL_ASSERT(smp_index < stage->num_samplers); - const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs.images[img_index].id); - const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[smp_index].id); - if (img && smp) { - if (img->cmn.num_mipmaps == 1) { - _SG_VALIDATE(smp->cmn.mipmap_filter == SG_FILTER_NONE, VALIDATE_ABND_FS_IMG_SMP_MIPMAPS); + if (stage->storage_buffers[i].used) { + _SG_VALIDATE(bindings->fs.storage_buffers[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_STORAGEBUFFER_BINDING); + if (bindings->fs.storage_buffers[i].id != SG_INVALID_ID) { + const _sg_buffer_t* sbuf = _sg_lookup_buffer(&_sg.pools, bindings->fs.storage_buffers[i].id); + _SG_VALIDATE(sbuf != 0, VALIDATE_ABND_FS_STORAGEBUFFER_EXISTS); + if (sbuf) { + _SG_VALIDATE(sbuf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER, VALIDATE_ABND_FS_STORAGEBUFFER_BINDING_BUFFERTYPE); + } } + } else { + _SG_VALIDATE(bindings->fs.storage_buffers[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_STORAGEBUFFER_BINDING); } } + return _sg_validate_end(); #endif } @@ -16256,8 +17121,8 @@ _SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) def.num_mipmaps = _sg_def(def.num_mipmaps, 1); def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); if (desc->render_target) { - def.pixel_format = _sg_def(def.pixel_format, _sg.desc.context.color_format); - def.sample_count = _sg_def(def.sample_count, _sg.desc.context.sample_count); + def.pixel_format = _sg_def(def.pixel_format, _sg.desc.environment.defaults.color_format); + def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count); } else { def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8); def.sample_count = _sg_def(def.sample_count, 1); @@ -16269,7 +17134,7 @@ _SOKOL_PRIVATE sg_sampler_desc _sg_sampler_desc_defaults(const sg_sampler_desc* sg_sampler_desc def = *desc; def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST); def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST); - def.mipmap_filter = _sg_def(def.mipmap_filter, SG_FILTER_NONE); + def.mipmap_filter = _sg_def(def.mipmap_filter, SG_FILTER_NEAREST); def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT); def.wrap_v = _sg_def(def.wrap_v, SG_WRAP_REPEAT); def.wrap_w = _sg_def(def.wrap_w, SG_WRAP_REPEAT); @@ -16339,7 +17204,7 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des def.index_type = _sg_def(def.index_type, SG_INDEXTYPE_NONE); def.cull_mode = _sg_def(def.cull_mode, SG_CULLMODE_NONE); def.face_winding = _sg_def(def.face_winding, SG_FACEWINDING_CW); - def.sample_count = _sg_def(def.sample_count, _sg.desc.context.sample_count); + def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count); def.stencil.front.compare = _sg_def(def.stencil.front.compare, SG_COMPAREFUNC_ALWAYS); def.stencil.front.fail_op = _sg_def(def.stencil.front.fail_op, SG_STENCILOP_KEEP); @@ -16351,7 +17216,7 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des def.stencil.back.pass_op = _sg_def(def.stencil.back.pass_op, SG_STENCILOP_KEEP); def.depth.compare = _sg_def(def.depth.compare, SG_COMPAREFUNC_ALWAYS); - def.depth.pixel_format = _sg_def(def.depth.pixel_format, _sg.desc.context.depth_format); + def.depth.pixel_format = _sg_def(def.depth.pixel_format, _sg.desc.environment.defaults.depth_format); if (def.colors[0].pixel_format == SG_PIXELFORMAT_NONE) { // special case depth-only rendering, enforce a color count of 0 def.color_count = 0; @@ -16363,7 +17228,7 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des } for (int i = 0; i < def.color_count; i++) { sg_color_target_state* cs = &def.colors[i]; - cs->pixel_format = _sg_def(cs->pixel_format, _sg.desc.context.color_format); + cs->pixel_format = _sg_def(cs->pixel_format, _sg.desc.environment.defaults.color_format); cs->write_mask = _sg_def(cs->write_mask, SG_COLORMASK_RGBA); sg_blend_state* bs = &def.colors[i].blend; bs->src_factor_rgb = _sg_def(bs->src_factor_rgb, SG_BLENDFACTOR_ONE); @@ -16417,9 +17282,8 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des return def; } -_SOKOL_PRIVATE sg_pass_desc _sg_pass_desc_defaults(const sg_pass_desc* desc) { - // FIXME: no values to replace in sg_pass_desc? - sg_pass_desc def = *desc; +_SOKOL_PRIVATE sg_attachments_desc _sg_attachments_desc_defaults(const sg_attachments_desc* desc) { + sg_attachments_desc def = *desc; return def; } @@ -16483,11 +17347,11 @@ _SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { return res; } -_SOKOL_PRIVATE sg_pass _sg_alloc_pass(void) { - sg_pass res; - int slot_index = _sg_pool_alloc_index(&_sg.pools.pass_pool); +_SOKOL_PRIVATE sg_attachments _sg_alloc_attachments(void) { + sg_attachments res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.attachments_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { - res.id = _sg_slot_alloc(&_sg.pools.pass_pool, &_sg.pools.passes[slot_index].slot, slot_index); + res.id = _sg_slot_alloc(&_sg.pools.attachments_pool, &_sg.pools.attachments[slot_index].slot, slot_index); } else { res.id = SG_INVALID_ID; _SG_ERROR(PASS_POOL_EXHAUSTED); @@ -16525,16 +17389,15 @@ _SOKOL_PRIVATE void _sg_dealloc_pipeline(_sg_pipeline_t* pip) { _sg_reset_slot(&pip->slot); } -_SOKOL_PRIVATE void _sg_dealloc_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass && (pass->slot.state == SG_RESOURCESTATE_ALLOC) && (pass->slot.id != SG_INVALID_ID)); - _sg_pool_free_index(&_sg.pools.pass_pool, _sg_slot_index(pass->slot.id)); - _sg_reset_slot(&pass->slot); +_SOKOL_PRIVATE void _sg_dealloc_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts && (atts->slot.state == SG_RESOURCESTATE_ALLOC) && (atts->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.attachments_pool, _sg_slot_index(atts->slot.id)); + _sg_reset_slot(&atts->slot); } _SOKOL_PRIVATE void _sg_init_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC)); SOKOL_ASSERT(desc); - buf->slot.ctx_id = _sg.active_context.id; if (_sg_validate_buffer_desc(desc)) { _sg_buffer_common_init(&buf->cmn, desc); buf->slot.state = _sg_create_buffer(buf, desc); @@ -16547,7 +17410,6 @@ _SOKOL_PRIVATE void _sg_init_buffer(_sg_buffer_t* buf, const sg_buffer_desc* des _SOKOL_PRIVATE void _sg_init_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC)); SOKOL_ASSERT(desc); - img->slot.ctx_id = _sg.active_context.id; if (_sg_validate_image_desc(desc)) { _sg_image_common_init(&img->cmn, desc); img->slot.state = _sg_create_image(img, desc); @@ -16560,7 +17422,6 @@ _SOKOL_PRIVATE void _sg_init_image(_sg_image_t* img, const sg_image_desc* desc) _SOKOL_PRIVATE void _sg_init_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC)); SOKOL_ASSERT(desc); - smp->slot.ctx_id = _sg.active_context.id; if (_sg_validate_sampler_desc(desc)) { _sg_sampler_common_init(&smp->cmn, desc); smp->slot.state = _sg_create_sampler(smp, desc); @@ -16573,7 +17434,6 @@ _SOKOL_PRIVATE void _sg_init_sampler(_sg_sampler_t* smp, const sg_sampler_desc* _SOKOL_PRIVATE void _sg_init_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC)); SOKOL_ASSERT(desc); - shd->slot.ctx_id = _sg.active_context.id; if (_sg_validate_shader_desc(desc)) { _sg_shader_common_init(&shd->cmn, desc); shd->slot.state = _sg_create_shader(shd, desc); @@ -16586,7 +17446,6 @@ _SOKOL_PRIVATE void _sg_init_shader(_sg_shader_t* shd, const sg_shader_desc* des _SOKOL_PRIVATE void _sg_init_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC)); SOKOL_ASSERT(desc); - pip->slot.ctx_id = _sg.active_context.id; if (_sg_validate_pipeline_desc(desc)) { _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); if (shd && (shd->slot.state == SG_RESOURCESTATE_VALID)) { @@ -16601,11 +17460,10 @@ _SOKOL_PRIVATE void _sg_init_pipeline(_sg_pipeline_t* pip, const sg_pipeline_des SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); } -_SOKOL_PRIVATE void _sg_init_pass(_sg_pass_t* pass, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); +_SOKOL_PRIVATE void _sg_init_attachments(_sg_attachments_t* atts, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && atts->slot.state == SG_RESOURCESTATE_ALLOC); SOKOL_ASSERT(desc); - pass->slot.ctx_id = _sg.active_context.id; - if (_sg_validate_pass_desc(desc)) { + if (_sg_validate_attachments_desc(desc)) { // lookup pass attachment image pointers _sg_image_t* color_images[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; _sg_image_t* resolve_images[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; @@ -16614,100 +17472,76 @@ _SOKOL_PRIVATE void _sg_init_pass(_sg_pass_t* pass, const sg_pass_desc* desc) { int width = 0; int height = 0; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (desc->color_attachments[i].image.id) { - color_images[i] = _sg_lookup_image(&_sg.pools, desc->color_attachments[i].image.id); + if (desc->colors[i].image.id) { + color_images[i] = _sg_lookup_image(&_sg.pools, desc->colors[i].image.id); if (!(color_images[i] && color_images[i]->slot.state == SG_RESOURCESTATE_VALID)) { - pass->slot.state = SG_RESOURCESTATE_FAILED; + atts->slot.state = SG_RESOURCESTATE_FAILED; return; } - const int mip_level = desc->color_attachments[i].mip_level; + const int mip_level = desc->colors[i].mip_level; width = _sg_miplevel_dim(color_images[i]->cmn.width, mip_level); height = _sg_miplevel_dim(color_images[i]->cmn.height, mip_level); } - if (desc->resolve_attachments[i].image.id) { - resolve_images[i] = _sg_lookup_image(&_sg.pools, desc->resolve_attachments[i].image.id); + if (desc->resolves[i].image.id) { + resolve_images[i] = _sg_lookup_image(&_sg.pools, desc->resolves[i].image.id); if (!(resolve_images[i] && resolve_images[i]->slot.state == SG_RESOURCESTATE_VALID)) { - pass->slot.state = SG_RESOURCESTATE_FAILED; + atts->slot.state = SG_RESOURCESTATE_FAILED; return; } } } - if (desc->depth_stencil_attachment.image.id) { - ds_image = _sg_lookup_image(&_sg.pools, desc->depth_stencil_attachment.image.id); + if (desc->depth_stencil.image.id) { + ds_image = _sg_lookup_image(&_sg.pools, desc->depth_stencil.image.id); if (!(ds_image && ds_image->slot.state == SG_RESOURCESTATE_VALID)) { - pass->slot.state = SG_RESOURCESTATE_FAILED; + atts->slot.state = SG_RESOURCESTATE_FAILED; return; } - const int mip_level = desc->depth_stencil_attachment.mip_level; + const int mip_level = desc->depth_stencil.mip_level; width = _sg_miplevel_dim(ds_image->cmn.width, mip_level); height = _sg_miplevel_dim(ds_image->cmn.height, mip_level); } - _sg_pass_common_init(&pass->cmn, desc, width, height); - pass->slot.state = _sg_create_pass(pass, color_images, resolve_images, ds_image, desc); + _sg_attachments_common_init(&atts->cmn, desc, width, height); + atts->slot.state = _sg_create_attachments(atts, color_images, resolve_images, ds_image, desc); } else { - pass->slot.state = SG_RESOURCESTATE_FAILED; + atts->slot.state = SG_RESOURCESTATE_FAILED; } - SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID)||(pass->slot.state == SG_RESOURCESTATE_FAILED)); + SOKOL_ASSERT((atts->slot.state == SG_RESOURCESTATE_VALID)||(atts->slot.state == SG_RESOURCESTATE_FAILED)); } _SOKOL_PRIVATE void _sg_uninit_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf && ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED))); - if (buf->slot.ctx_id == _sg.active_context.id) { - _sg_discard_buffer(buf); - _sg_reset_buffer_to_alloc_state(buf); - } else { - _SG_WARN(UNINIT_BUFFER_ACTIVE_CONTEXT_MISMATCH); - } + _sg_discard_buffer(buf); + _sg_reset_buffer_to_alloc_state(buf); } _SOKOL_PRIVATE void _sg_uninit_image(_sg_image_t* img) { SOKOL_ASSERT(img && ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED))); - if (img->slot.ctx_id == _sg.active_context.id) { - _sg_discard_image(img); - _sg_reset_image_to_alloc_state(img); - } else { - _SG_WARN(UNINIT_IMAGE_ACTIVE_CONTEXT_MISMATCH); - } + _sg_discard_image(img); + _sg_reset_image_to_alloc_state(img); } _SOKOL_PRIVATE void _sg_uninit_sampler(_sg_sampler_t* smp) { SOKOL_ASSERT(smp && ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED))); - if (smp->slot.ctx_id == _sg.active_context.id) { - _sg_discard_sampler(smp); - _sg_reset_sampler_to_alloc_state(smp); - } else { - _SG_WARN(UNINIT_SAMPLER_ACTIVE_CONTEXT_MISMATCH); - } + _sg_discard_sampler(smp); + _sg_reset_sampler_to_alloc_state(smp); } _SOKOL_PRIVATE void _sg_uninit_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd && ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED))); - if (shd->slot.ctx_id == _sg.active_context.id) { - _sg_discard_shader(shd); - _sg_reset_shader_to_alloc_state(shd); - } else { - _SG_WARN(UNINIT_SHADER_ACTIVE_CONTEXT_MISMATCH); - } + _sg_discard_shader(shd); + _sg_reset_shader_to_alloc_state(shd); } _SOKOL_PRIVATE void _sg_uninit_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip && ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED))); - if (pip->slot.ctx_id == _sg.active_context.id) { - _sg_discard_pipeline(pip); - _sg_reset_pipeline_to_alloc_state(pip); - } else { - _SG_WARN(UNINIT_PIPELINE_ACTIVE_CONTEXT_MISMATCH); - } + _sg_discard_pipeline(pip); + _sg_reset_pipeline_to_alloc_state(pip); } -_SOKOL_PRIVATE void _sg_uninit_pass(_sg_pass_t* pass) { - SOKOL_ASSERT(pass && ((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED))); - if (pass->slot.ctx_id == _sg.active_context.id) { - _sg_discard_pass(pass); - _sg_reset_pass_to_alloc_state(pass); - } else { - _SG_WARN(UNINIT_PASS_ACTIVE_CONTEXT_MISMATCH); - } +_SOKOL_PRIVATE void _sg_uninit_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts && ((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_attachments(atts); + _sg_reset_attachments_to_alloc_state(atts); } _SOKOL_PRIVATE void _sg_setup_commit_listeners(const sg_desc* desc) { @@ -16793,27 +17627,38 @@ _SOKOL_PRIVATE sg_desc _sg_desc_defaults(const sg_desc* desc) { */ sg_desc res = *desc; #if defined(SOKOL_WGPU) - SOKOL_ASSERT(SG_PIXELFORMAT_NONE != res.context.color_format); + SOKOL_ASSERT(SG_PIXELFORMAT_NONE < res.environment.defaults.color_format); #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) - res.context.color_format = _sg_def(res.context.color_format, SG_PIXELFORMAT_BGRA8); + res.environment.defaults.color_format = _sg_def(res.environment.defaults.color_format, SG_PIXELFORMAT_BGRA8); #else - res.context.color_format = _sg_def(res.context.color_format, SG_PIXELFORMAT_RGBA8); + res.environment.defaults.color_format = _sg_def(res.environment.defaults.color_format, SG_PIXELFORMAT_RGBA8); #endif - res.context.depth_format = _sg_def(res.context.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL); - res.context.sample_count = _sg_def(res.context.sample_count, 1); + res.environment.defaults.depth_format = _sg_def(res.environment.defaults.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL); + res.environment.defaults.sample_count = _sg_def(res.environment.defaults.sample_count, 1); res.buffer_pool_size = _sg_def(res.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE); res.image_pool_size = _sg_def(res.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE); res.sampler_pool_size = _sg_def(res.sampler_pool_size, _SG_DEFAULT_SAMPLER_POOL_SIZE); res.shader_pool_size = _sg_def(res.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE); res.pipeline_pool_size = _sg_def(res.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE); - res.pass_pool_size = _sg_def(res.pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE); - res.context_pool_size = _sg_def(res.context_pool_size, _SG_DEFAULT_CONTEXT_POOL_SIZE); + res.attachments_pool_size = _sg_def(res.attachments_pool_size, _SG_DEFAULT_ATTACHMENTS_POOL_SIZE); res.uniform_buffer_size = _sg_def(res.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); res.max_commit_listeners = _sg_def(res.max_commit_listeners, _SG_DEFAULT_MAX_COMMIT_LISTENERS); res.wgpu_bindgroups_cache_size = _sg_def(res.wgpu_bindgroups_cache_size, _SG_DEFAULT_WGPU_BINDGROUP_CACHE_SIZE); return res; } +_SOKOL_PRIVATE sg_pass _sg_pass_defaults(const sg_pass* pass) { + sg_pass res = *pass; + if (res.attachments.id == SG_INVALID_ID) { + // this is a swapchain-pass + res.swapchain.sample_count = _sg_def(res.swapchain.sample_count, _sg.desc.environment.defaults.sample_count); + res.swapchain.color_format = _sg_def(res.swapchain.color_format, _sg.desc.environment.defaults.color_format); + res.swapchain.depth_format = _sg_def(res.swapchain.depth_format, _sg.desc.environment.defaults.depth_format); + } + res.action = _sg_pass_action_defaults(&res.action); + return res; +} + // ██████ ██ ██ ██████ ██ ██ ██████ // ██ ██ ██ ██ ██ ██ ██ ██ ██ // ██████ ██ ██ ██████ ██ ██ ██ @@ -16833,21 +17678,10 @@ SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { _sg.stats_enabled = true; _sg_setup_backend(&_sg.desc); _sg.valid = true; - sg_setup_context(); } SOKOL_API_IMPL void sg_shutdown(void) { - /* can only delete resources for the currently set context here, if multiple - contexts are used, the app code must take care of properly releasing them - (since only the app code can switch between 3D-API contexts) - */ - if (_sg.active_context.id != SG_INVALID_ID) { - _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, _sg.active_context.id); - if (ctx) { - _sg_discard_all_resources(&_sg.pools, _sg.active_context.id); - _sg_discard_context(ctx); - } - } + _sg_discard_all_resources(&_sg.pools); _sg_discard_backend(); _sg_discard_commit_listeners(); _sg_discard_pools(&_sg.pools); @@ -16882,52 +17716,41 @@ SOKOL_API_IMPL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) { SOKOL_ASSERT(_sg.valid); int fmt_index = (int) fmt; SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); - return _sg.formats[fmt_index]; -} - -SOKOL_API_IMPL sg_frame_stats sg_query_frame_stats(void) { - SOKOL_ASSERT(_sg.valid); - return _sg.prev_stats; + const _sg_pixelformat_info_t* src = &_sg.formats[fmt_index]; + sg_pixelformat_info res; + _sg_clear(&res, sizeof(res)); + res.sample = src->sample; + res.filter = src->filter; + res.render = src->render; + res.blend = src->blend; + res.msaa = src->msaa; + res.depth = src->depth; + res.compressed = _sg_is_compressed_pixel_format(fmt); + if (!res.compressed) { + res.bytes_per_pixel = _sg_pixelformat_bytesize(fmt); + } + return res; } -SOKOL_API_IMPL sg_context sg_setup_context(void) { +SOKOL_API_IMPL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes) { SOKOL_ASSERT(_sg.valid); - sg_context res; - int slot_index = _sg_pool_alloc_index(&_sg.pools.context_pool); - if (_SG_INVALID_SLOT_INDEX != slot_index) { - res.id = _sg_slot_alloc(&_sg.pools.context_pool, &_sg.pools.contexts[slot_index].slot, slot_index); - _sg_context_t* ctx = _sg_context_at(&_sg.pools, res.id); - ctx->slot.state = _sg_create_context(ctx); - SOKOL_ASSERT(ctx->slot.state == SG_RESOURCESTATE_VALID); - _sg_activate_context(ctx); - } else { - // pool is exhausted - res.id = SG_INVALID_ID; - } - _sg.active_context = res; - return res; + SOKOL_ASSERT(width > 0); + SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes)); + SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + return _sg_row_pitch(fmt, width, row_align_bytes); } -SOKOL_API_IMPL void sg_discard_context(sg_context ctx_id) { +SOKOL_API_IMPL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes) { SOKOL_ASSERT(_sg.valid); - _sg_discard_all_resources(&_sg.pools, ctx_id.id); - _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); - if (ctx) { - _sg_discard_context(ctx); - _sg_reset_context_to_alloc_state(ctx); - _sg_reset_slot(&ctx->slot); - _sg_pool_free_index(&_sg.pools.context_pool, _sg_slot_index(ctx_id.id)); - } - _sg.active_context.id = SG_INVALID_ID; - _sg_activate_context(0); + SOKOL_ASSERT((width > 0) && (height > 0)); + SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes)); + SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + return _sg_surface_pitch(fmt, width, height, row_align_bytes); } -SOKOL_API_IMPL void sg_activate_context(sg_context ctx_id) { +SOKOL_API_IMPL sg_frame_stats sg_query_frame_stats(void) { SOKOL_ASSERT(_sg.valid); - _sg.active_context = ctx_id; - _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); - // NOTE: ctx can be 0 here if the context is no longer valid - _sg_activate_context(ctx); + return _sg.prev_stats; } SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks) { @@ -16979,10 +17802,10 @@ SOKOL_API_IMPL sg_pipeline sg_alloc_pipeline(void) { return res; } -SOKOL_API_IMPL sg_pass sg_alloc_pass(void) { +SOKOL_API_IMPL sg_attachments sg_alloc_attachments(void) { SOKOL_ASSERT(_sg.valid); - sg_pass res = _sg_alloc_pass(); - _SG_TRACE_ARGS(alloc_pass, res); + sg_attachments res = _sg_alloc_attachments(); + _SG_TRACE_ARGS(alloc_attachments, res); return res; } @@ -17051,17 +17874,17 @@ SOKOL_API_IMPL void sg_dealloc_pipeline(sg_pipeline pip_id) { _SG_TRACE_ARGS(dealloc_pipeline, pip_id); } -SOKOL_API_IMPL void sg_dealloc_pass(sg_pass pass_id) { +SOKOL_API_IMPL void sg_dealloc_attachments(sg_attachments atts_id) { SOKOL_ASSERT(_sg.valid); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass) { - if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { - _sg_dealloc_pass(pass); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_attachments(atts); } else { - _SG_ERROR(DEALLOC_PASS_INVALID_STATE); + _SG_ERROR(DEALLOC_ATTACHMENTS_INVALID_STATE); } } - _SG_TRACE_ARGS(dealloc_pass, pass_id); + _SG_TRACE_ARGS(dealloc_attachments, atts_id); } SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { @@ -17139,19 +17962,19 @@ SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* _SG_TRACE_ARGS(init_pipeline, pip_id, &desc_def); } -SOKOL_API_IMPL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { +SOKOL_API_IMPL void sg_init_attachments(sg_attachments atts_id, const sg_attachments_desc* desc) { SOKOL_ASSERT(_sg.valid); - sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass) { - if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { - _sg_init_pass(pass, &desc_def); - SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)); + sg_attachments_desc desc_def = _sg_attachments_desc_defaults(desc); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_attachments(atts, &desc_def); + SOKOL_ASSERT((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)); } else { - _SG_ERROR(INIT_PASS_INVALID_STATE); + _SG_ERROR(INIT_ATTACHMENTS_INVALID_STATE); } } - _SG_TRACE_ARGS(init_pass, pass_id, &desc_def); + _SG_TRACE_ARGS(init_attachments, atts_id, &desc_def); } SOKOL_API_IMPL void sg_uninit_buffer(sg_buffer buf_id) { @@ -17224,18 +18047,18 @@ SOKOL_API_IMPL void sg_uninit_pipeline(sg_pipeline pip_id) { _SG_TRACE_ARGS(uninit_pipeline, pip_id); } -SOKOL_API_IMPL void sg_uninit_pass(sg_pass pass_id) { +SOKOL_API_IMPL void sg_uninit_attachments(sg_attachments atts_id) { SOKOL_ASSERT(_sg.valid); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass) { - if ((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)) { - _sg_uninit_pass(pass); - SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_ALLOC); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if ((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_attachments(atts); + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_ALLOC); } else { - _SG_ERROR(UNINIT_PASS_INVALID_STATE); + _SG_ERROR(UNINIT_ATTACHMENTS_INVALID_STATE); } } - _SG_TRACE_ARGS(uninit_pass, pass_id); + _SG_TRACE_ARGS(uninit_attachments, atts_id); } SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { @@ -17243,7 +18066,6 @@ SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); if (buf) { if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { - buf->slot.ctx_id = _sg.active_context.id; buf->slot.state = SG_RESOURCESTATE_FAILED; } else { _SG_ERROR(FAIL_BUFFER_INVALID_STATE); @@ -17257,7 +18079,6 @@ SOKOL_API_IMPL void sg_fail_image(sg_image img_id) { _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); if (img) { if (img->slot.state == SG_RESOURCESTATE_ALLOC) { - img->slot.ctx_id = _sg.active_context.id; img->slot.state = SG_RESOURCESTATE_FAILED; } else { _SG_ERROR(FAIL_IMAGE_INVALID_STATE); @@ -17271,7 +18092,6 @@ SOKOL_API_IMPL void sg_fail_sampler(sg_sampler smp_id) { _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); if (smp) { if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { - smp->slot.ctx_id = _sg.active_context.id; smp->slot.state = SG_RESOURCESTATE_FAILED; } else { _SG_ERROR(FAIL_SAMPLER_INVALID_STATE); @@ -17285,7 +18105,6 @@ SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); if (shd) { if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { - shd->slot.ctx_id = _sg.active_context.id; shd->slot.state = SG_RESOURCESTATE_FAILED; } else { _SG_ERROR(FAIL_SHADER_INVALID_STATE); @@ -17299,7 +18118,6 @@ SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); if (pip) { if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { - pip->slot.ctx_id = _sg.active_context.id; pip->slot.state = SG_RESOURCESTATE_FAILED; } else { _SG_ERROR(FAIL_PIPELINE_INVALID_STATE); @@ -17308,18 +18126,17 @@ SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { _SG_TRACE_ARGS(fail_pipeline, pip_id); } -SOKOL_API_IMPL void sg_fail_pass(sg_pass pass_id) { +SOKOL_API_IMPL void sg_fail_attachments(sg_attachments atts_id) { SOKOL_ASSERT(_sg.valid); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass) { - if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { - pass->slot.ctx_id = _sg.active_context.id; - pass->slot.state = SG_RESOURCESTATE_FAILED; + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + atts->slot.state = SG_RESOURCESTATE_FAILED; } else { - _SG_ERROR(FAIL_PASS_INVALID_STATE); + _SG_ERROR(FAIL_ATTACHMENTS_INVALID_STATE); } } - _SG_TRACE_ARGS(fail_pass, pass_id); + _SG_TRACE_ARGS(fail_attachments, atts_id); } SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) { @@ -17357,10 +18174,10 @@ SOKOL_API_IMPL sg_resource_state sg_query_pipeline_state(sg_pipeline pip_id) { return res; } -SOKOL_API_IMPL sg_resource_state sg_query_pass_state(sg_pass pass_id) { +SOKOL_API_IMPL sg_resource_state sg_query_attachments_state(sg_attachments atts_id) { SOKOL_ASSERT(_sg.valid); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - sg_resource_state res = pass ? pass->slot.state : SG_RESOURCESTATE_INVALID; + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + sg_resource_state res = atts ? atts->slot.state : SG_RESOURCESTATE_INVALID; return res; } @@ -17439,19 +18256,19 @@ SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { return pip_id; } -SOKOL_API_IMPL sg_pass sg_make_pass(const sg_pass_desc* desc) { +SOKOL_API_IMPL sg_attachments sg_make_attachments(const sg_attachments_desc* desc) { SOKOL_ASSERT(_sg.valid); SOKOL_ASSERT(desc); - sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); - sg_pass pass_id = _sg_alloc_pass(); - if (pass_id.id != SG_INVALID_ID) { - _sg_pass_t* pass = _sg_pass_at(&_sg.pools, pass_id.id); - SOKOL_ASSERT(pass && (pass->slot.state == SG_RESOURCESTATE_ALLOC)); - _sg_init_pass(pass, &desc_def); - SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)); + sg_attachments_desc desc_def = _sg_attachments_desc_defaults(desc); + sg_attachments atts_id = _sg_alloc_attachments(); + if (atts_id.id != SG_INVALID_ID) { + _sg_attachments_t* atts = _sg_attachments_at(&_sg.pools, atts_id.id); + SOKOL_ASSERT(atts && (atts->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_attachments(atts, &desc_def); + SOKOL_ASSERT((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)); } - _SG_TRACE_ARGS(make_pass, &desc_def, pass_id); - return pass_id; + _SG_TRACE_ARGS(make_attachments, &desc_def, atts_id); + return atts_id; } SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { @@ -17534,59 +18351,66 @@ SOKOL_API_IMPL void sg_destroy_pipeline(sg_pipeline pip_id) { } } -SOKOL_API_IMPL void sg_destroy_pass(sg_pass pass_id) { +SOKOL_API_IMPL void sg_destroy_attachments(sg_attachments atts_id) { SOKOL_ASSERT(_sg.valid); - _SG_TRACE_ARGS(destroy_pass, pass_id); - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass) { - if ((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)) { - _sg_uninit_pass(pass); - SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_ALLOC); + _SG_TRACE_ARGS(destroy_attachments, atts_id); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if ((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_attachments(atts); + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_ALLOC); } - if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { - _sg_dealloc_pass(pass); - SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_INITIAL); + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_attachments(atts); + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_INITIAL); } } } -SOKOL_API_IMPL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height) { - SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(pass_action); - SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); - sg_pass_action pa; - _sg_resolve_default_pass_action(pass_action, &pa); - _sg.cur_pass.id = SG_INVALID_ID; - _sg.pass_valid = true; - _sg_begin_pass(0, &pa, width, height); - _SG_TRACE_ARGS(begin_default_pass, &pa, width, height); -} - -SOKOL_API_IMPL void sg_begin_default_passf(const sg_pass_action* pass_action, float width, float height) { - sg_begin_default_pass(pass_action, (int)width, (int)height); -} - -SOKOL_API_IMPL void sg_begin_pass(sg_pass pass_id, const sg_pass_action* pass_action) { +SOKOL_API_IMPL void sg_begin_pass(const sg_pass* pass) { SOKOL_ASSERT(_sg.valid); - SOKOL_ASSERT(pass_action); - SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); - _sg.cur_pass = pass_id; - _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass && _sg_validate_begin_pass(pass)) { - _sg.pass_valid = true; - sg_pass_action pa; - _sg_resolve_default_pass_action(pass_action, &pa); - _sg_begin_pass(pass, &pa, pass->cmn.width, pass->cmn.height); - _SG_TRACE_ARGS(begin_pass, pass_id, &pa); - } else { - _sg.pass_valid = false; + SOKOL_ASSERT(!_sg.cur_pass.valid); + SOKOL_ASSERT(!_sg.cur_pass.in_pass); + SOKOL_ASSERT(pass); + SOKOL_ASSERT((pass->_start_canary == 0) && (pass->_end_canary == 0)); + const sg_pass pass_def = _sg_pass_defaults(pass); + if (!_sg_validate_begin_pass(&pass_def)) { + return; } + if (pass_def.attachments.id != SG_INVALID_ID) { + // an offscreen pass + SOKOL_ASSERT(_sg.cur_pass.atts == 0); + _sg.cur_pass.atts = _sg_lookup_attachments(&_sg.pools, pass_def.attachments.id); + if (0 == _sg.cur_pass.atts) { + _SG_ERROR(BEGINPASS_ATTACHMENT_INVALID); + return; + } + _sg.cur_pass.atts_id = pass_def.attachments; + _sg.cur_pass.width = _sg.cur_pass.atts->cmn.width; + _sg.cur_pass.height = _sg.cur_pass.atts->cmn.height; + } else { + // a swapchain pass + SOKOL_ASSERT(pass_def.swapchain.width > 0); + SOKOL_ASSERT(pass_def.swapchain.height > 0); + SOKOL_ASSERT(pass_def.swapchain.color_format > SG_PIXELFORMAT_NONE); + SOKOL_ASSERT(pass_def.swapchain.sample_count > 0); + _sg.cur_pass.width = pass_def.swapchain.width; + _sg.cur_pass.height = pass_def.swapchain.height; + _sg.cur_pass.swapchain.color_fmt = pass_def.swapchain.color_format; + _sg.cur_pass.swapchain.depth_fmt = pass_def.swapchain.depth_format; + _sg.cur_pass.swapchain.sample_count = pass_def.swapchain.sample_count; + } + _sg.cur_pass.valid = true; // may be overruled by backend begin-pass functions + _sg.cur_pass.in_pass = true; + _sg_begin_pass(&pass_def); + _SG_TRACE_ARGS(begin_pass, &pass_def); } SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); _sg_stats_add(num_apply_viewport, 1); - if (!_sg.pass_valid) { + if (!_sg.cur_pass.valid) { return; } _sg_apply_viewport(x, y, width, height, origin_top_left); @@ -17599,8 +18423,9 @@ SOKOL_API_IMPL void sg_apply_viewportf(float x, float y, float width, float heig SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); _sg_stats_add(num_apply_scissor_rect, 1); - if (!_sg.pass_valid) { + if (!_sg.cur_pass.valid) { return; } _sg_apply_scissor_rect(x, y, width, height, origin_top_left); @@ -17613,13 +18438,13 @@ SOKOL_API_IMPL void sg_apply_scissor_rectf(float x, float y, float width, float SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); _sg_stats_add(num_apply_pipeline, 1); - _sg.bindings_applied = false; if (!_sg_validate_apply_pipeline(pip_id)) { _sg.next_draw_valid = false; return; } - if (!_sg.pass_valid) { + if (!_sg.cur_pass.valid) { return; } _sg.cur_pipeline = pip_id; @@ -17633,6 +18458,7 @@ SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); SOKOL_ASSERT(bindings); SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); _sg_stats_add(num_apply_bindings, 1); @@ -17640,7 +18466,9 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { _sg.next_draw_valid = false; return; } - _sg.bindings_applied = true; + if (!_sg.cur_pass.valid) { + return; + } _sg_bindings_t bnd; _sg_clear(&bnd, sizeof(bnd)); @@ -17701,6 +18529,19 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { } } + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++, bnd.num_vs_sbufs++) { + if (bindings->vs.storage_buffers[i].id) { + bnd.vs_sbufs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vs.storage_buffers[i].id); + if (bnd.vs_sbufs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vs_sbufs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, bnd.num_fs_imgs++) { if (bindings->fs.images[i].id) { bnd.fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); @@ -17727,6 +18568,18 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { } } + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++, bnd.num_fs_sbufs++) { + if (bindings->fs.storage_buffers[i].id) { + bnd.fs_sbufs[i] = _sg_lookup_buffer(&_sg.pools, bindings->fs.storage_buffers[i].id); + if (bnd.fs_sbufs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.fs_sbufs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } if (_sg.next_draw_valid) { _sg.next_draw_valid &= _sg_apply_bindings(&bnd); _SG_TRACE_ARGS(apply_bindings, bindings); @@ -17735,6 +18588,7 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) { SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); SOKOL_ASSERT((stage == SG_SHADERSTAGE_VS) || (stage == SG_SHADERSTAGE_FS)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); SOKOL_ASSERT(data && data->ptr && (data->size > 0)); @@ -17744,7 +18598,7 @@ SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const _sg.next_draw_valid = false; return; } - if (!_sg.pass_valid) { + if (!_sg.cur_pass.valid) { return; } if (!_sg.next_draw_valid) { @@ -17756,24 +18610,17 @@ SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instances) { SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); SOKOL_ASSERT(base_element >= 0); SOKOL_ASSERT(num_elements >= 0); SOKOL_ASSERT(num_instances >= 0); _sg_stats_add(num_draw, 1); - #if defined(SOKOL_DEBUG) - if (!_sg.bindings_applied) { - _SG_WARN(DRAW_WITHOUT_BINDINGS); - } - #endif - if (!_sg.pass_valid) { + if (!_sg.cur_pass.valid) { return; } if (!_sg.next_draw_valid) { return; } - if (!_sg.bindings_applied) { - return; - } /* attempting to draw with zero elements or instances is not technically an error, but might be handled as an error in the backend API (e.g. on Metal) */ @@ -17786,19 +18633,19 @@ SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instance SOKOL_API_IMPL void sg_end_pass(void) { SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); _sg_stats_add(num_passes, 1); - if (!_sg.pass_valid) { - return; - } + // NOTE: don't exit early if !_sg.cur_pass.valid _sg_end_pass(); - _sg.cur_pass.id = SG_INVALID_ID; _sg.cur_pipeline.id = SG_INVALID_ID; - _sg.pass_valid = false; + _sg_clear(&_sg.cur_pass, sizeof(_sg.cur_pass)); _SG_TRACE_NOARGS(end_pass); } SOKOL_API_IMPL void sg_commit(void) { SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(!_sg.cur_pass.valid); + SOKOL_ASSERT(!_sg.cur_pass.in_pass); _sg_commit(); _sg.stats.frame_index = _sg.frame_index; _sg.prev_stats = _sg.stats; @@ -17965,7 +18812,6 @@ SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) { if (buf) { info.slot.state = buf->slot.state; info.slot.res_id = buf->slot.id; - info.slot.ctx_id = buf->slot.ctx_id; info.update_frame_index = buf->cmn.update_frame_index; info.append_frame_index = buf->cmn.append_frame_index; info.append_pos = buf->cmn.append_pos; @@ -17989,7 +18835,6 @@ SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { if (img) { info.slot.state = img->slot.state; info.slot.res_id = img->slot.id; - info.slot.ctx_id = img->slot.ctx_id; info.upd_frame_index = img->cmn.upd_frame_index; #if defined(SOKOL_D3D11) info.num_slots = 1; @@ -18010,7 +18855,6 @@ SOKOL_API_IMPL sg_sampler_info sg_query_sampler_info(sg_sampler smp_id) { if (smp) { info.slot.state = smp->slot.state; info.slot.res_id = smp->slot.id; - info.slot.ctx_id = smp->slot.ctx_id; } return info; } @@ -18023,7 +18867,6 @@ SOKOL_API_IMPL sg_shader_info sg_query_shader_info(sg_shader shd_id) { if (shd) { info.slot.state = shd->slot.state; info.slot.res_id = shd->slot.id; - info.slot.ctx_id = shd->slot.ctx_id; } return info; } @@ -18036,20 +18879,18 @@ SOKOL_API_IMPL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip_id) { if (pip) { info.slot.state = pip->slot.state; info.slot.res_id = pip->slot.id; - info.slot.ctx_id = pip->slot.ctx_id; } return info; } -SOKOL_API_IMPL sg_pass_info sg_query_pass_info(sg_pass pass_id) { +SOKOL_API_IMPL sg_attachments_info sg_query_attachments_info(sg_attachments atts_id) { SOKOL_ASSERT(_sg.valid); - sg_pass_info info; + sg_attachments_info info; _sg_clear(&info, sizeof(info)); - const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass) { - info.slot.state = pass->slot.state; - info.slot.res_id = pass->slot.id; - info.slot.ctx_id = pass->slot.ctx_id; + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + info.slot.state = atts->slot.state; + info.slot.res_id = atts->slot.id; } return info; } @@ -18173,20 +19014,20 @@ SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip_id) { return desc; } -SOKOL_API_IMPL sg_pass_desc sg_query_pass_desc(sg_pass pass_id) { +SOKOL_API_IMPL sg_attachments_desc sg_query_attachments_desc(sg_attachments atts_id) { SOKOL_ASSERT(_sg.valid); - sg_pass_desc desc; + sg_attachments_desc desc; _sg_clear(&desc, sizeof(desc)); - const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); - if (pass) { - for (int i = 0; i < pass->cmn.num_color_atts; i++) { - desc.color_attachments[i].image = pass->cmn.color_atts[i].image_id; - desc.color_attachments[i].mip_level = pass->cmn.color_atts[i].mip_level; - desc.color_attachments[i].slice = pass->cmn.color_atts[i].slice; + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + for (int i = 0; i < atts->cmn.num_colors; i++) { + desc.colors[i].image = atts->cmn.colors[i].image_id; + desc.colors[i].mip_level = atts->cmn.colors[i].mip_level; + desc.colors[i].slice = atts->cmn.colors[i].slice; } - desc.depth_stencil_attachment.image = pass->cmn.ds_att.image_id; - desc.depth_stencil_attachment.mip_level = pass->cmn.ds_att.mip_level; - desc.depth_stencil_attachment.slice = pass->cmn.ds_att.slice; + desc.depth_stencil.image = atts->cmn.depth_stencil.image_id; + desc.depth_stencil.mip_level = atts->cmn.depth_stencil.mip_level; + desc.depth_stencil.slice = atts->cmn.depth_stencil.slice; } return desc; } @@ -18216,29 +19057,142 @@ SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_des return _sg_pipeline_desc_defaults(desc); } -SOKOL_API_IMPL sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) { +SOKOL_API_IMPL sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc* desc) { SOKOL_ASSERT(_sg.valid && desc); - return _sg_pass_desc_defaults(desc); + return _sg_attachments_desc_defaults(desc); } SOKOL_API_IMPL const void* sg_d3d11_device(void) { -#if defined(SOKOL_D3D11) - return (const void*) _sg.d3d11.dev; -#else - return 0; -#endif + #if defined(SOKOL_D3D11) + return (const void*) _sg.d3d11.dev; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_d3d11_device_context(void) { + #if defined(SOKOL_D3D11) + return (const void*) _sg.d3d11.ctx; + #else + return 0; + #endif +} + +SOKOL_API_IMPL sg_d3d11_buffer_info sg_d3d11_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + res.buf = (const void*) buf->d3d11.buf; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_image_info sg_d3d11_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + res.tex2d = (const void*) img->d3d11.tex2d; + res.tex3d = (const void*) img->d3d11.tex3d; + res.res = (const void*) img->d3d11.res; + res.srv = (const void*) img->d3d11.srv; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_sampler_info sg_d3d11_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + res.smp = (const void*) smp->d3d11.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_shader_info sg_d3d11_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + res.vs_cbufs[i] = (const void*) shd->d3d11.stage[SG_SHADERSTAGE_VS].cbufs[i]; + res.fs_cbufs[i] = (const void*) shd->d3d11.stage[SG_SHADERSTAGE_FS].cbufs[i]; + } + res.vs = (const void*) shd->d3d11.vs; + res.fs = (const void*) shd->d3d11.fs; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_pipeline_info sg_d3d11_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + res.il = (const void*) pip->d3d11.il; + res.rs = (const void*) pip->d3d11.rs; + res.dss = (const void*) pip->d3d11.dss; + res.bs = (const void*) pip->d3d11.bs; + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_attachments_info sg_d3d11_query_attachments_info(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_attachments_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.color_rtv[i] = (const void*) atts->d3d11.colors[i].view.rtv; + res.resolve_rtv[i] = (const void*) atts->d3d11.resolves[i].view.rtv; + } + res.dsv = (const void*) atts->d3d11.depth_stencil.view.dsv; + } + #else + _SOKOL_UNUSED(atts_id); + #endif + return res; } SOKOL_API_IMPL const void* sg_mtl_device(void) { -#if defined(SOKOL_METAL) - if (nil != _sg.mtl.device) { - return (__bridge const void*) _sg.mtl.device; - } else { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.device) { + return (__bridge const void*) _sg.mtl.device; + } else { + return 0; + } + #else return 0; - } -#else - return 0; -#endif + #endif } SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { @@ -18253,9 +19207,116 @@ SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { #endif } +SOKOL_API_IMPL sg_mtl_buffer_info sg_mtl_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + if (buf->mtl.buf[i] != 0) { + res.buf[i] = (__bridge void*) _sg_mtl_id(buf->mtl.buf[i]); + } + } + res.active_slot = buf->cmn.active_slot; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_image_info sg_mtl_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + if (img->mtl.tex[i] != 0) { + res.tex[i] = (__bridge void*) _sg_mtl_id(img->mtl.tex[i]); + } + } + res.active_slot = img->cmn.active_slot; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_sampler_info sg_mtl_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->mtl.sampler_state != 0) { + res.smp = (__bridge void*) _sg_mtl_id(smp->mtl.sampler_state); + } + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_shader_info sg_mtl_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + const int vs_lib = shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib; + const int vs_func = shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func; + const int fs_lib = shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib; + const int fs_func = shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func; + if (vs_lib != 0) { + res.vs_lib = (__bridge void*) _sg_mtl_id(vs_lib); + } + if (fs_lib != 0) { + res.fs_lib = (__bridge void*) _sg_mtl_id(fs_lib); + } + if (vs_func != 0) { + res.vs_func = (__bridge void*) _sg_mtl_id(vs_func); + } + if (fs_func != 0) { + res.fs_func = (__bridge void*) _sg_mtl_id(fs_func); + } + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_pipeline_info sg_mtl_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->mtl.rps != 0) { + res.rps = (__bridge void*) _sg_mtl_id(pip->mtl.rps); + } + if (pip->mtl.dss != 0) { + res.dss = (__bridge void*) _sg_mtl_id(pip->mtl.dss); + } + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + SOKOL_API_IMPL const void* sg_wgpu_device(void) { #if defined(SOKOL_WGPU) - return _sg.wgpu.dev; + return (const void*) _sg.wgpu.dev; #else return 0; #endif @@ -18263,7 +19324,7 @@ SOKOL_API_IMPL const void* sg_wgpu_device(void) { SOKOL_API_IMPL const void* sg_wgpu_queue(void) { #if defined(SOKOL_WGPU) - return _sg.wgpu.queue; + return (const void*) _sg.wgpu.queue; #else return 0; #endif @@ -18271,7 +19332,7 @@ SOKOL_API_IMPL const void* sg_wgpu_queue(void) { SOKOL_API_IMPL const void* sg_wgpu_command_encoder(void) { #if defined(SOKOL_WGPU) - return _sg.wgpu.cmd_enc; + return (const void*) _sg.wgpu.cmd_enc; #else return 0; #endif @@ -18279,12 +19340,195 @@ SOKOL_API_IMPL const void* sg_wgpu_command_encoder(void) { SOKOL_API_IMPL const void* sg_wgpu_render_pass_encoder(void) { #if defined(SOKOL_WGPU) - return _sg.wgpu.pass_enc; + return (const void*) _sg.wgpu.pass_enc; #else return 0; #endif } +SOKOL_API_IMPL sg_wgpu_buffer_info sg_wgpu_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + res.buf = (const void*) buf->wgpu.buf; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_image_info sg_wgpu_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + res.tex = (const void*) img->wgpu.tex; + res.view = (const void*) img->wgpu.view; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_sampler_info sg_wgpu_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + res.smp = (const void*) smp->wgpu.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_shader_info sg_wgpu_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + res.vs_mod = (const void*) shd->wgpu.stage[SG_SHADERSTAGE_VS].module; + res.fs_mod = (const void*) shd->wgpu.stage[SG_SHADERSTAGE_FS].module; + res.bgl = (const void*) shd->wgpu.bind_group_layout; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_pipeline_info sg_wgpu_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + res.pip = (const void*) pip->wgpu.pip; + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_attachments_info sg_wgpu_query_attachments_info(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_attachments_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.color_view[i] = (const void*) atts->wgpu.colors[i].view; + res.resolve_view[i] = (const void*) atts->wgpu.resolves[i].view; + } + res.ds_view = (const void*) atts->wgpu.depth_stencil.view; + } + #else + _SOKOL_UNUSED(atts_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_buffer_info sg_gl_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + res.buf[i] = buf->gl.buf[i]; + } + res.active_slot = buf->cmn.active_slot; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_image_info sg_gl_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + res.tex[i] = img->gl.tex[i]; + } + res.tex_target = img->gl.target; + res.msaa_render_buffer = img->gl.msaa_render_buffer; + res.active_slot = img->cmn.active_slot; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_sampler_info sg_gl_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + res.smp = smp->gl.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_shader_info sg_gl_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + res.prog = shd->gl.prog; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_attachments_info sg_gl_query_attachments_info(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_attachments_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + res.framebuffer = atts->gl.fb; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.msaa_resolve_framebuffer[i] = atts->gl.msaa_resolve_framebuffer[i]; + } + } + #else + _SOKOL_UNUSED(atts_id); + #endif + return res; +} + #ifdef _MSC_VER #pragma warning(pop) #endif