diff --git a/.gitignore b/.gitignore index 4d2ba95..e6165a5 100644 --- a/.gitignore +++ b/.gitignore @@ -109,3 +109,5 @@ modules.order Module.symvers Mkfile.old dkms.conf + +emsdk/ diff --git a/build_web_common.bat b/build_web_common.bat index be59b2e..d9c3744 100644 --- a/build_web_common.bat +++ b/build_web_common.bat @@ -15,6 +15,7 @@ copy marketing_page\favicon.ico %OUTPUT_FOLDER%\favicon.ico emcc ^ -sEXPORTED_FUNCTIONS=_main,_end_text_input,_stop_controlling_input,_start_controlling_input,_read_from_save_data,_dump_save_data,_is_receiving_text_input^ -sEXPORTED_RUNTIME_METHODS=ccall,cwrap^ + -s USE_WEBGL2=1^ -s INITIAL_MEMORY=62914560^ -s ALLOW_MEMORY_GROWTH -s TOTAL_STACK=15728640^ %FLAGS%^ diff --git a/main.c b/main.c index 0503d00..e99ce8d 100644 --- a/main.c +++ b/main.c @@ -26,8 +26,8 @@ #if defined(__EMSCRIPTEN__) #define WEB -#define SOKOL_GLES2 -#define SAMPLE_COUNT 1 // bumping this back to 4 is troublesome for web, because there's a mismatch in sample counts. Perhaps we must upgrade to gles3, in doing so, we should upgrade to the newest sokol gfx. +#define SOKOL_GLES3 +#define SAMPLE_COUNT 4 #endif #define DRWAV_ASSERT game_assert @@ -58,11 +58,7 @@ __declspec(dllexport) uint32_t AmdPowerXpressRequestHighPerformance = 0x00000001 #pragma warning(push) #pragma warning(disable : 4191) // unsafe function calling #ifdef WEB -# ifndef GL_EXT_PROTOTYPES -# define GL_GLEXT_PROTOTYPES -# endif -# include -# include +# include # undef glGetError # define glGetError() (GL_NO_ERROR) #endif @@ -843,17 +839,14 @@ sg_image load_image(MD_String8 path) .width = png_width, .height = png_height, .pixel_format = SG_PIXELFORMAT_RGBA8, - .min_filter = SG_FILTER_LINEAR, .num_mipmaps = 1, - .wrap_u = SG_WRAP_CLAMP_TO_EDGE, - .wrap_v = SG_WRAP_CLAMP_TO_EDGE, - .mag_filter = SG_FILTER_LINEAR, .data.subimage[0][0] = { .ptr = pixels, .size = (size_t)(png_width * png_height * num_channels), } }); + loaded->image = to_return; MD_ReleaseScratch(scratch); return to_return; @@ -1232,14 +1225,6 @@ Armature load_armature(MD_Arena *arena, MD_String8 binary_file, MD_String8 armat .width = to_return.bones_texture_width, .height = to_return.bones_texture_height, .pixel_format = SG_PIXELFORMAT_RGBA8, - .min_filter = SG_FILTER_NEAREST, - .mag_filter = SG_FILTER_NEAREST, - - // for webgl NPOT texures https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL - .wrap_u = SG_WRAP_CLAMP_TO_EDGE, - .wrap_v = SG_WRAP_CLAMP_TO_EDGE, - .wrap_w = SG_WRAP_CLAMP_TO_EDGE, - .usage = SG_USAGE_STREAM, }); @@ -2670,17 +2655,23 @@ static struct sg_bindings threedee_bind; sg_image outline_pass_image; + sg_image outline_pass_resolve_image; sg_pass outline_pass; sg_pipeline outline_mesh_pip; sg_pipeline outline_armature_pip; sg_pass threedee_pass; // is a pass so I can do post processing in a shader sg_image threedee_pass_image; + sg_image threedee_pass_resolve_image; sg_image threedee_pass_depth_image; sg_pipeline twodee_outline_pip; sg_pipeline twodee_colorcorrect_pip; + sg_sampler sampler_linear; + sg_sampler sampler_linear_border; + sg_sampler sampler_nearest; + Shadow_State shadows; } state; @@ -2695,7 +2686,9 @@ void create_screenspace_gfx_state() MAYBE_DESTROY(state.threedee_pass, sg_destroy_pass); MAYBE_DESTROY(state.outline_pass_image, sg_destroy_image); + MAYBE_DESTROY(state.outline_pass_resolve_image, sg_destroy_image); MAYBE_DESTROY(state.threedee_pass_image, sg_destroy_image); + MAYBE_DESTROY(state.threedee_pass_resolve_image, sg_destroy_image); MAYBE_DESTROY(state.threedee_pass_depth_image, sg_destroy_image); #undef MAYBE_DESTROY @@ -2766,34 +2759,40 @@ void create_screenspace_gfx_state() .width = sapp_width(), .height = sapp_height(), .pixel_format = SG_PIXELFORMAT_RGBA8, - .min_filter = SG_FILTER_LINEAR, - .mag_filter = SG_FILTER_LINEAR, - .wrap_u = SG_WRAP_CLAMP_TO_BORDER, - .wrap_v = SG_WRAP_CLAMP_TO_BORDER, - .border_color = SG_BORDERCOLOR_OPAQUE_WHITE, - .sample_count = SAMPLE_COUNT, .label = "outline-pass-render-target", }; + + desc.sample_count = SAMPLE_COUNT; state.outline_pass_image = sg_make_image(&desc); + desc.sample_count = 1; + state.outline_pass_resolve_image = sg_make_image(&desc); state.outline_pass = sg_make_pass(&(sg_pass_desc){ .color_attachments[0].image = state.outline_pass_image, + .resolve_attachments[0].image = state.outline_pass_resolve_image, .depth_stencil_attachment = { 0 }, .label = "outline-pass", }); - desc.sample_count = SAMPLE_COUNT; + desc.sample_count = 1; desc.label = "threedee-pass-render-target"; + desc.sample_count = SAMPLE_COUNT; state.threedee_pass_image = sg_make_image(&desc); + desc.label = "threedee-pass-resolve-render-target"; + desc.sample_count = 1; + state.threedee_pass_resolve_image = sg_make_image(&desc); + desc.label = "threedee-pass-depth-render-target"; desc.pixel_format = sapp_depth_format(); + desc.sample_count = SAMPLE_COUNT; state.threedee_pass_depth_image = sg_make_image(&desc); state.threedee_pass = sg_make_pass(&(sg_pass_desc){ .color_attachments[0].image = state.threedee_pass_image, + .resolve_attachments[0].image = state.threedee_pass_resolve_image, .depth_stencil_attachment = (sg_pass_attachment_desc){ .image = state.threedee_pass_depth_image, .mip_level = 0, @@ -3013,8 +3012,8 @@ Color blendalpha(Color c, float alpha) // in pixels Vec2 img_size(sg_image img) { - sg_image_info info = sg_query_image_info(img); - return V2((float)info.width, (float)info.height); + sg_image_desc desc = sg_query_image_desc(img); + return V2((float)desc.width, (float)desc.height); } #ifdef DEVTOOLS @@ -3136,8 +3135,6 @@ LoadedFont load_font(MD_Arena *arena, MD_String8 font_filepath, float font_size) .width = font_bitmap_width, .height = font_bitmap_width, .pixel_format = SG_PIXELFORMAT_RGBA8, - .min_filter = SG_FILTER_LINEAR, - .mag_filter = SG_FILTER_LINEAR, .data.subimage[0][0] = { .ptr = font_bitmap_rgba, @@ -3212,6 +3209,7 @@ void init(void) sg_setup(&(sg_desc) { .context = sapp_sgcontext(), .buffer_pool_size = 512, + .logger.func = slog_func, }); stm_setup(); saudio_setup(&(saudio_desc) { @@ -3380,6 +3378,7 @@ void init(void) .compare = SG_COMPAREFUNC_LESS_EQUAL, .write_enabled = true, }, + .sample_count = SAMPLE_COUNT, .colors[0].blend = (sg_blend_state){ // allow transparency .enabled = true, @@ -3405,6 +3404,7 @@ void init(void) .compare = SG_COMPAREFUNC_LESS_EQUAL, .write_enabled = true }, + .sample_count = SAMPLE_COUNT, .layout = { .attrs = { @@ -3428,11 +3428,42 @@ void init(void) state.clear_depth_buffer_pass_action = (sg_pass_action) { - .colors[0] = { .action = SG_ACTION_LOAD }, - .depth = { .action = SG_ACTION_CLEAR, .value = 1.0f }, + .colors[0] = { .load_action = SG_LOADACTION_LOAD }, + .depth = { .load_action = SG_LOADACTION_CLEAR, .clear_value = 1.0f }, }; state.clear_everything_pass_action = state.clear_depth_buffer_pass_action; - state.clear_everything_pass_action.colors[0] = (sg_color_attachment_action){ .action = SG_ACTION_CLEAR, .value = { clearcol.r, clearcol.g, clearcol.b, 1.0f } }; + state.clear_everything_pass_action.colors[0] = (sg_color_attachment_action){ .load_action = SG_LOADACTION_CLEAR, .clear_value = { clearcol.r, clearcol.g, clearcol.b, 1.0f } }; + + state.sampler_linear = sg_make_sampler(&(sg_sampler_desc) { + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + // .mipmap_filter = SG_FILTER_LINEAR, + .wrap_u = SG_WRAP_CLAMP_TO_EDGE, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE, + // .max_anisotropy = 16, + .label = "sampler-linear", + }); + + state.sampler_linear_border = sg_make_sampler(&(sg_sampler_desc) { + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .wrap_u = SG_WRAP_CLAMP_TO_BORDER, + .wrap_v = SG_WRAP_CLAMP_TO_BORDER, + .border_color = SG_BORDERCOLOR_OPAQUE_WHITE, + .label = "sampler-linear-border", + }); + + state.sampler_nearest = sg_make_sampler(&(sg_sampler_desc) { + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_NEAREST, + + // for webgl NPOT texures https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL + .wrap_u = SG_WRAP_CLAMP_TO_EDGE, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE, + .wrap_w = SG_WRAP_CLAMP_TO_EDGE, + + .label = "sampler-nearest", + }); } Vec2 screen_size() @@ -3723,7 +3754,8 @@ void flush_quad_batch() state.bind.vertex_buffer_offsets[0] = sg_append_buffer(state.bind.vertex_buffers[0], &(sg_range) { cur_batch_data, cur_batch_data_index*sizeof(*cur_batch_data) }); - state.bind.fs_images[SLOT_threedee_twodee_tex] = cur_batch_image; + state.bind.fs.images[SLOT_threedee_twodee_tex] = cur_batch_image; // NOTE that this might get FUCKED if a custom pipeline is provided with more/less texture slots!!! + state.bind.fs.samplers[SLOT_threedee_fs_twodee_smp] = state.sampler_linear; // NOTE that this might get FUCKED if a custom pipeline is provided with more/less sampler slots!!! sg_apply_bindings(&state.bind); cur_batch_params.tex_size = img_size(cur_batch_image); sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_threedee_twodee_fs_params, &SG_RANGE(cur_batch_params)); @@ -4827,8 +4859,8 @@ Shadow_State init_shadow_state() { shadows.pass_action = (sg_pass_action) { .colors[0] = { - .action = SG_ACTION_CLEAR, - .value = { 1.0f, 1.0f, 1.0f, 1.0f } + .load_action = SG_LOADACTION_CLEAR, + .clear_value = { 1.0f, 1.0f, 1.0f, 1.0f } } }; @@ -4838,18 +4870,14 @@ Shadow_State init_shadow_state() { be pertinent to just dig into sokol and add the functionality we want later, but as a first pass, we will just do as the romans do. I.e. have both a colour and depth component. - Canada Day 2023. + TODO: with GLES3, depth only rendering passes are now supported */ sg_image_desc img_desc = { .render_target = true, .width = SHADOW_MAP_DIMENSION, .height = SHADOW_MAP_DIMENSION, .pixel_format = SG_PIXELFORMAT_RGBA8, - .min_filter = SG_FILTER_LINEAR, - .mag_filter = SG_FILTER_LINEAR, - .wrap_u = SG_WRAP_CLAMP_TO_BORDER, - .wrap_v = SG_WRAP_CLAMP_TO_BORDER, - .border_color = SG_BORDERCOLOR_OPAQUE_WHITE, - .sample_count = SAMPLE_COUNT, + .sample_count = 1, .label = "shadow-map-color-image" }; shadows.color_img = sg_make_image(&img_desc); @@ -4873,7 +4901,7 @@ Shadow_State init_shadow_state() { .shader = sg_make_shader(threedee_mesh_shadow_mapping_shader_desc(sg_query_backend())), // Cull front faces in the shadow map pass // .cull_mode = SG_CULLMODE_BACK, - .sample_count = SAMPLE_COUNT, + .sample_count = 1, .depth = { .pixel_format = SG_PIXELFORMAT_DEPTH, .compare = SG_COMPAREFUNC_LESS_EQUAL, @@ -4887,7 +4915,7 @@ Shadow_State init_shadow_state() { desc.label = "armature-shadow-map-pipeline"; desc.shader = sg_make_shader(threedee_armature_shadow_mapping_shader_desc(sg_query_backend())); - sg_vertex_attr_desc skeleton_vertex_attrs[] = { + sg_vertex_attr_state skeleton_vertex_attrs[] = { [ATTR_threedee_vs_skeleton_pos_in].format = SG_VERTEXFORMAT_FLOAT3, [ATTR_threedee_vs_skeleton_uv_in].format = SG_VERTEXFORMAT_FLOAT2, [ATTR_threedee_vs_skeleton_indices_in].format = SG_VERTEXFORMAT_USHORT4N, @@ -5090,9 +5118,17 @@ void actually_draw_thing(DrawnThing *it, Mat4 light_space_matrix, bool for_outli else sg_apply_pipeline(state.threedee_pip); sg_bindings bindings = {0}; - bindings.fs_images[SLOT_threedee_tex] = it->mesh->image; - if(!for_outline) - bindings.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img; + bindings.fs.images[SLOT_threedee_tex] = it->mesh->image; + if(for_outline) + { + bindings.fs.samplers[SLOT_threedee_fs_outline_smp] = state.sampler_linear; + } + else + { + bindings.fs.images[SLOT_threedee_shadow_map] = state.shadows.color_img; + bindings.fs.samplers[SLOT_threedee_fs_shadow_smp] = state.sampler_linear_border; + bindings.fs.samplers[SLOT_threedee_fs_smp] = state.sampler_linear; + } bindings.vertex_buffers[0] = it->mesh->loaded_buffer; sg_apply_bindings(&bindings); @@ -5118,10 +5154,19 @@ void actually_draw_thing(DrawnThing *it, Mat4 light_space_matrix, bool for_outli else sg_apply_pipeline(state.armature_pip); sg_bindings bindings = {0}; - bindings.vs_images[SLOT_threedee_bones_tex] = it->armature->bones_texture; - bindings.fs_images[SLOT_threedee_tex] = it->armature->image; - if(!for_outline) - bindings.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img; + bindings.vs.images[SLOT_threedee_bones_tex] = it->armature->bones_texture; + bindings.vs.samplers[SLOT_threedee_vs_skeleton_smp] = state.sampler_nearest; + bindings.fs.images[SLOT_threedee_tex] = it->armature->image; + if(for_outline) + { + bindings.fs.samplers[SLOT_threedee_fs_outline_smp] = state.sampler_linear; + } + else + { + bindings.fs.images[SLOT_threedee_shadow_map] = state.shadows.color_img; + bindings.fs.samplers[SLOT_threedee_fs_shadow_smp] = state.sampler_linear_border; + bindings.fs.samplers[SLOT_threedee_fs_smp] = state.sampler_linear; + } bindings.vertex_buffers[0] = it->armature->loaded_buffer; sg_apply_bindings(&bindings); @@ -5265,7 +5310,8 @@ void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3 if(it->mesh) { sg_bindings bindings = {0}; - bindings.fs_images[SLOT_threedee_tex] = it->mesh->image; + bindings.fs.images[SLOT_threedee_tex] = it->mesh->image; + bindings.fs.samplers[SLOT_threedee_fs_shadow_mapping_smp] = state.sampler_linear; bindings.vertex_buffers[0] = it->mesh->loaded_buffer; sg_apply_bindings(&bindings); @@ -5290,8 +5336,10 @@ void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3 if(it->armature) { sg_bindings bindings = {0}; - bindings.vs_images[SLOT_threedee_bones_tex] = it->armature->bones_texture; - bindings.fs_images[SLOT_threedee_tex] = it->armature->image; + bindings.vs.images[SLOT_threedee_bones_tex] = it->armature->bones_texture; + bindings.vs.samplers[SLOT_threedee_vs_skeleton_smp] = state.sampler_nearest; + bindings.fs.images[SLOT_threedee_tex] = it->armature->image; + bindings.fs.samplers[SLOT_threedee_fs_shadow_mapping_smp] = state.sampler_linear; bindings.vertex_buffers[0] = it->armature->loaded_buffer; sg_apply_bindings(&bindings); @@ -5317,8 +5365,8 @@ void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3 { sg_begin_pass(state.outline_pass, &(sg_pass_action) { .colors[0] = { - .action = SG_ACTION_CLEAR, - .value = { 0.0f, 0.0f, 0.0f, 0.0f }, + .load_action = SG_LOADACTION_CLEAR, + .clear_value = { 0.0f, 0.0f, 0.0f, 0.0f }, } }); @@ -5674,8 +5722,8 @@ void frame(void) flush_all_drawn_things(light_dir, cam_pos, facing, right); // draw the 3d render - draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y), screen_size()), IMG(state.threedee_pass_image), WHITE, .layer = LAYER_WORLD, .custom_pipeline = state.twodee_colorcorrect_pip }); - draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y), screen_size()), IMG(state.outline_pass_image), WHITE, .custom_pipeline = state.twodee_outline_pip, .layer = LAYER_UI}); + draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y), screen_size()), IMG(state.threedee_pass_resolve_image), WHITE, .layer = LAYER_WORLD, .custom_pipeline = state.twodee_colorcorrect_pip }); + draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y), screen_size()), IMG(state.outline_pass_resolve_image), WHITE, .custom_pipeline = state.twodee_outline_pip, .layer = LAYER_UI}); // 2d drawing TODO move this to when the drawing is flushed. sg_begin_default_pass(&state.clear_depth_buffer_pass_action, sapp_width(), sapp_height()); @@ -6949,7 +6997,7 @@ ISANERROR("Don't know how to do this stuff on this platform.") { Vec2 depth_size = V2(200.0f, 200.0f); draw_quad((DrawParams){quad_at(V2(screen_size().x - depth_size.x, screen_size().y), depth_size), IMG(state.shadows.color_img), WHITE, .layer = LAYER_UI_FG}); - draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y/2.0f), MulV2F(screen_size(), 0.1f)), IMG(state.outline_pass_image), WHITE, .layer = LAYER_UI_FG}); + draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y/2.0f), MulV2F(screen_size(), 0.1f)), IMG(state.outline_pass_resolve_image), WHITE, .layer = LAYER_UI_FG}); Vec3 view_cam_pos = MulM4V4(InvGeneralM4(view), V4(0,0,0,1)).xyz; //if(view_cam_pos.y >= 4.900f) // causes nan if not true... not good... @@ -7110,10 +7158,10 @@ ISANERROR("Don't know how to do this stuff on this platform.") }; // convert to uv space - sg_image_info info = sg_query_image_info(d.image); + sg_image_desc desc = sg_query_image_desc(d.image); for (int i = 0; i < 4; i++) { - tex_coords[i] = DivV2(tex_coords[i], V2((float)info.width, (float)info.height)); + tex_coords[i] = DivV2(tex_coords[i], V2((float)desc.width, (float)desc.height)); } for (int i = 0; i < 4; i++) { @@ -7481,13 +7529,13 @@ sapp_desc sokol_main(int argc, char* argv[]) .frame_cb = frame, .cleanup_cb = cleanup, .event_cb = event, - .sample_count = SAMPLE_COUNT, + .sample_count = 1, .width = 800, .height = 600, - //.gl_force_gles2 = true, not sure why this was here in example, look into .window_title = "Dante's Cowboy", .win32_console_attach = true, .win32_console_create = true, .icon.sokol_default = true, + .logger.func = slog_func, }; } diff --git a/run_codegen.bat b/run_codegen.bat index 1d66612..60b2b84 100644 --- a/run_codegen.bat +++ b/run_codegen.bat @@ -9,7 +9,7 @@ if exist gen\ ( ) @REM shaders -thirdparty\sokol-shdc.exe --input threedee.glsl --output gen\threedee.glsl.h --slang glsl100:hlsl5:metal_macos:glsl330 || goto :error +thirdparty\sokol-shdc.exe --input threedee.glsl --output gen\threedee.glsl.h --slang glsl300es:hlsl5:glsl330 || goto :error @REM metadesk codegen cl /nologo /diagnostics:caret /Ithirdparty /W3 /Zi /WX codegen.c || goto :error diff --git a/thirdparty/sokol-shdc.exe b/thirdparty/sokol-shdc.exe index 39c337b..b879f41 100644 Binary files a/thirdparty/sokol-shdc.exe and b/thirdparty/sokol-shdc.exe differ diff --git a/thirdparty/sokol_app.h b/thirdparty/sokol_app.h index 30db44d..a1f45b6 100644 --- a/thirdparty/sokol_app.h +++ b/thirdparty/sokol_app.h @@ -19,7 +19,6 @@ project): #define SOKOL_GLCORE33 - #define SOKOL_GLES2 #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL @@ -29,7 +28,6 @@ SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) - SOKOL_ABORT() - called after an unrecoverable error (default: abort()) SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function SOKOL_APP_API_DECL - public function declaration prefix (default: extern) @@ -77,10 +75,6 @@ On Linux, you also need to use the -pthread compiler and linker option, otherwise weird things will happen, see here for details: https://github.com/floooh/sokol/issues/376 - Building for UWP requires a recent Visual Studio toolchain and Windows SDK - (at least VS2019 and Windows SDK 10.0.19041.0). When the UWP backend is - selected, the sokol_app.h implementation must be compiled as C++17. - On macOS and iOS, the implementation must be compiled as Objective-C. FEATURE OVERVIEW @@ -93,52 +87,51 @@ - makes the rendered frame visible - provides keyboard-, mouse- and low-level touch-events - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android - - 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2 + - 3D-APIs: Metal, D3D11, GL3.2, GLES3, WebGL, WebGL2 FEATURE/PLATFORM MATRIX ======================= - | Windows | macOS | Linux | iOS | Android | UWP | HTML5 - --------------------+---------+-------+-------+-------+---------+------+------- - gl 3.x | YES | YES | YES | --- | --- | --- | --- - gles2/webgl | --- | --- | YES(2)| YES | YES | --- | YES - gles3/webgl2 | --- | --- | YES(2)| YES | YES | --- | YES - metal | --- | YES | --- | YES | --- | --- | --- - d3d11 | YES | --- | --- | --- | --- | YES | --- - KEY_DOWN | YES | YES | YES | SOME | TODO | YES | YES - KEY_UP | YES | YES | YES | SOME | TODO | YES | YES - CHAR | YES | YES | YES | YES | TODO | YES | YES - MOUSE_DOWN | YES | YES | YES | --- | --- | YES | YES - MOUSE_UP | YES | YES | YES | --- | --- | YES | YES - MOUSE_SCROLL | YES | YES | YES | --- | --- | YES | YES - MOUSE_MOVE | YES | YES | YES | --- | --- | YES | YES - MOUSE_ENTER | YES | YES | YES | --- | --- | YES | YES - MOUSE_LEAVE | YES | YES | YES | --- | --- | YES | YES - TOUCHES_BEGAN | --- | --- | --- | YES | YES | TODO | YES - TOUCHES_MOVED | --- | --- | --- | YES | YES | TODO | YES - TOUCHES_ENDED | --- | --- | --- | YES | YES | TODO | YES - TOUCHES_CANCELLED | --- | --- | --- | YES | YES | TODO | YES - RESIZED | YES | YES | YES | YES | YES | YES | YES - ICONIFIED | YES | YES | YES | --- | --- | YES | --- - RESTORED | YES | YES | YES | --- | --- | YES | --- - FOCUSED | YES | YES | YES | --- | --- | --- | YES - UNFOCUSED | YES | YES | YES | --- | --- | --- | YES - SUSPENDED | --- | --- | --- | YES | YES | YES | TODO - RESUMED | --- | --- | --- | YES | YES | YES | TODO - QUIT_REQUESTED | YES | YES | YES | --- | --- | --- | YES - IME | TODO | TODO? | TODO | ??? | TODO | --- | ??? - key repeat flag | YES | YES | YES | --- | --- | YES | YES - windowed | YES | YES | YES | --- | --- | YES | YES - fullscreen | YES | YES | YES | YES | YES | YES | --- - mouse hide | YES | YES | YES | --- | --- | YES | YES - mouse lock | YES | YES | YES | --- | --- | TODO | YES - set cursor type | YES | YES | YES | --- | --- | YES | YES - screen keyboard | --- | --- | --- | YES | TODO | TODO | YES - swap interval | YES | YES | YES | YES | TODO | --- | YES - high-dpi | YES | YES | TODO | YES | YES | YES | YES - clipboard | YES | YES | TODO | --- | --- | TODO | YES - MSAA | YES | YES | YES | YES | YES | TODO | YES - drag'n'drop | YES | YES | YES | --- | --- | TODO | YES - window icon | YES | YES(1)| YES | --- | --- | TODO | YES + | Windows | macOS | Linux | iOS | Android | HTML5 + --------------------+---------+-------+-------+-------+---------+-------- + gl 3.x | YES | YES | YES | --- | --- | --- + gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES + metal | --- | YES | --- | YES | --- | --- + d3d11 | YES | --- | --- | --- | --- | --- + KEY_DOWN | YES | YES | YES | SOME | TODO | YES + KEY_UP | YES | YES | YES | SOME | TODO | YES + CHAR | YES | YES | YES | YES | TODO | YES + MOUSE_DOWN | YES | YES | YES | --- | --- | YES + MOUSE_UP | YES | YES | YES | --- | --- | YES + MOUSE_SCROLL | YES | YES | YES | --- | --- | YES + MOUSE_MOVE | YES | YES | YES | --- | --- | YES + MOUSE_ENTER | YES | YES | YES | --- | --- | YES + MOUSE_LEAVE | YES | YES | YES | --- | --- | YES + TOUCHES_BEGAN | --- | --- | --- | YES | YES | YES + TOUCHES_MOVED | --- | --- | --- | YES | YES | YES + TOUCHES_ENDED | --- | --- | --- | YES | YES | YES + TOUCHES_CANCELLED | --- | --- | --- | YES | YES | YES + RESIZED | YES | YES | YES | YES | YES | YES + ICONIFIED | YES | YES | YES | --- | --- | --- + RESTORED | YES | YES | YES | --- | --- | --- + FOCUSED | YES | YES | YES | --- | --- | YES + UNFOCUSED | YES | YES | YES | --- | --- | YES + SUSPENDED | --- | --- | --- | YES | YES | TODO + RESUMED | --- | --- | --- | YES | YES | TODO + QUIT_REQUESTED | YES | YES | YES | --- | --- | YES + IME | TODO | TODO? | TODO | ??? | TODO | ??? + key repeat flag | YES | YES | YES | --- | --- | YES + windowed | YES | YES | YES | --- | --- | YES + fullscreen | YES | YES | YES | YES | YES | --- + mouse hide | YES | YES | YES | --- | --- | YES + mouse lock | YES | YES | YES | --- | --- | YES + set cursor type | YES | YES | YES | --- | --- | YES + screen keyboard | --- | --- | --- | YES | TODO | YES + swap interval | YES | YES | YES | YES | TODO | YES + high-dpi | YES | YES | TODO | YES | YES | YES + clipboard | YES | YES | TODO | --- | --- | YES + MSAA | YES | YES | YES | YES | YES | YES + drag'n'drop | YES | YES | YES | --- | --- | YES + window icon | YES | YES(1)| YES | --- | --- | YES (1) macOS has no regular window icons, instead the dock icon is changed (2) supported with EGL only (not GLX) @@ -164,6 +157,18 @@ }; } + To get any logging output in case of errors you need to provide a log + callback. The easiest way is via sokol_log.h: + + #include "sokol_log.h" + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + ... + .logger.func = slog_func, + }; + } + There are many more setup parameters, but these are the most important. For a complete list search for the sapp_desc structure declaration below. @@ -199,11 +204,6 @@ used to communicate other types of events to the application. Keep the event_cb struct member zero-initialized if your application doesn't require event handling. - .fail_cb (void (*)(const char* msg)) - The fail callback is called when a fatal error is encountered - during start which doesn't allow the program to continue. - Providing a callback here gives you a chance to show an error message - to the user. The default behaviour is SAPP_LOG(msg) As you can see, those 'standard callbacks' don't have a user_data argument, so any data that needs to be preserved between callbacks @@ -217,10 +217,6 @@ .frame_userdata_cb (void (*)(void* user_data)) .cleanup_userdata_cb (void (*)(void* user_data)) .event_userdata_cb (void(*)(const sapp_event* event, void* user_data)) - .fail_userdata_cb (void(*)(const char* msg, void* user_data)) - These are the user-data versions of the callback functions. You - can mix those with the standard callbacks that don't have the - user_data argument. The function sapp_userdata() can be used to query the user_data pointer provided in the sapp_desc struct. @@ -269,11 +265,6 @@ int sapp_sample_count(void) Return the MSAA sample count of the default framebuffer. - bool sapp_gles2(void) - Returns true if a GLES2 or WebGL context has been created. This - is useful when a GLES3/WebGL2 context was requested but is not - available so that sokol_app.h had to fallback to GLES2/WebGL. - const void* sapp_metal_get_device(void) const void* sapp_metal_get_renderpass_descriptor(void) const void* sapp_metal_get_drawable(void) @@ -782,7 +773,7 @@ programmatically close the browser tab). On the web it's also not possible to run custom code when the user - closes a brower tab, so it's not possible to prevent this with a + closes a browser tab, so it's not possible to prevent this with a fancy custom dialog box. Instead the standard "Leave Site?" dialog box can be activated (or @@ -1041,26 +1032,51 @@ itself though, not any allocations in OS libraries. - LOG FUNCTION OVERRIDE - ===================== - You can override the log function at initialization time like this: + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + ... + .logger.func = slog_func, + }; + } - void my_log(const char* message, void* user_data) { - printf("sapp says: \s\n", message); + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sapp' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SAPP_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_app.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... } + ...and then setup sokol-app like this: + sapp_desc sokol_main(int argc, char* argv[]) { - return (sapp_desc){ - // ... + return (sapp_desc) { + ... .logger = { - .log_cb = my_log, - .user_data = ..., + .func = my_log, + .user_data = my_user_data, } }; } - If no overrides are provided, puts will be used on most platforms. - On Android, __android_log_write will be used instead. + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. TEMP NOTE DUMP @@ -1466,14 +1482,125 @@ typedef struct sapp_allocator { void* user_data; } sapp_allocator; +/* + sapp_log_item + + Log items are defined via X-Macros and expanded to an enum + 'sapp_log_item', and in debug mode to corresponding + human readable error messages. +*/ +#define _SAPP_LOG_ITEMS \ + _SAPP_LOGITEM_XMACRO(OK, "Ok") \ + _SAPP_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SAPP_LOGITEM_XMACRO(MACOS_INVALID_NSOPENGL_PROFILE, "macos: invalid NSOpenGLProfile (valid choices are 1.0, 3.2 and 4.1)") \ + _SAPP_LOGITEM_XMACRO(WIN32_LOAD_OPENGL32_DLL_FAILED, "failed loading opengl32.dll") \ + _SAPP_LOGITEM_XMACRO(WIN32_CREATE_HELPER_WINDOW_FAILED, "failed to create helper window") \ + _SAPP_LOGITEM_XMACRO(WIN32_HELPER_WINDOW_GETDC_FAILED, "failed to get helper window DC") \ + _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED, "failed to set pixel format for dummy GL context") \ + _SAPP_LOGITEM_XMACRO(WIN32_CREATE_DUMMY_CONTEXT_FAILED, "failed to create dummy GL context") \ + _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_MAKE_CURRENT_FAILED, "failed to make dummy GL context current") \ + _SAPP_LOGITEM_XMACRO(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED, "failed to get WGL pixel format attribute") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_FIND_PIXELFORMAT_FAILED, "failed to find matching WGL pixel format") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED, "failed to get pixel format descriptor") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_SET_PIXELFORMAT_FAILED, "failed to set selected pixel format") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED, "ARB_create_context required") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED, "ARB_create_context_profile required") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_3_2_NOT_SUPPORTED, "OpenGL 3.2 not supported by GL driver (ERROR_INVALID_VERSION_ARB)") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED, "requested OpenGL profile not support by GL driver (ERROR_INVALID_PROFILE_ARB)") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT, "CreateContextAttribsARB failed with ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER, "CreateContextAttribsARB failed for other reason") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_CREATE_DEVICE_AND_SWAPCHAIN_WITH_DEBUG_FAILED, "D3D11CreateDeviceAndSwapChain() with D3D11_CREATE_DEVICE_DEBUG failed, retrying without debug flag.") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIFACTORY_FAILED, "could not obtain IDXGIFactory object") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIADAPTER_FAILED, "could not obtain IDXGIAdapter object") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_QUERY_INTERFACE_IDXGIDEVICE1_FAILED, "could not obtain IDXGIDevice1 interface") \ + _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK, "RegisterRawInputDevices() failed (on mouse lock)") \ + _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK, "RegisterRawInputDevices() failed (on mouse unlock)") \ + _SAPP_LOGITEM_XMACRO(WIN32_GET_RAW_INPUT_DATA_FAILED, "GetRawInputData() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_LIBGL_FAILED, "failed to load libGL") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED, "failed to load GLX entry points") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_EXTENSION_NOT_FOUND, "GLX extension not found") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_QUERY_VERSION_FAILED, "failed to query GLX version") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_VERSION_TOO_LOW, "GLX version too low (need at least 1.3)") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_GLXFBCONFIGS, "glXGetFBConfigs() returned no configs") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG, "failed to find a suitable GLXFBConfig") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED, "glXGetVisualFromFBConfig failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING, "GLX extensions ARB_create_context and ARB_create_context_profile missing") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_CONTEXT_FAILED, "Failed to create GL context via glXCreateContextAttribsARB") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_WINDOW_FAILED, "glXCreateWindow() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_CREATE_WINDOW_FAILED, "XCreateWindow() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_API_FAILED, "eglBindAPI(EGL_OPENGL_API) failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_ES_API_FAILED, "eglBindAPI(EGL_OPENGL_ES_API) failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_DISPLAY_FAILED, "eglGetDisplay() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_INITIALIZE_FAILED, "eglInitialize() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_CONFIGS, "eglChooseConfig() returned no configs") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_NATIVE_VISUAL, "eglGetConfigAttrib() for EGL_NATIVE_VISUAL_ID failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_VISUAL_INFO_FAILED, "XGetVisualInfo() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_WINDOW_SURFACE_FAILED, "eglCreateWindowSurface() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_CONTEXT_FAILED, "eglCreateContext() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_MAKE_CURRENT_FAILED, "eglMakeCurrent() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_OPEN_DISPLAY_FAILED, "XOpenDisplay() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_QUERY_SYSTEM_DPI_FAILED, "failed to query system dpi value, assuming default 96.0") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME, "dropped file URL doesn't start with 'file://'") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB, "unsupported input event encountered in _sapp_android_input_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB, "unsupported input event encountered in _sapp_android_main_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_READ_MSG_FAILED, "failed to read message in _sapp_android_main_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_WRITE_MSG_FAILED, "failed to write message in _sapp_android_msg") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_CREATE, "MSG_CREATE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_RESUME, "MSG_RESUME") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_PAUSE, "MSG_PAUSE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_FOCUS, "MSG_FOCUS") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_NO_FOCUS, "MSG_NO_FOCUS") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_NATIVE_WINDOW, "MSG_SET_NATIVE_WINDOW") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_INPUT_QUEUE, "MSG_SET_INPUT_QUEUE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_DESTROY, "MSG_DESTROY") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNKNOWN_MSG, "unknown msg type received") \ + _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_STARTED, "loop thread started") \ + _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_DONE, "loop thread done") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTART, "NativeActivity onStart()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONRESUME, "NativeActivity onResume") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSAVEINSTANCESTATE, "NativeActivity onSaveInstanceState") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED, "NativeActivity onWindowFocusChanged") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONPAUSE, "NativeActivity onPause") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTOP, "NativeActivity onStop()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWCREATED, "NativeActivity onNativeWindowCreated") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED, "NativeActivity onNativeWindowDestroyed") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUECREATED, "NativeActivity onInputQueueCreated") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUEDESTROYED, "NativeActivity onInputQueueDestroyed") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED, "NativeActivity onConfigurationChanged") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY, "NativeActivity onLowMemory") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONDESTROY, "NativeActivity onDestroy") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_DONE, "NativeActivity done") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCREATE, "NativeActivity onCreate") \ + _SAPP_LOGITEM_XMACRO(ANDROID_CREATE_THREAD_PIPE_FAILED, "failed to create thread pipe") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, "NativeActivity sucessfully created") \ + _SAPP_LOGITEM_XMACRO(IMAGE_DATA_SIZE_MISMATCH, "image data size mismatch (must be width*height*4 bytes)") \ + _SAPP_LOGITEM_XMACRO(DROPPED_FILE_PATH_TOO_LONG, "dropped file path too long (sapp_desc.max_dropped_filed_path_length)") \ + _SAPP_LOGITEM_XMACRO(CLIPBOARD_STRING_TOO_BIG, "clipboard string didn't fit into clipboard buffer") \ + +#define _SAPP_LOGITEM_XMACRO(item,msg) SAPP_LOGITEM_##item, +typedef enum sapp_log_item { + _SAPP_LOG_ITEMS +} sapp_log_item; +#undef _SAPP_LOGITEM_XMACRO + /* sapp_logger - Used in sapp_desc to provide custom log callbacks to sokol_app.h. - Default behavior is SOKOL_LOG(message). + Used in sapp_desc to provide a logging function. Please be aware that + without logging function, sokol-app will be completely silent, e.g. it will + not report errors or warnings. 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). */ typedef struct sapp_logger { - void (*log_cb)(const char* message, void* user_data); + void (*func)( + const char* tag, // always "sapp" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SAPP_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_app.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); void* user_data; } sapp_logger; @@ -1482,14 +1609,12 @@ typedef struct sapp_desc { void (*frame_cb)(void); void (*cleanup_cb)(void); void (*event_cb)(const sapp_event*); - void (*fail_cb)(const char*); void* user_data; // these are the user-provided callbacks with user data void (*init_userdata_cb)(void*); void (*frame_userdata_cb)(void*); void (*cleanup_userdata_cb)(void*); void (*event_userdata_cb)(const sapp_event*, void*); - void (*fail_userdata_cb)(const char*, void*); int width; // the preferred width of the window / canvas int height; // the preferred height of the window / canvas @@ -1506,10 +1631,9 @@ typedef struct sapp_desc { int max_dropped_file_path_length; // max length in bytes of a dropped UTF-8 file path (default: 2048) sapp_icon_desc icon; // the initial window icon to set sapp_allocator allocator; // optional memory allocation overrides (default: malloc/free) - sapp_logger logger; // optional log callback overrides (default: SAPP_LOG(message)) + sapp_logger logger; // logging callback override (default: NO LOGGING!) /* backend-specific options */ - bool gl_force_gles2; // if true, setup GLES2/WebGL even if GLES3/WebGL2 is available int gl_major_version; // override GL major and minor version (the default GL version is 3.2) int gl_minor_version; bool win32_console_utf8; // if true, set the output console codepage to UTF-8 @@ -1648,9 +1772,6 @@ SOKOL_APP_API_DECL const void* sapp_egl_get_display(void); /* EGL: get EGLContext object */ SOKOL_APP_API_DECL const void* sapp_egl_get_context(void); -/* GL: return true when GLES2 fallback is active (to detect fallback from GLES3) */ -SOKOL_APP_API_DECL bool sapp_gles2(void); - /* HTML5: enable or disable the hardwired "Leave Site?" dialog box */ SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask); /* HTML5: get byte size of a dropped file */ @@ -1712,7 +1833,13 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #endif // SOKOL_APP_INCLUDED -/*-- IMPLEMENTATION ----------------------------------------------------------*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_APP_IMPL #define SOKOL_APP_IMPL_INCLUDED (1) @@ -1723,7 +1850,7 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #include // malloc, free #include // memset #include // size_t -#include /* roundf() */ +#include // roundf /* check if the config defines are alright */ #if defined(__APPLE__) @@ -1751,31 +1878,20 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #elif defined(__EMSCRIPTEN__) /* emscripten (asm.js or wasm) */ #define _SAPP_EMSCRIPTEN (1) - #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) && !defined(SOKOL_WGPU) - #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3, SOKOL_GLES2 or SOKOL_WGPU") + #if !defined(SOKOL_GLES3) && !defined(SOKOL_WGPU) + #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3 or SOKOL_WGPU") #endif #elif defined(_WIN32) /* Windows (D3D11 or GL) */ - #include - #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - #define _SAPP_UWP (1) - #if !defined(SOKOL_D3D11) - #error("sokol_app.h: unknown 3D API selected for UWP, must be SOKOL_D3D11") - #endif - #if !defined(__cplusplus) - #error("sokol_app.h: UWP bindings require C++/17") - #endif - #else - #define _SAPP_WIN32 (1) - #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33) - #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33") - #endif + #define _SAPP_WIN32 (1) + #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33) + #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33") #endif #elif defined(__ANDROID__) /* Android */ #define _SAPP_ANDROID (1) - #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) - #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3 or SOKOL_GLES2") + #if !defined(SOKOL_GLES3) + #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3") #endif #if defined(SOKOL_NO_ENTRY) #error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android") @@ -1787,8 +1903,8 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #if !defined(SOKOL_FORCE_EGL) #define _SAPP_GLX (1) #endif - #elif !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) - #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33, SOKOL_GLES3 or SOKOL_GLES2") + #elif !defined(SOKOL_GLES3) + #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33, SOKOL_GLES3") #endif #else #error "sokol_app.h: Unknown platform" @@ -1810,24 +1926,6 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif -#if !defined(SOKOL_DEBUG) - #define SAPP_LOG(s) -#else - #define SAPP_LOG(s) _sapp_log(s) - #ifndef SOKOL_LOG - #if defined(__ANDROID__) - #include - #define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_APP", s) - #else - #include - #define SOKOL_LOG(s) puts(s) - #endif - #endif -#endif - -#ifndef SOKOL_ABORT - #define SOKOL_ABORT() abort() -#endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static @@ -1839,7 +1937,6 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #define _SOKOL_UNUSED(x) (void)(x) #endif -/*== PLATFORM SPECIFIC INCLUDES AND DEFINES ==================================*/ #if defined(_SAPP_APPLE) #if defined(SOKOL_METAL) #import @@ -1919,36 +2016,6 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #ifndef WM_DPICHANGED #define WM_DPICHANGED (0x02E0) #endif -#elif defined(_SAPP_UWP) - #ifndef NOMINMAX - #define NOMINMAX - #endif - #ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ - #pragma warning(disable:4054) /* 'type cast': from function pointer */ - #pragma warning(disable:4055) /* 'type cast': from data pointer */ - #pragma warning(disable:4505) /* unreferenced local function has been removed */ - #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ - #endif - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - #include - - #pragma comment (lib, "WindowsApp") - #pragma comment (lib, "dxguid") #elif defined(_SAPP_ANDROID) #include #include @@ -1977,7 +2044,13 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #include #endif -/*== frame timing helpers ===================================================*/ +// ███████ ██████ █████ ███ ███ ███████ ████████ ██ ███ ███ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ████ ████ ██ ████ ██ ██ +// █████ ██████ ███████ ██ ████ ██ █████ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ████ ██████ +// +// >>frame timing #define _SAPP_RING_NUM_SLOTS (256) typedef struct { int head; @@ -2046,7 +2119,7 @@ typedef struct { } mach; #elif defined(_SAPP_EMSCRIPTEN) // empty - #elif defined(_SAPP_WIN32) || defined(_SAPP_UWP) + #elif defined(_SAPP_WIN32) struct { LARGE_INTEGER freq; LARGE_INTEGER start; @@ -2076,7 +2149,7 @@ _SOKOL_PRIVATE void _sapp_timestamp_init(_sapp_timestamp_t* ts) { ts->mach.start = mach_absolute_time(); #elif defined(_SAPP_EMSCRIPTEN) (void)ts; - #elif defined(_SAPP_WIN32) || defined(_SAPP_UWP) + #elif defined(_SAPP_WIN32) QueryPerformanceFrequency(&ts->win.freq); QueryPerformanceCounter(&ts->win.start); #else @@ -2095,7 +2168,7 @@ _SOKOL_PRIVATE double _sapp_timestamp_now(_sapp_timestamp_t* ts) { (void)ts; SOKOL_ASSERT(false); return 0.0; - #elif defined(_SAPP_WIN32) || defined(_SAPP_UWP) + #elif defined(_SAPP_WIN32) LARGE_INTEGER qpc; QueryPerformanceCounter(&qpc); const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - ts->win.start.QuadPart, 1000000000, ts->win.freq.QuadPart); @@ -2188,7 +2261,13 @@ _SOKOL_PRIVATE double _sapp_timing_get_avg(_sapp_timing_t* t) { return t->avg; } -/*== MACOS DECLARATIONS ======================================================*/ +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >> structs #if defined(_SAPP_MACOS) @interface _sapp_macos_app_delegate : NSObject @end @@ -2210,6 +2289,7 @@ typedef struct { uint8_t mouse_buttons; NSWindow* window; NSTrackingArea* tracking_area; + id keyup_monitor; _sapp_macos_app_delegate* app_dlg; _sapp_macos_window_delegate* win_dlg; _sapp_macos_view* view; @@ -2221,7 +2301,6 @@ typedef struct { #endif // _SAPP_MACOS -/*== IOS DECLARATIONS ========================================================*/ #if defined(_SAPP_IOS) @interface _sapp_app_delegate : NSObject @@ -2256,7 +2335,6 @@ typedef struct { #endif // _SAPP_IOS -/*== EMSCRIPTEN DECLARATIONS =================================================*/ #if defined(_SAPP_EMSCRIPTEN) #if defined(SOKOL_WGPU) @@ -2285,8 +2363,7 @@ typedef struct { } _sapp_emsc_t; #endif // _SAPP_EMSCRIPTEN -/*== WIN32 DECLARATIONS ======================================================*/ -#if defined(SOKOL_D3D11) && (defined(_SAPP_WIN32) || defined(_SAPP_UWP)) +#if defined(SOKOL_D3D11) && defined(_SAPP_WIN32) typedef struct { ID3D11Device* device; ID3D11DeviceContext* device_context; @@ -2304,7 +2381,6 @@ typedef struct { } _sapp_d3d11_t; #endif -/*== WIN32 DECLARATIONS ======================================================*/ #if defined(_SAPP_WIN32) #ifndef DPI_ENUMS_DECLARED @@ -2412,25 +2488,6 @@ typedef struct { #endif // _SAPP_WIN32 -/*== UWP DECLARATIONS ======================================================*/ -#if defined(_SAPP_UWP) - -typedef struct { - float content_scale; - float window_scale; - float mouse_scale; -} _sapp_uwp_dpi_t; - -typedef struct { - bool mouse_tracked; - uint8_t mouse_buttons; - _sapp_uwp_dpi_t dpi; -} _sapp_uwp_t; - -#endif // _SAPP_UWP - -/*== ANDROID DECLARATIONS ====================================================*/ - #if defined(_SAPP_ANDROID) typedef enum { _SOKOL_ANDROID_MSG_CREATE, @@ -2476,7 +2533,6 @@ typedef struct { #endif // _SAPP_ANDROID -/*== LINUX DECLARATIONS ======================================================*/ #if defined(_SAPP_LINUX) #define _SAPP_X11_XDND_VERSION (5) @@ -2627,8 +2683,6 @@ typedef struct { #endif // _SAPP_LINUX -/*== COMMON DECLARATIONS =====================================================*/ - /* helper macros */ #define _sapp_def(val, def) (((val) == 0) ? (def) : (val)) #define _sapp_absf(a) (((a)<0.0f)?-(a):(a)) @@ -2681,7 +2735,6 @@ typedef struct { sapp_desc desc; bool valid; bool fullscreen; - bool gles2_fallback; bool first_frame; bool init_called; bool cleanup_called; @@ -2718,11 +2771,6 @@ typedef struct { #elif defined(SOKOL_GLCORE33) _sapp_wgl_t wgl; #endif - #elif defined(_SAPP_UWP) - _sapp_uwp_t uwp; - #if defined(SOKOL_D3D11) - _sapp_d3d11_t d3d11; - #endif #elif defined(_SAPP_ANDROID) _sapp_android_t android; #elif defined(_SAPP_LINUX) @@ -2740,7 +2788,52 @@ typedef struct { } _sapp_t; static _sapp_t _sapp; -/*=== PRIVATE HELPER FUNCTIONS ===============================================*/ +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SAPP_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sapp_log_messages[] = { + _SAPP_LOG_ITEMS +}; +#undef _SAPP_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SAPP_PANIC(code) _sapp_log(SAPP_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SAPP_ERROR(code) _sapp_log(SAPP_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SAPP_WARN(code) _sapp_log(SAPP_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SAPP_INFO(code) _sapp_log(SAPP_LOGITEM_ ##code, 3, 0, __LINE__) + +static void _sapp_log(sapp_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_sapp.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _sapp_log_messages[log_item]; + } + #endif + _sapp.desc.logger.func("sapp", log_level, log_item, msg, line_nr, filename, _sapp.desc.logger.user_data); + } + else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory _SOKOL_PRIVATE void _sapp_clear(void* ptr, size_t size) { SOKOL_ASSERT(ptr && (size > 0)); memset(ptr, 0, size); @@ -2755,7 +2848,9 @@ _SOKOL_PRIVATE void* _sapp_malloc(size_t size) { else { ptr = malloc(size); } - SOKOL_ASSERT(ptr); + if (0 == ptr) { + _SAPP_PANIC(MALLOC_FAILED); + } return ptr; } @@ -2774,31 +2869,13 @@ _SOKOL_PRIVATE void _sapp_free(void* ptr) { } } -#if defined(SOKOL_DEBUG) -_SOKOL_PRIVATE void _sapp_log(const char* msg) { - SOKOL_ASSERT(msg); - if (_sapp.desc.logger.log_cb) { - _sapp.desc.logger.log_cb(msg, _sapp.desc.logger.user_data); - } - else { - SOKOL_LOG(msg); - } -} -#endif - -_SOKOL_PRIVATE void _sapp_fail(const char* msg) { - if (_sapp.desc.fail_cb) { - _sapp.desc.fail_cb(msg); - } - else if (_sapp.desc.fail_userdata_cb) { - _sapp.desc.fail_userdata_cb(msg, _sapp.desc.user_data); - } - else { - SAPP_LOG(msg); - } - SOKOL_ABORT(); -} - +// ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>helpers _SOKOL_PRIVATE void _sapp_call_init(void) { if (_sapp.desc.init_cb) { _sapp.desc.init_cb(); @@ -3019,7 +3096,7 @@ _SOKOL_PRIVATE bool _sapp_image_validate(const sapp_image_desc* desc) { SOKOL_ASSERT(desc->pixels.size > 0); const size_t wh_size = (size_t)(desc->width * desc->height) * sizeof(uint32_t); if (wh_size != desc->pixels.size) { - SAPP_LOG("Image data size mismatch (must be width*height*4 bytes)\n"); + _SAPP_ERROR(IMAGE_DATA_SIZE_MISMATCH); return false; } return true; @@ -3175,7 +3252,13 @@ _SOKOL_PRIVATE void _sapp_setup_default_icon(void) { SOKOL_ASSERT(dst == dst_end); } -/*== MacOS/iOS ===============================================================*/ +// █████ ██████ ██████ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██ █████ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ +// +// >>apple #if defined(_SAPP_APPLE) #if __has_feature(objc_arc) @@ -3184,7 +3267,13 @@ _SOKOL_PRIVATE void _sapp_setup_default_icon(void) { #define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; } #endif -/*== MacOS ===================================================================*/ +// ███ ███ █████ ██████ ██████ ███████ +// ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ ███████ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██████ ██████ ███████ +// +// >>macos #if defined(_SAPP_MACOS) _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { @@ -3303,6 +3392,11 @@ _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { _SOKOL_PRIVATE void _sapp_macos_discard_state(void) { // NOTE: it's safe to call [release] on a nil object + if (_sapp.macos.keyup_monitor != nil) { + [NSEvent removeMonitor:_sapp.macos.keyup_monitor]; + // NOTE: removeMonitor also releases the object + _sapp.macos.keyup_monitor = nil; + } _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg); _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg); @@ -3339,11 +3433,22 @@ _SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) { _sapp_init_state(desc); _sapp_macos_init_keytable(); [NSApplication sharedApplication]; + // set the application dock icon as early as possible, otherwise // the dummy icon will be visible for a short time sapp_set_icon(&_sapp.desc.icon); _sapp.macos.app_dlg = [[_sapp_macos_app_delegate alloc] init]; NSApp.delegate = _sapp.macos.app_dlg; + + // workaround for "no key-up sent while Cmd is pressed" taken from GLFW: + NSEvent* (^keyup_monitor)(NSEvent*) = ^NSEvent* (NSEvent* event) { + if ([event modifierFlags] & NSEventModifierFlagCommand) { + [[NSApp keyWindow] sendEvent:event]; + } + return event; + }; + _sapp.macos.keyup_monitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp handler:keyup_monitor]; + [NSApp run]; // NOTE: [NSApp run] never returns, instead cleanup code // must be put into applicationWillTerminate @@ -3359,7 +3464,7 @@ int main(int argc, char* argv[]) { #endif /* SOKOL_NO_ENTRY */ _SOKOL_PRIVATE uint32_t _sapp_macos_mods(NSEvent* ev) { - const NSEventModifierFlags f = ev.modifierFlags; + const NSEventModifierFlags f = (ev == nil) ? NSEvent.modifierFlags : ev.modifierFlags; const NSUInteger b = NSEvent.pressedMouseButtons; uint32_t m = 0; if (f & NSEventModifierFlagShift) { @@ -3505,12 +3610,15 @@ _SOKOL_PRIVATE void _sapp_macos_update_window_title(void) { [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]]; } -_SOKOL_PRIVATE void _sapp_macos_mouse_update(NSEvent* event) { +_SOKOL_PRIVATE void _sapp_macos_mouse_update_from_nspoint(NSPoint mouse_pos, bool clear_dxdy) { if (!_sapp.mouse.locked) { - const NSPoint mouse_pos = event.locationInWindow; float new_x = mouse_pos.x * _sapp.dpi_scale; float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; - if (_sapp.mouse.pos_valid) { + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } + else if (_sapp.mouse.pos_valid) { // don't update dx/dy in the very first update _sapp.mouse.dx = new_x - _sapp.mouse.x; _sapp.mouse.dy = new_y - _sapp.mouse.y; @@ -3521,6 +3629,10 @@ _SOKOL_PRIVATE void _sapp_macos_mouse_update(NSEvent* event) { } } +_SOKOL_PRIVATE void _sapp_macos_mouse_update_from_nsevent(NSEvent* event, bool clear_dxdy) { + _sapp_macos_mouse_update_from_nspoint(event.locationInWindow, clear_dxdy); +} + _SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) { /* NOTE: this function is only called when the mouse visibility actually changes */ if (visible) { @@ -3681,7 +3793,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { case 32: attrs[i++] = NSOpenGLProfileVersion3_2Core; break; case 41: attrs[i++] = NSOpenGLProfileVersion4_1Core; break; default: - _sapp_fail("Invalid NSOpenGLProfile (valid choices are 1.0, 3.2, and 4.1)\n"); + _SAPP_PANIC(MACOS_INVALID_NSOPENGL_PROFILE); } attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24; attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8; @@ -3844,14 +3956,16 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { for (int i = 0; i < _sapp.drop.num_files; i++) { NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[(NSUInteger)i] stringForType:NSPasteboardTypeFileURL]]; if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { - SAPP_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); drop_failed = true; break; } } if (!drop_failed) { if (_sapp_events_enabled()) { + _sapp_macos_mouse_update_from_nspoint(sender.draggingLocation, true); _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp.event.modifiers = _sapp_macos_mods(nil); _sapp_call_event(&_sapp.event); } } @@ -3868,19 +3982,6 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { @implementation _sapp_macos_view #if defined(SOKOL_GLCORE33) -/* NOTE: this is a hack/fix when the initial window size has been clipped by - macOS because it didn't fit on the screen, in that case the - frame size of the window is reported wrong if low-dpi rendering - was requested (instead the high-dpi dimensions are returned) - until the window is resized for the first time. - - Hooking into reshape and getting the frame dimensions seems to report - the correct dimensions. -*/ -- (void)reshape { - _sapp_macos_update_dimensions(); - [super reshape]; -} - (void)timerFired:(id)sender { _SOKOL_UNUSED(sender); [self setNeedsDisplay:YES]; @@ -3898,7 +3999,7 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { /* NOTE: late event polling temporarily out-commented to check if this - causes infrequent and almost impossible to reproduce probelms with the + causes infrequent and almost impossible to reproduce problems with the window close events, see: https://github.com/floooh/sokol/pull/483#issuecomment-805148815 @@ -3974,8 +4075,17 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { [self addTrackingArea:_sapp.macos.tracking_area]; [super updateTrackingAreas]; } + +// helper function to make GL context active +static void _sapp_gl_make_current(void) { + #if defined(SOKOL_GLCORE33) + [[_sapp.macos.view openGLContext] makeCurrentContext]; + #endif +} + - (void)mouseEntered:(NSEvent*)event { - _sapp_macos_mouse_update(event); + _sapp_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, true); /* don't send mouse enter/leave while dragging (so that it behaves the same as on Windows while SetCapture is active */ @@ -3984,47 +4094,55 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { } } - (void)mouseExited:(NSEvent*)event { - _sapp_macos_mouse_update(event); + _sapp_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, true); if (0 == _sapp.macos.mouse_buttons) { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); } } - (void)mouseDown:(NSEvent*)event { - _sapp_macos_mouse_update(event); + _sapp_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, false); _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event)); _sapp.macos.mouse_buttons |= (1< 0) { @@ -4110,6 +4226,7 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { } } - (void)keyUp:(NSEvent*)event { + _sapp_gl_make_current(); _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, _sapp_translate_key(event.keyCode), event.isARepeat, @@ -4146,9 +4263,15 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { } @end -#endif /* MacOS */ +#endif // macOS -/*== iOS =====================================================================*/ +// ██ ██████ ███████ +// ██ ██ ██ ██ +// ██ ██ ██ ███████ +// ██ ██ ██ ██ +// ██ ██████ ███████ +// +// >>ios #if defined(_SAPP_IOS) _SOKOL_PRIVATE void _sapp_ios_discard_state(void) { @@ -4314,17 +4437,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { _sapp.ios.view_ctrl.view = _sapp.ios.view; _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; #else - if (_sapp.desc.gl_force_gles2) { - _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - _sapp.gles2_fallback = true; - } - else { - _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; - if (_sapp.ios.eagl_ctx == nil) { - _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - _sapp.gles2_fallback = true; - } - } + _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; _sapp.ios.view = [[_sapp_ios_view alloc] initWithFrame:screen_rect]; _sapp.ios.view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; _sapp.ios.view.drawableDepthFormat = GLKViewDrawableDepthFormat24; @@ -4481,7 +4594,13 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { #endif /* _SAPP_APPLE */ -/*== EMSCRIPTEN ==============================================================*/ +// ███████ ███ ███ ███████ ██████ ██████ ██ ██████ ████████ ███████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// █████ ██ ████ ██ ███████ ██ ██████ ██ ██████ ██ █████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ███████ ██████ ██ ██ ██ ██ ██ ███████ ██ ████ +// +// >>emscripten #if defined(_SAPP_EMSCRIPTEN) #if defined(EM_JS_DEPS) @@ -4543,12 +4662,12 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) { return; } if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { - SAPP_LOG("sokol_app.h: dropped file path too long!\n"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); _sapp.drop.num_files = 0; } } -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y) { +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y, int mods) { if (!_sapp.drop.enabled) { return; } @@ -4564,6 +4683,11 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y) { _sapp.mouse.dx = 0.0f; _sapp.mouse.dy = 0.0f; _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + // see sapp_js_add_dragndrop_listeners for mods constants + if (mods & 1) { _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; } + if (mods & 2) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; } + if (mods & 4) { _sapp.event.modifiers |= SAPP_MODIFIER_ALT; } + if (mods & 8) { _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; } _sapp_call_event(&_sapp.event); } } @@ -4686,8 +4810,13 @@ EM_JS(void, sapp_js_add_dragndrop_listeners, (const char* canvas_name_cstr), { __sapp_emsc_drop(i, cstr); }); } + let mods = 0; + if (event.shiftKey) { mods |= 1; } + if (event.ctrlKey) { mods |= 2; } + if (event.altKey) { mods |= 4; } + if (event.metaKey) { mods |= 8; } // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect - __sapp_emsc_end_drop(event.clientX, event.clientY); + __sapp_emsc_end_drop(event.clientX, event.clientY, mods); }; canvas.addEventListener('dragenter', Module.sokol_dragenter, false); canvas.addEventListener('dragleave', Module.sokol_dragleave, false); @@ -5007,8 +5136,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE if (_sapp.mouse.locked) { _sapp.mouse.dx = (float) emsc_event->movementX; _sapp.mouse.dy = (float) emsc_event->movementY; - } - else { + } else { float new_x = emsc_event->targetX * _sapp.dpi_scale; float new_y = emsc_event->targetY * _sapp.dpi_scale; if (_sapp.mouse.pos_valid) { @@ -5022,6 +5150,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) { sapp_event_type type; bool is_button_event = false; + bool clear_dxdy = false; switch (emsc_type) { case EMSCRIPTEN_EVENT_MOUSEDOWN: type = SAPP_EVENTTYPE_MOUSE_DOWN; @@ -5036,14 +5165,20 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE break; case EMSCRIPTEN_EVENT_MOUSEENTER: type = SAPP_EVENTTYPE_MOUSE_ENTER; + clear_dxdy = true; break; case EMSCRIPTEN_EVENT_MOUSELEAVE: type = SAPP_EVENTTYPE_MOUSE_LEAVE; + clear_dxdy = true; break; default: type = SAPP_EVENTTYPE_INVALID; break; } + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } if (type != SAPP_EVENTTYPE_INVALID) { _sapp_init_event(type); _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(emsc_event); @@ -5054,13 +5189,12 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break; default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break; } - } - else { + } else { _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; } _sapp_call_event(&_sapp.event); } - /* mouse lock can only be activated in mouse button events (not in move, enter or leave) */ + // mouse lock can only be activated in mouse button events (not in move, enter or leave) if (is_button_event) { _sapp_emsc_update_mouse_lock_state(); } @@ -5239,6 +5373,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard _sapp.event.key_repeat = emsc_event->repeat; _sapp.event.modifiers = _sapp_emsc_key_event_mods(emsc_event); if (type == SAPP_EVENTTYPE_CHAR) { + // FIXME: this doesn't appear to work on Android Chrome _sapp.event.char_code = emsc_event->charCode; /* workaround to make Cmd+V work on Safari */ if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) { @@ -5246,7 +5381,18 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard } } else { - _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->code); + if (0 != emsc_event->code[0]) { + // This code path is for desktop browsers which send untranslated 'physical' key code strings + // (which is what we actually want for key events) + _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->code); + } else { + // This code path is for mobile browsers which only send localized key code + // strings. Note that the translation will only work for a small subset + // of localization-agnostic keys (like Enter, arrow keys, etc...), but + // regular alpha-numeric keys will all result in an SAPP_KEYCODE_INVALID) + _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->key); + } + /* Special hack for macOS: if the Super key is pressed, macOS doesn't send keyUp events. As a workaround, to prevent keys from "sticking", we'll send a keyup event following a keydown @@ -5259,7 +5405,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard { send_keyup_followup = true; } - /* only forward a certain key ranges to the browser */ + // only forward keys to the browser (can further be suppressed by sapp_consume_event()) switch (_sapp.event.key_code) { case SAPP_KEYCODE_WORLD_1: case SAPP_KEYCODE_WORLD_2: @@ -5325,7 +5471,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard } } if (_sapp_call_event(&_sapp.event)) { - /* consume event via sapp_consume_event() */ + // event was consumed via sapp_consume_event() retval = true; } if (send_keyup_followup) { @@ -5408,7 +5554,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_blur_cb(int emsc_type, const EmscriptenFocusEv return true; } -#if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +#if defined(SOKOL_GLES3) _SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* reserved, void* user_data) { _SOKOL_UNUSED(reserved); _SOKOL_UNUSED(user_data); @@ -5435,21 +5581,9 @@ _SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) { attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha; attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer; attrs.enableExtensionsByDefault = true; - #if defined(SOKOL_GLES3) - if (_sapp.desc.gl_force_gles2) { - attrs.majorVersion = 1; - _sapp.gles2_fallback = true; - } - else { - attrs.majorVersion = 2; - } - #endif + attrs.majorVersion = 2; EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); - if (!ctx) { - attrs.majorVersion = 1; - ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); - _sapp.gles2_fallback = true; - } + // FIXME: error message? emscripten_webgl_make_context_current(ctx); /* some WebGL extension are not enabled automatically by emscripten */ @@ -5592,7 +5726,7 @@ _SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { if (_sapp.drop.enabled) { sapp_js_add_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); } - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + #if defined(SOKOL_GLES3) emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); #endif @@ -5623,7 +5757,7 @@ _SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() { if (_sapp.drop.enabled) { sapp_js_remove_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); } - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + #if defined(SOKOL_GLES3) emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, 0); emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, 0); #endif @@ -5696,7 +5830,7 @@ _SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { _sapp.framebuffer_width = (int)roundf(w * _sapp.dpi_scale); _sapp.framebuffer_height = (int)roundf(h * _sapp.dpi_scale); emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + #if defined(SOKOL_GLES3) _sapp_emsc_webgl_init(); #elif defined(SOKOL_WGPU) sapp_js_wgpu_init(); @@ -5722,7 +5856,13 @@ int main(int argc, char* argv[]) { #endif /* SOKOL_NO_ENTRY */ #endif /* _SAPP_EMSCRIPTEN */ -/*== MISC GL SUPPORT FUNCTIONS ================================================*/ +// ██████ ██ ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ███ ██ ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ███████ ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>gl helpers #if defined(SOKOL_GLCORE33) typedef struct { int red_bits; @@ -5831,9 +5971,15 @@ _SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_ } #endif -/*== WINDOWS DESKTOP and UWP====================================================*/ -#if defined(_SAPP_WIN32) || defined(_SAPP_UWP) -_SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { +// ██ ██ ██ ███ ██ ██████ ██████ ██ ██ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █ ██ ███████ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ +// ███ ███ ██ ██ ████ ██████ ██████ ███ ███ ███████ +// +// >>windows +#if defined(_SAPP_WIN32) +_SOKOL_PRIVATE bool _sapp_win32_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); _sapp_clear(dst, (size_t)dst_num_bytes); const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t); @@ -5848,14 +5994,14 @@ _SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, } } -_SOKOL_PRIVATE void _sapp_win32_uwp_app_event(sapp_event_type type) { +_SOKOL_PRIVATE void _sapp_win32_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp_call_event(&_sapp.event); } } -_SOKOL_PRIVATE void _sapp_win32_uwp_init_keytable(void) { +_SOKOL_PRIVATE void _sapp_win32_init_keytable(void) { /* same as GLFW */ _sapp.keycodes[0x00B] = SAPP_KEYCODE_0; _sapp.keycodes[0x002] = SAPP_KEYCODE_1; @@ -5976,9 +6122,8 @@ _SOKOL_PRIVATE void _sapp_win32_uwp_init_keytable(void) { _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY; _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT; } -#endif // _SAPP_WIN32 || _SAPP_UWP +#endif // _SAPP_WIN32 -/*== WINDOWS DESKTOP===========================================================*/ #if defined(_SAPP_WIN32) #if defined(SOKOL_D3D11) @@ -6145,7 +6290,7 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { _SOKOL_UNUSED(hr); #if defined(SOKOL_DEBUG) if (!SUCCEEDED(hr)) { - // if initialization with D3D11_CREATE_DEVICE_DEBUG failes, this could be because the + // if initialization with D3D11_CREATE_DEVICE_DEBUG fails, this could be because the // 'D3D11 debug layer' stopped working, indicated by the error message: // === // D3D11CreateDevice: Flags (0x2) were specified which require the D3D11 SDK Layers for Windows 10, but they are not present on the system. @@ -6154,7 +6299,7 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { // === // // ...just retry with the DEBUG flag switched off - SAPP_LOG("sokol_app.h: D3D11CreateDeviceAndSwapChain() with D3D11_CREATE_DEVICE_DEBUG failed, retrying without debug flag.\n"); + _SAPP_ERROR(WIN32_D3D11_CREATE_DEVICE_AND_SWAPCHAIN_WITH_DEBUG_FAILED); create_flags &= ~D3D11_CREATE_DEVICE_DEBUG; hr = D3D11CreateDeviceAndSwapChain( NULL, /* pAdapter (use default) */ @@ -6173,7 +6318,7 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { #endif SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context); - // mimimize frame latency, disable Alt-Enter + // minimize frame latency, disable Alt-Enter hr = _sapp_d3d11_QueryInterface(_sapp.d3d11.device, _sapp_win32_refiid(_sapp_IID_IDXGIDevice1), (void**)&_sapp.d3d11.dxgi_device); if (SUCCEEDED(hr) && _sapp.d3d11.dxgi_device) { _sapp_dxgi_SetMaximumFrameLatency(_sapp.d3d11.dxgi_device, 1); @@ -6187,16 +6332,16 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { _SAPP_SAFE_RELEASE(dxgi_factory); } else { - SAPP_LOG("sokol_app.h: could not obtain IDXGIFactory object.\n"); + _SAPP_ERROR(WIN32_D3D11_GET_IDXGIFACTORY_FAILED); } _SAPP_SAFE_RELEASE(dxgi_adapter); } else { - SAPP_LOG("sokol_app.h: could not obtain IDXGIAdapter object.\n"); + _SAPP_ERROR(WIN32_D3D11_GET_IDXGIADAPTER_FAILED); } } else { - SAPP_LOG("sokol_app.h: could not obtain IDXGIDevice1 interface\n"); + _SAPP_PANIC(WIN32_D3D11_QUERY_INTERFACE_IDXGIDEVICE1_FAILED); } } @@ -6294,7 +6439,7 @@ _SOKOL_PRIVATE void _sapp_d3d11_present(bool do_not_wait) { _SOKOL_PRIVATE void _sapp_wgl_init(void) { _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll"); if (!_sapp.wgl.opengl32) { - _sapp_fail("Failed to load opengl32.dll\n"); + _SAPP_PANIC(WIN32_LOAD_OPENGL32_DLL_FAILED); } SOKOL_ASSERT(_sapp.wgl.opengl32); _sapp.wgl.CreateContext = (PFN_wglCreateContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext"); @@ -6317,7 +6462,7 @@ _SOKOL_PRIVATE void _sapp_wgl_init(void) { GetModuleHandleW(NULL), NULL); if (!_sapp.wgl.msg_hwnd) { - _sapp_fail("Win32: failed to create helper window!\n"); + _SAPP_PANIC(WIN32_CREATE_HELPER_WINDOW_FAILED); } SOKOL_ASSERT(_sapp.wgl.msg_hwnd); ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE); @@ -6328,7 +6473,7 @@ _SOKOL_PRIVATE void _sapp_wgl_init(void) { } _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd); if (!_sapp.wgl.msg_dc) { - _sapp_fail("Win32: failed to obtain helper window DC!\n"); + _SAPP_PANIC(WIN32_HELPER_WINDOW_GETDC_FAILED); } } @@ -6388,14 +6533,14 @@ _SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; if (!SetPixelFormat(_sapp.wgl.msg_dc, ChoosePixelFormat(_sapp.wgl.msg_dc, &pfd), &pfd)) { - _sapp_fail("WGL: failed to set pixel format for dummy context\n"); + _SAPP_PANIC(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED); } HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc); if (!rc) { - _sapp_fail("WGL: Failed to create dummy context\n"); + _SAPP_PANIC(WIN32_CREATE_DUMMY_CONTEXT_FAILED); } if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) { - _sapp_fail("WGL: Failed to make context current\n"); + _SAPP_PANIC(WIN32_DUMMY_CONTEXT_MAKE_CURRENT_FAILED); } _sapp.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringEXT"); _sapp.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringARB"); @@ -6415,7 +6560,7 @@ _SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) { SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); int value = 0; if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) { - _sapp_fail("WGL: Failed to retrieve pixel format attribute\n"); + _SAPP_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED); } return value; } @@ -6480,20 +6625,20 @@ _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { int pixel_format = _sapp_wgl_find_pixel_format(); if (0 == pixel_format) { - _sapp_fail("WGL: Didn't find matching pixel format.\n"); + _SAPP_PANIC(WIN32_WGL_FIND_PIXELFORMAT_FAILED); } PIXELFORMATDESCRIPTOR pfd; if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) { - _sapp_fail("WGL: Failed to retrieve PFD for selected pixel format!\n"); + _SAPP_PANIC(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED); } if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) { - _sapp_fail("WGL: Failed to set selected pixel format!\n"); + _SAPP_PANIC(WIN32_WGL_SET_PIXELFORMAT_FAILED); } if (!_sapp.wgl.arb_create_context) { - _sapp_fail("WGL: ARB_create_context required!\n"); + _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED); } if (!_sapp.wgl.arb_create_context_profile) { - _sapp_fail("WGL: ARB_create_context_profile required!\n"); + _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED); } const int attrs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl_major_version, @@ -6506,16 +6651,16 @@ _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { if (!_sapp.wgl.gl_ctx) { const DWORD err = GetLastError(); if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) { - _sapp_fail("WGL: Driver does not support OpenGL version 3.3\n"); + _SAPP_PANIC(WIN32_WGL_OPENGL_3_2_NOT_SUPPORTED); } else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) { - _sapp_fail("WGL: Driver does not support the requested OpenGL profile"); + _SAPP_PANIC(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED); } else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) { - _sapp_fail("WGL: The share context is not compatible with the requested context"); + _SAPP_PANIC(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT); } else { - _sapp_fail("WGL: Failed to create OpenGL context"); + _SAPP_PANIC(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER); } } _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx); @@ -6724,7 +6869,7 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { /* while the mouse is locked, make the mouse cursor invisible and confine the mouse movement to a small rectangle inside our window - (so that we dont miss any mouse up events) + (so that we don't miss any mouse up events) */ RECT client_rect = { _sapp.win32.mouse_locked_x, @@ -6745,7 +6890,7 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { _sapp.win32.hwnd // hwndTarget }; if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { - SAPP_LOG("RegisterRawInputDevices() failed (on mouse lock).\n"); + _SAPP_ERROR(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK); } /* in case the raw mouse device only supports absolute position reporting, we need to skip the dx/dy compution for the first WM_INPUT event @@ -6756,7 +6901,7 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { /* disable raw input for mouse */ const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { - SAPP_LOG("RegisterRawInputDevices() failed (on mouse unlock).\n"); + _SAPP_ERROR(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK); } /* let the mouse roam freely again */ @@ -6911,7 +7056,7 @@ _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { WCHAR* buffer = (WCHAR*) _sapp_malloc_clear(num_chars * sizeof(WCHAR)); DragQueryFileW(hdrop, i, buffer, num_chars); if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr((int)i), _sapp.drop.max_path_length)) { - SAPP_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); drop_failed = true; } _sapp_free(buffer); @@ -6920,6 +7065,7 @@ _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { if (!drop_failed) { if (_sapp_events_enabled()) { _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp.event.modifiers = _sapp_win32_mods(); _sapp_call_event(&_sapp.event); } } @@ -6967,7 +7113,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM a change to intervene via sapp_cancel_quit() */ _sapp.quit_requested = true; - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + _sapp_win32_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); /* if user code hasn't intervened, quit the app */ if (_sapp.quit_requested) { _sapp.quit_ordered = true; @@ -6999,23 +7145,23 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM if (iconified != _sapp.win32.iconified) { _sapp.win32.iconified = iconified; if (iconified) { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_ICONIFIED); + _sapp_win32_app_event(SAPP_EVENTTYPE_ICONIFIED); } else { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESTORED); + _sapp_win32_app_event(SAPP_EVENTTYPE_RESTORED); } } } break; case WM_SETFOCUS: - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_FOCUSED); + _sapp_win32_app_event(SAPP_EVENTTYPE_FOCUSED); break; case WM_KILLFOCUS: /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ if (_sapp.mouse.locked) { _sapp_win32_lock_mouse(false); } - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_UNFOCUSED); + _sapp_win32_app_event(SAPP_EVENTTYPE_UNFOCUSED); break; case WM_SETCURSOR: if (LOWORD(lParam) == HTCLIENT) { @@ -7072,6 +7218,8 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM tme.dwFlags = TME_LEAVE; tme.hwndTrack = _sapp.win32.hwnd; TrackMouseEvent(&tme); + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID); } _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); @@ -7084,25 +7232,26 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM UINT size = sizeof(_sapp.win32.raw_input_data); // see: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputdata if ((UINT)-1 == GetRawInputData(ri, RID_INPUT, &_sapp.win32.raw_input_data, &size, sizeof(RAWINPUTHEADER))) { - SAPP_LOG("GetRawInputData() failed\n"); + _SAPP_ERROR(WIN32_GET_RAW_INPUT_DATA_FAILED); break; } const RAWINPUT* raw_mouse_data = (const RAWINPUT*) &_sapp.win32.raw_input_data; if (raw_mouse_data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { /* mouse only reports absolute position - NOTE: THIS IS UNTESTED, it's unclear from reading the - Win32 RawInput docs under which circumstances absolute - positions are sent. + NOTE: This code is untested and will most likely behave wrong in Remote Desktop sessions. + (such remote desktop sessions are setting the MOUSE_MOVE_ABSOLUTE flag). + See: https://github.com/floooh/sokol/issues/806 and + https://github.com/microsoft/DirectXTK/commit/ef56b63f3739381e451f7a5a5bd2c9779d2a7555) */ + LONG new_x = raw_mouse_data->data.mouse.lLastX; + LONG new_y = raw_mouse_data->data.mouse.lLastY; if (_sapp.win32.raw_input_mousepos_valid) { - LONG new_x = raw_mouse_data->data.mouse.lLastX; - LONG new_y = raw_mouse_data->data.mouse.lLastY; _sapp.mouse.dx = (float) (new_x - _sapp.win32.raw_input_mousepos_x); _sapp.mouse.dy = (float) (new_y - _sapp.win32.raw_input_mousepos_y); - _sapp.win32.raw_input_mousepos_x = new_x; - _sapp.win32.raw_input_mousepos_y = new_y; - _sapp.win32.raw_input_mousepos_valid = true; } + _sapp.win32.raw_input_mousepos_x = new_x; + _sapp.win32.raw_input_mousepos_y = new_y; + _sapp.win32.raw_input_mousepos_valid = true; } else { /* mouse reports movement delta (this seems to be the common case) */ @@ -7115,16 +7264,16 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM case WM_MOUSELEAVE: if (!_sapp.mouse.locked) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; _sapp.win32.mouse_tracked = false; _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); } break; case WM_MOUSEWHEEL: - _sapp_win32_mouse_update(lParam); _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam))); break; case WM_MOUSEHWHEEL: - _sapp_win32_mouse_update(lParam); _sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f); break; case WM_CHAR: @@ -7161,7 +7310,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM #if defined(SOKOL_D3D11) _sapp_d3d11_resize_default_render_target(); #endif - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); + _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); } */ break; @@ -7384,26 +7533,32 @@ _SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { SOKOL_ASSERT(_sapp.win32.hwnd); SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0)); + if (!OpenClipboard(_sapp.win32.hwnd)) { + return false; + } + + HANDLE object = 0; wchar_t* wchar_buf = 0; + const SIZE_T wchar_buf_size = (SIZE_T)_sapp.clipboard.buf_size * sizeof(wchar_t); - HANDLE object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); - if (!object) { + object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); + if (NULL == object) { goto error; } wchar_buf = (wchar_t*) GlobalLock(object); - if (!wchar_buf) { + if (NULL == wchar_buf) { goto error; } - if (!_sapp_win32_uwp_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) { + if (!_sapp_win32_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) { goto error; } - GlobalUnlock(wchar_buf); + GlobalUnlock(object); wchar_buf = 0; - if (!OpenClipboard(_sapp.win32.hwnd)) { + EmptyClipboard(); + // NOTE: when successful, SetClipboardData() takes ownership of memory object! + if (NULL == SetClipboardData(CF_UNICODETEXT, object)) { goto error; } - EmptyClipboard(); - SetClipboardData(CF_UNICODETEXT, object); CloseClipboard(); return true; @@ -7414,6 +7569,7 @@ error: if (object) { GlobalFree(object); } + CloseClipboard(); return false; } @@ -7437,7 +7593,7 @@ _SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { return _sapp.clipboard.buffer; } if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) { - SAPP_LOG("sokol_app.h: clipboard string didn't fit into clipboard buffer\n"); + _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG); } GlobalUnlock(object); CloseClipboard(); @@ -7445,7 +7601,7 @@ _SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { } _SOKOL_PRIVATE void _sapp_win32_update_window_title(void) { - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); + _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); SetWindowTextW(_sapp.win32.hwnd, _sapp.window_title_wide); } @@ -7547,8 +7703,8 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { _sapp_init_state(desc); _sapp_win32_init_console(); _sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater(); - _sapp_win32_uwp_init_keytable(); - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); + _sapp_win32_init_keytable(); + _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); _sapp_win32_init_dpi(); _sapp_win32_init_cursors(); _sapp_win32_create_window(); @@ -7593,7 +7749,7 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { #if defined(SOKOL_D3D11) _sapp_d3d11_resize_default_render_target(); #endif - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); + _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); } /* check if the window monitor has changed, need to reset timing because the new monitor might have a different refresh rate @@ -7627,7 +7783,7 @@ _SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_lin LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc); if (w_argv == NULL) { - _sapp_fail("Win32: failed to parse command line"); + // FIXME: chicken egg problem, can't report errors before sokol_main() is called! } else { size_t size = wcslen(w_command_line) * 4; argv = (char**) _sapp_malloc_clear(((size_t)argc + 1) * sizeof(char*) + size); @@ -7637,7 +7793,7 @@ _SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_lin for (int i = 0; i < argc; ++i) { n = WideCharToMultiByte(CP_UTF8, 0, w_argv[i], -1, args, (int)size, NULL, NULL); if (n == 0) { - _sapp_fail("Win32: failed to convert all arguments to utf8"); + // FIXME: chicken egg problem, can't report errors before sokol_main() is called! break; } argv[i] = args; @@ -7679,1035 +7835,13 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _ #endif /* _SAPP_WIN32 */ -/*== UWP ================================================================*/ -#if defined(_SAPP_UWP) - -// Helper functions -_SOKOL_PRIVATE void _sapp_uwp_configure_dpi(float monitor_dpi) { - _sapp.uwp.dpi.window_scale = monitor_dpi / 96.0f; - if (_sapp.desc.high_dpi) { - _sapp.uwp.dpi.content_scale = _sapp.uwp.dpi.window_scale; - _sapp.uwp.dpi.mouse_scale = 1.0f * _sapp.uwp.dpi.window_scale; - } - else { - _sapp.uwp.dpi.content_scale = 1.0f; - _sapp.uwp.dpi.mouse_scale = 1.0f; - } - _sapp.dpi_scale = _sapp.uwp.dpi.content_scale; -} - -_SOKOL_PRIVATE void _sapp_uwp_update_cursor(sapp_mouse_cursor cursor, bool shown) { - using namespace winrt::Windows::UI::Core; - - CoreCursor uwp_cursor(nullptr); - if (shown) { - switch (cursor) { - case SAPP_MOUSECURSOR_ARROW: uwp_cursor = CoreCursor(CoreCursorType::Arrow, 0); break; - case SAPP_MOUSECURSOR_IBEAM: uwp_cursor = CoreCursor(CoreCursorType::IBeam, 0); break; - case SAPP_MOUSECURSOR_CROSSHAIR: uwp_cursor = CoreCursor(CoreCursorType::Cross, 0); break; - case SAPP_MOUSECURSOR_POINTING_HAND: uwp_cursor = CoreCursor(CoreCursorType::Hand, 0); break; - case SAPP_MOUSECURSOR_RESIZE_EW: uwp_cursor = CoreCursor(CoreCursorType::SizeWestEast, 0); break; - case SAPP_MOUSECURSOR_RESIZE_NS: uwp_cursor = CoreCursor(CoreCursorType::SizeNorthSouth, 0); break; - case SAPP_MOUSECURSOR_RESIZE_NWSE: uwp_cursor = CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); break; - case SAPP_MOUSECURSOR_RESIZE_NESW: uwp_cursor = CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); break; - case SAPP_MOUSECURSOR_RESIZE_ALL: uwp_cursor = CoreCursor(CoreCursorType::SizeAll, 0); break; - case SAPP_MOUSECURSOR_NOT_ALLOWED: uwp_cursor = CoreCursor(CoreCursorType::UniversalNo, 0); break; - default: uwp_cursor = CoreCursor(CoreCursorType::Arrow, 0); break; - } - } - CoreWindow::GetForCurrentThread().PointerCursor(uwp_cursor); -} - -_SOKOL_PRIVATE uint32_t _sapp_uwp_mods(winrt::Windows::UI::Core::CoreWindow const& sender_window) { - using namespace winrt::Windows::System; - using namespace winrt::Windows::UI::Core; - - uint32_t mods = 0; - if ((sender_window.GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_SHIFT; - } - if ((sender_window.GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_CTRL; - } - if ((sender_window.GetKeyState(VirtualKey::Menu) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_ALT; - } - if (((sender_window.GetKeyState(VirtualKey::LeftWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) || - ((sender_window.GetKeyState(VirtualKey::RightWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)) - { - mods |= SAPP_MODIFIER_SUPER; - } - if (0 != (_sapp.uwp.mouse_buttons & (1<= 32)) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); - _sapp.event.modifiers = _sapp_uwp_mods(sender_window); - _sapp.event.char_code = c; - _sapp.event.key_repeat = repeat; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_uwp_toggle_fullscreen(void) { - auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); - _sapp.fullscreen = appView.IsFullScreenMode(); - if (!_sapp.fullscreen) { - appView.TryEnterFullScreenMode(); - } - else { - appView.ExitFullScreenMode(); - } - _sapp.fullscreen = appView.IsFullScreenMode(); -} - -namespace {/* Empty namespace to ensure internal linkage (same as _SOKOL_PRIVATE) */ - -// Controls all the DirectX device resources. -class DeviceResources { -public: - // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created. - interface IDeviceNotify { - virtual void OnDeviceLost() = 0; - virtual void OnDeviceRestored() = 0; - }; - - DeviceResources(); - ~DeviceResources(); - void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); - void SetLogicalSize(winrt::Windows::Foundation::Size logicalSize); - void SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation); - void SetDpi(float dpi); - void ValidateDevice(); - void HandleDeviceLost(); - void RegisterDeviceNotify(IDeviceNotify* deviceNotify); - void Trim(); - void Present(); - -private: - - // Swapchain Rotation Matrices (Z-rotation) - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation0 = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation90 = { - 0.0f, 1.0f, 0.0f, 0.0f, - -1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation180 = { - -1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation270 = { - 0.0f, -1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - - void CreateDeviceResources(); - void CreateWindowSizeDependentResources(); - void UpdateRenderTargetSize(); - DXGI_MODE_ROTATION ComputeDisplayRotation(); - bool SdkLayersAvailable(); - - // Direct3D objects. - winrt::com_ptr m_d3dDevice; - winrt::com_ptr m_d3dContext; - winrt::com_ptr m_swapChain; - - // Direct3D rendering objects. Required for 3D. - winrt::com_ptr m_d3dRenderTarget; - winrt::com_ptr m_d3dRenderTargetView; - winrt::com_ptr m_d3dMSAARenderTarget; - winrt::com_ptr m_d3dMSAARenderTargetView; - winrt::com_ptr m_d3dDepthStencil; - winrt::com_ptr m_d3dDepthStencilView; - D3D11_VIEWPORT m_screenViewport = { }; - - // Cached reference to the Window. - winrt::agile_ref< winrt::Windows::UI::Core::CoreWindow> m_window; - - // Cached device properties. - D3D_FEATURE_LEVEL m_d3dFeatureLevel = D3D_FEATURE_LEVEL_9_1; - winrt::Windows::Foundation::Size m_d3dRenderTargetSize = { }; - winrt::Windows::Foundation::Size m_outputSize = { }; - winrt::Windows::Foundation::Size m_logicalSize = { }; - winrt::Windows::Graphics::Display::DisplayOrientations m_nativeOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; - winrt::Windows::Graphics::Display::DisplayOrientations m_currentOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; - float m_dpi = -1.0f; - - // Transforms used for display orientation. - DirectX::XMFLOAT4X4 m_orientationTransform3D; - - // The IDeviceNotify can be held directly as it owns the DeviceResources. - IDeviceNotify* m_deviceNotify = nullptr; -}; - -// Main entry point for our app. Connects the app with the Windows shell and handles application lifecycle events. -struct App : winrt::implements { -public: - // IFrameworkViewSource Methods - winrt::Windows::ApplicationModel::Core::IFrameworkView CreateView() { return *this; } - - // IFrameworkView Methods. - virtual void Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView); - virtual void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); - virtual void Load(winrt::hstring const& entryPoint); - virtual void Run(); - virtual void Uninitialize(); - -protected: - // Application lifecycle event handlers - void OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args); - void OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args); - void OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args); - - // Window event handlers - void OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args); - void OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args); - - // Navigation event handlers - void OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args); - - // Input event handlers - void OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); - void OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); - void OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args); - - // Pointer event handlers - void OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - - // DisplayInformation event handlers. - void OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - void OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - void OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - -private: - std::unique_ptr m_deviceResources; - bool m_windowVisible = true; -}; - -DeviceResources::DeviceResources() { - CreateDeviceResources(); -} - -DeviceResources::~DeviceResources() { - // Cleanup Sokol Context - _sapp.d3d11.device = nullptr; - _sapp.d3d11.device_context = nullptr; -} - -void DeviceResources::CreateDeviceResources() { - // This flag adds support for surfaces with a different color channel ordering - // than the API default. It is required for compatibility with Direct2D. - UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - - #if defined(_DEBUG) - if (SdkLayersAvailable()) { - // If the project is in a debug build, enable debugging via SDK Layers with this flag. - creationFlags |= D3D11_CREATE_DEVICE_DEBUG; - } - #endif - - // This array defines the set of DirectX hardware feature levels this app will support. - // Note the ordering should be preserved. - // Don't forget to declare your application's minimum required feature level in its - // description. All applications are assumed to support 9.1 unless otherwise stated. - D3D_FEATURE_LEVEL featureLevels[] = { - D3D_FEATURE_LEVEL_12_1, - D3D_FEATURE_LEVEL_12_0, - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; - - // Create the Direct3D 11 API device object and a corresponding context. - winrt::com_ptr device; - winrt::com_ptr context; - - HRESULT hr = D3D11CreateDevice( - nullptr, // Specify nullptr to use the default adapter. - D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver. - 0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE. - creationFlags, // Set debug and Direct2D compatibility flags. - featureLevels, // List of feature levels this app can support. - ARRAYSIZE(featureLevels), // Size of the list above. - D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. - device.put(), // Returns the Direct3D device created. - &m_d3dFeatureLevel, // Returns feature level of device created. - context.put() // Returns the device immediate context. - ); - - if (FAILED(hr)) { - // If the initialization fails, fall back to the WARP device. - // For more information on WARP, see: - // https://go.microsoft.com/fwlink/?LinkId=286690 - winrt::check_hresult( - D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device. - 0, - creationFlags, - featureLevels, - ARRAYSIZE(featureLevels), - D3D11_SDK_VERSION, - device.put(), - &m_d3dFeatureLevel, - context.put() - ) - ); - } - - // Store pointers to the Direct3D 11.3 API device and immediate context. - m_d3dDevice = device.as(); - m_d3dContext = context.as(); - - // Setup Sokol Context - _sapp.d3d11.device = m_d3dDevice.get(); - _sapp.d3d11.device_context = m_d3dContext.get(); -} - -void DeviceResources::CreateWindowSizeDependentResources() { - // Cleanup Sokol Context (these are non-owning raw pointers) - _sapp.d3d11.rt = nullptr; - _sapp.d3d11.rtv = nullptr; - _sapp.d3d11.msaa_rt = nullptr; - _sapp.d3d11.msaa_rtv = nullptr; - _sapp.d3d11.ds = nullptr; - _sapp.d3d11.dsv = nullptr; - - // Clear the previous window size specific context. - ID3D11RenderTargetView* nullViews[] = { nullptr }; - m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); - // these are smart pointers, setting to nullptr will delete the objects - m_d3dRenderTarget = nullptr; - m_d3dRenderTargetView = nullptr; - m_d3dMSAARenderTarget = nullptr; - m_d3dMSAARenderTargetView = nullptr; - m_d3dDepthStencilView = nullptr; - m_d3dDepthStencil = nullptr; - m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr); - - UpdateRenderTargetSize(); - - // The width and height of the swap chain must be based on the window's - // natively-oriented width and height. If the window is not in the native - // orientation, the dimensions must be reversed. - DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation(); - - bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270; - m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width; - m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height; - - if (m_swapChain != nullptr) { - // If the swap chain already exists, resize it. - HRESULT hr = m_swapChain->ResizeBuffers( - 2, // Double-buffered swap chain. - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - DXGI_FORMAT_B8G8R8A8_UNORM, - 0 - ); - - if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { - // If the device was removed for any reason, a new device and swap chain will need to be created. - HandleDeviceLost(); - - // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method - // and correctly set up the new device. - return; - } - else { - winrt::check_hresult(hr); - } - } - else { - // Otherwise, create a new one using the same adapter as the existing Direct3D device. - DXGI_SCALING scaling = (_sapp.uwp.dpi.content_scale == _sapp.uwp.dpi.window_scale) ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH; - DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 }; - - swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window. - swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height); - swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. - swapChainDesc.Stereo = false; - swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. - swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency. - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Microsoft Store apps must use this SwapEffect. - swapChainDesc.Flags = 0; - swapChainDesc.Scaling = scaling; - swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - - // This sequence obtains the DXGI factory that was used to create the Direct3D device above. - winrt::com_ptr dxgiDevice = m_d3dDevice.as(); - winrt::com_ptr dxgiAdapter; - winrt::check_hresult(dxgiDevice->GetAdapter(dxgiAdapter.put())); - winrt::com_ptr dxgiFactory; - winrt::check_hresult(dxgiAdapter->GetParent(__uuidof(IDXGIFactory4), dxgiFactory.put_void())); - winrt::com_ptr swapChain; - winrt::check_hresult(dxgiFactory->CreateSwapChainForCoreWindow(m_d3dDevice.get(), m_window.get().as<::IUnknown>().get(), &swapChainDesc, nullptr, swapChain.put())); - m_swapChain = swapChain.as(); - - // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and - // ensures that the application will only render after each VSync, minimizing power consumption. - winrt::check_hresult(dxgiDevice->SetMaximumFrameLatency(1)); - - // Setup Sokol Context - winrt::check_hresult(swapChain->GetDesc(&_sapp.d3d11.swap_chain_desc)); - _sapp.d3d11.swap_chain = m_swapChain.as().detach(); - } - - // Set the proper orientation for the swap chain, and generate 2D and - // 3D matrix transformations for rendering to the rotated swap chain. - // Note the rotation angle for the 2D and 3D transforms are different. - // This is due to the difference in coordinate spaces. Additionally, - // the 3D matrix is specified explicitly to avoid rounding errors. - switch (displayRotation) { - case DXGI_MODE_ROTATION_IDENTITY: - m_orientationTransform3D = m_rotation0; - break; - - case DXGI_MODE_ROTATION_ROTATE90: - m_orientationTransform3D = m_rotation270; - break; - - case DXGI_MODE_ROTATION_ROTATE180: - m_orientationTransform3D = m_rotation180; - break; - - case DXGI_MODE_ROTATION_ROTATE270: - m_orientationTransform3D = m_rotation90; - break; - } - winrt::check_hresult(m_swapChain->SetRotation(displayRotation)); - - // Create a render target view of the swap chain back buffer. - winrt::check_hresult(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_d3dRenderTarget))); - winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dRenderTarget.get(), nullptr, m_d3dRenderTargetView.put())); - - // Create MSAA texture and view if needed - if (_sapp.sample_count > 1) { - CD3D11_TEXTURE2D_DESC1 msaaTexDesc( - DXGI_FORMAT_B8G8R8A8_UNORM, - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - 1, // arraySize - 1, // mipLevels - D3D11_BIND_RENDER_TARGET, - D3D11_USAGE_DEFAULT, - 0, // cpuAccessFlags - _sapp.sample_count, - _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 - ); - winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&msaaTexDesc, nullptr, m_d3dMSAARenderTarget.put())); - winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dMSAARenderTarget.get(), nullptr, m_d3dMSAARenderTargetView.put())); - } - - // Create a depth stencil view for use with 3D rendering if needed. - CD3D11_TEXTURE2D_DESC1 depthStencilDesc( - DXGI_FORMAT_D24_UNORM_S8_UINT, - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - 1, // This depth stencil view has only one texture. - 1, // Use a single mipmap level. - D3D11_BIND_DEPTH_STENCIL, - D3D11_USAGE_DEFAULT, - 0, // cpuAccessFlag - _sapp.sample_count, - _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 - ); - winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&depthStencilDesc, nullptr, m_d3dDepthStencil.put())); - - CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); - winrt::check_hresult(m_d3dDevice->CreateDepthStencilView(m_d3dDepthStencil.get(), nullptr, m_d3dDepthStencilView.put())); - - // Set sokol window and framebuffer sizes - _sapp.window_width = (int) m_logicalSize.Width; - _sapp.window_height = (int) m_logicalSize.Height; - _sapp.framebuffer_width = lround(m_d3dRenderTargetSize.Width); - _sapp.framebuffer_height = lround(m_d3dRenderTargetSize.Height); - - // Setup Sokol Context - _sapp.d3d11.rt = m_d3dRenderTarget.as().get(); - _sapp.d3d11.rtv = m_d3dRenderTargetView.as().get(); - _sapp.d3d11.ds = m_d3dDepthStencil.as().get(); - _sapp.d3d11.dsv = m_d3dDepthStencilView.get(); - if (_sapp.sample_count > 1) { - _sapp.d3d11.msaa_rt = m_d3dMSAARenderTarget.as().get(); - _sapp.d3d11.msaa_rtv = m_d3dMSAARenderTargetView.as().get(); - } - - // Sokol app is now valid - _sapp.valid = true; -} - -// Determine the dimensions of the render target and whether it will be scaled down. -void DeviceResources::UpdateRenderTargetSize() { - // Calculate the necessary render target size in pixels. - m_outputSize.Width = m_logicalSize.Width * _sapp.uwp.dpi.content_scale; - m_outputSize.Height = m_logicalSize.Height * _sapp.uwp.dpi.content_scale; - - // Prevent zero size DirectX content from being created. - m_outputSize.Width = std::max(m_outputSize.Width, 1.0f); - m_outputSize.Height = std::max(m_outputSize.Height, 1.0f); -} - -// This method is called when the CoreWindow is created (or re-created). -void DeviceResources::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { - auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); - m_window = window; - m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); - m_nativeOrientation = currentDisplayInformation.NativeOrientation(); - m_currentOrientation = currentDisplayInformation.CurrentOrientation(); - m_dpi = currentDisplayInformation.LogicalDpi(); - _sapp_uwp_configure_dpi(m_dpi); - CreateWindowSizeDependentResources(); -} - -// This method is called in the event handler for the SizeChanged event. -void DeviceResources::SetLogicalSize(winrt::Windows::Foundation::Size logicalSize) { - if (m_logicalSize != logicalSize) { - m_logicalSize = logicalSize; - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the DpiChanged event. -void DeviceResources::SetDpi(float dpi) { - if (dpi != m_dpi) { - m_dpi = dpi; - _sapp_uwp_configure_dpi(m_dpi); - // When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated. - auto window = m_window.get(); - m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the OrientationChanged event. -void DeviceResources::SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation) { - if (m_currentOrientation != currentOrientation) { - m_currentOrientation = currentOrientation; - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the DisplayContentsInvalidated event. -void DeviceResources::ValidateDevice() { - // The D3D Device is no longer valid if the default adapter changed since the device - // was created or if the device has been removed. - - // First, get the information for the default adapter from when the device was created. - winrt::com_ptr dxgiDevice = m_d3dDevice.as< IDXGIDevice3>(); - winrt::com_ptr deviceAdapter; - winrt::check_hresult(dxgiDevice->GetAdapter(deviceAdapter.put())); - winrt::com_ptr deviceFactory; - winrt::check_hresult(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory))); - winrt::com_ptr previousDefaultAdapter; - winrt::check_hresult(deviceFactory->EnumAdapters1(0, previousDefaultAdapter.put())); - DXGI_ADAPTER_DESC1 previousDesc; - winrt::check_hresult(previousDefaultAdapter->GetDesc1(&previousDesc)); - - // Next, get the information for the current default adapter. - winrt::com_ptr currentFactory; - winrt::check_hresult(CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory))); - winrt::com_ptr currentDefaultAdapter; - winrt::check_hresult(currentFactory->EnumAdapters1(0, currentDefaultAdapter.put())); - DXGI_ADAPTER_DESC1 currentDesc; - winrt::check_hresult(currentDefaultAdapter->GetDesc1(¤tDesc)); - - // If the adapter LUIDs don't match, or if the device reports that it has been removed, - // a new D3D device must be created. - if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart || - previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart || - FAILED(m_d3dDevice->GetDeviceRemovedReason())) - { - // Release references to resources related to the old device. - dxgiDevice = nullptr; - deviceAdapter = nullptr; - deviceFactory = nullptr; - previousDefaultAdapter = nullptr; - - // Create a new device and swap chain. - HandleDeviceLost(); - } -} - -// Recreate all device resources and set them back to the current state. -void DeviceResources::HandleDeviceLost() { - m_swapChain = nullptr; - if (m_deviceNotify != nullptr) { - m_deviceNotify->OnDeviceLost(); - } - CreateDeviceResources(); - CreateWindowSizeDependentResources(); - if (m_deviceNotify != nullptr) { - m_deviceNotify->OnDeviceRestored(); - } -} - -// Register our DeviceNotify to be informed on device lost and creation. -void DeviceResources::RegisterDeviceNotify(IDeviceNotify* deviceNotify) { - m_deviceNotify = deviceNotify; -} - -// Call this method when the app suspends. It provides a hint to the driver that the app -// is entering an idle state and that temporary buffers can be reclaimed for use by other apps. -void DeviceResources::Trim() { - m_d3dDevice.as()->Trim(); -} - -// Present the contents of the swap chain to the screen. -void DeviceResources::Present() { - - // MSAA resolve if needed - if (_sapp.sample_count > 1) { - m_d3dContext->ResolveSubresource(m_d3dRenderTarget.get(), 0, m_d3dMSAARenderTarget.get(), 0, DXGI_FORMAT_B8G8R8A8_UNORM); - m_d3dContext->DiscardView1(m_d3dMSAARenderTargetView.get(), nullptr, 0); - } - - // The first argument instructs DXGI to block until VSync, putting the application - // to sleep until the next VSync. This ensures we don't waste any cycles rendering - // frames that will never be displayed to the screen. - DXGI_PRESENT_PARAMETERS parameters = { 0 }; - HRESULT hr = m_swapChain->Present1(1, 0, ¶meters); - - // Discard the contents of the render target. - // This is a valid operation only when the existing contents will be entirely - // overwritten. If dirty or scroll rects are used, this call should be removed. - m_d3dContext->DiscardView1(m_d3dRenderTargetView.get(), nullptr, 0); - - // Discard the contents of the depth stencil. - m_d3dContext->DiscardView1(m_d3dDepthStencilView.get(), nullptr, 0); - - // If the device was removed either by a disconnection or a driver upgrade, we - // must recreate all device resources. - if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { - HandleDeviceLost(); - } - else { - winrt::check_hresult(hr); - } -} - -// This method determines the rotation between the display device's native orientation and the -// current display orientation. -DXGI_MODE_ROTATION DeviceResources::ComputeDisplayRotation() { - DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED; - - // Note: NativeOrientation can only be Landscape or Portrait even though - // the DisplayOrientations enum has other values. - switch (m_nativeOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - switch (m_currentOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - rotation = DXGI_MODE_ROTATION_IDENTITY; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - rotation = DXGI_MODE_ROTATION_ROTATE270; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE180; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE90; - break; - } - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - switch (m_currentOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - rotation = DXGI_MODE_ROTATION_ROTATE90; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - rotation = DXGI_MODE_ROTATION_IDENTITY; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE270; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE180; - break; - } - break; - } - return rotation; -} - -// Check for SDK Layer support. -bool DeviceResources::SdkLayersAvailable() { - #if defined(_DEBUG) - HRESULT hr = D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device. - 0, - D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers. - nullptr, // Any feature level will do. - 0, - D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. - nullptr, // No need to keep the D3D device reference. - nullptr, // No need to know the feature level. - nullptr // No need to keep the D3D device context reference. - ); - return SUCCEEDED(hr); - #else - return false; - #endif -} - -// The first method called when the IFrameworkView is being created. -void App::Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView) { - // Register event handlers for app lifecycle. This example includes Activated, so that we - // can make the CoreWindow active and start rendering on the window. - applicationView.Activated({ this, &App::OnActivated }); - - winrt::Windows::ApplicationModel::Core::CoreApplication::Suspending({ this, &App::OnSuspending }); - winrt::Windows::ApplicationModel::Core::CoreApplication::Resuming({ this, &App::OnResuming }); - - // At this point we have access to the device. - // We can create the device-dependent resources. - m_deviceResources = std::make_unique(); -} - -// Called when the CoreWindow object is created (or re-created). -void App::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { - window.SizeChanged({ this, &App::OnWindowSizeChanged }); - window.VisibilityChanged({ this, &App::OnVisibilityChanged }); - - window.KeyDown({ this, &App::OnKeyDown }); - window.KeyUp({ this, &App::OnKeyUp }); - window.CharacterReceived({ this, &App::OnCharacterReceived }); - - window.PointerEntered({ this, &App::OnPointerEntered }); - window.PointerExited({ this, &App::OnPointerExited }); - window.PointerPressed({ this, &App::OnPointerPressed }); - window.PointerReleased({ this, &App::OnPointerReleased }); - window.PointerMoved({ this, &App::OnPointerMoved }); - window.PointerWheelChanged({ this, &App::OnPointerWheelChanged }); - - auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); - - currentDisplayInformation.DpiChanged({ this, &App::OnDpiChanged }); - currentDisplayInformation.OrientationChanged({ this, &App::OnOrientationChanged }); - winrt::Windows::Graphics::Display::DisplayInformation::DisplayContentsInvalidated({ this, &App::OnDisplayContentsInvalidated }); - - winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested({ this, &App::OnBackRequested }); - - m_deviceResources->SetWindow(window); -} - -// Initializes scene resources, or loads a previously saved app state. -void App::Load(winrt::hstring const& entryPoint) { - _SOKOL_UNUSED(entryPoint); -} - -// This method is called after the window becomes active. -void App::Run() { - // NOTE: UWP will simply terminate an application, it's not possible to detect when an application is being closed - while (true) { - if (m_windowVisible) { - _sapp_timing_measure(&_sapp.timing); - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent); - _sapp_frame(); - m_deviceResources->Present(); - } - else { - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessOneAndAllPending); - } - } -} - -// Required for IFrameworkView. -// Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView -// class is torn down while the app is in the foreground. -void App::Uninitialize() { - // empty -} - -// Application lifecycle event handlers. -void App::OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args) { - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(applicationView); - auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); - const float window_width = (float)_sapp_def(_sapp.desc.width, _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH); - const float window_height = (float)_sapp_def(_sapp.desc.height, _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT); - auto targetSize = winrt::Windows::Foundation::Size(window_width, window_height); - winrt::Windows::UI::ViewManagement::ApplicationView::PreferredLaunchViewSize(targetSize); - winrt::Windows::UI::ViewManagement::ApplicationView::PreferredLaunchWindowingMode(winrt::Windows::UI::ViewManagement::ApplicationViewWindowingMode::PreferredLaunchViewSize); - appView.SetPreferredMinSize(targetSize); - appView.TryResizeView(targetSize); - - // Disabling this since it can only append the title to the app name (Title - Appname). - // There's no way of just setting a string to be the window title. - //appView.Title(_sapp.window_title_wide); - - // Run() won't start until the CoreWindow is activated. - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Activate(); - if (_sapp.desc.fullscreen) { - appView.TryEnterFullScreenMode(); - } - _sapp.fullscreen = appView.IsFullScreenMode(); -} - -void App::OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args) { - _SOKOL_UNUSED(sender); - _SOKOL_UNUSED(args); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_SUSPENDED); -} - -void App::OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args) { - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(sender); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESUMED); -} - -void App::OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args) { - _SOKOL_UNUSED(args); - m_deviceResources->SetLogicalSize(winrt::Windows::Foundation::Size(sender.Bounds().Width, sender.Bounds().Height)); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args) { - _SOKOL_UNUSED(sender); - m_windowVisible = args.Visible(); - _sapp_win32_uwp_app_event(m_windowVisible ? SAPP_EVENTTYPE_RESTORED : SAPP_EVENTTYPE_ICONIFIED); -} - -void App::OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args) { - _SOKOL_UNUSED(sender); - args.Handled(true); -} - -void App::OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { - auto status = args.KeyStatus(); - _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_DOWN, sender, args); -} - -void App::OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { - auto status = args.KeyStatus(); - _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_UP, sender, args); -} - -void App::OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args) { - _sapp_uwp_char_event(args.KeyCode(), args.KeyStatus().WasKeyDown, sender); -} - -void App::OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _SOKOL_UNUSED(args); - _sapp.uwp.mouse_tracked = true; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); -} - -void App::OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _SOKOL_UNUSED(args); - _sapp.uwp.mouse_tracked = false; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, sender); -} - -void App::OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -// NOTE: for some reason this event handler is never called?? -void App::OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -void App::OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - auto position = args.CurrentPoint().Position(); - const float new_x = (float)(int)(position.X * _sapp.uwp.dpi.mouse_scale + 0.5f); - const float new_y = (float)(int)(position.Y * _sapp.uwp.dpi.mouse_scale + 0.5f); - // don't update dx/dy in the very first event - if (_sapp.mouse.pos_valid) { - _sapp.mouse.dx = new_x - _sapp.mouse.x; - _sapp.mouse.dy = new_y - _sapp.mouse.y; - } - _sapp.mouse.x = new_x; - _sapp.mouse.y = new_y; - _sapp.mouse.pos_valid = true; - if (!_sapp.uwp.mouse_tracked) { - _sapp.uwp.mouse_tracked = true; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); - } - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, sender); - - // HACK for detecting multiple mouse button presses - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -void App::OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - auto properties = args.CurrentPoint().Properties(); - _sapp_uwp_scroll_event((float)properties.MouseWheelDelta(), properties.IsHorizontalMouseWheel(), sender); -} - -void App::OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - m_deviceResources->SetDpi(sender.LogicalDpi()); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - m_deviceResources->SetCurrentOrientation(sender.CurrentOrientation()); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(sender); - m_deviceResources->ValidateDevice(); -} - -} /* End empty namespace */ - -_SOKOL_PRIVATE void _sapp_uwp_run(const sapp_desc* desc) { - _sapp_init_state(desc); - _sapp_win32_uwp_init_keytable(); - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); - winrt::Windows::ApplicationModel::Core::CoreApplication::Run(winrt::make()); -} - -#if !defined(SOKOL_NO_ENTRY) -#if defined(UNICODE) -int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { -#else -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { -#endif - _SOKOL_UNUSED(hInstance); - _SOKOL_UNUSED(hPrevInstance); - _SOKOL_UNUSED(lpCmdLine); - _SOKOL_UNUSED(nCmdShow); - sapp_desc desc = sokol_main(0, nullptr); - _sapp_uwp_run(&desc); - return 0; -} -#endif /* SOKOL_NO_ENTRY */ -#endif /* _SAPP_UWP */ - -/*== Android ================================================================*/ +// █████ ███ ██ ██████ ██████ ██████ ██ ██████ +// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██ ██ ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ████ ██████ ██ ██ ██████ ██ ██████ +// +// >>android #if defined(_SAPP_ANDROID) /* android loop thread */ @@ -8722,16 +7856,10 @@ _SOKOL_PRIVATE bool _sapp_android_init_egl(void) { if (eglInitialize(display, NULL, NULL) == EGL_FALSE) { return false; } - _sapp.gles2_fallback = _sapp.desc.gl_force_gles2; - EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; const EGLint cfg_attributes[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - #if defined(SOKOL_GLES3) - EGL_RENDERABLE_TYPE, _sapp.desc.gl_force_gles2?EGL_OPENGL_ES2_BIT:EGL_OPENGL_ES3_BIT, - #else - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - #endif + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, @@ -8768,11 +7896,7 @@ _SOKOL_PRIVATE bool _sapp_android_init_egl(void) { } EGLint ctx_attributes[] = { - #if defined(SOKOL_GLES3) - EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3, - #else - EGL_CONTEXT_CLIENT_VERSION, 2, - #endif + EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE, }; EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes); @@ -8790,16 +7914,13 @@ _SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) { if (_sapp.android.display != EGL_NO_DISPLAY) { eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (_sapp.android.surface != EGL_NO_SURFACE) { - SAPP_LOG("Destroying egl surface"); eglDestroySurface(_sapp.android.display, _sapp.android.surface); _sapp.android.surface = EGL_NO_SURFACE; } if (_sapp.android.context != EGL_NO_CONTEXT) { - SAPP_LOG("Destroying egl context"); eglDestroyContext(_sapp.android.display, _sapp.android.context); _sapp.android.context = EGL_NO_CONTEXT; } - SAPP_LOG("Terminating egl display"); eglTerminate(_sapp.android.display); _sapp.android.display = EGL_NO_DISPLAY; } @@ -8840,7 +7961,6 @@ _SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) { _SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { _sapp_init_event(type); - SAPP_LOG("event_cb()"); _sapp_call_event(&_sapp.event); } } @@ -8886,18 +8006,15 @@ _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; if (win_changed || fb_changed || force_update) { if (!_sapp.first_frame) { - SAPP_LOG("SAPP_EVENTTYPE_RESIZED"); _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED); } } } _SOKOL_PRIVATE void _sapp_android_cleanup(void) { - SAPP_LOG("Cleaning up"); if (_sapp.android.surface != EGL_NO_SURFACE) { /* egl context is bound, cleanup gracefully */ if (_sapp.init_called && !_sapp.cleanup_called) { - SAPP_LOG("cleanup_cb()"); _sapp_call_cleanup(); } } @@ -8934,22 +8051,17 @@ _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { sapp_event_type type = SAPP_EVENTTYPE_INVALID; switch (action) { case AMOTION_EVENT_ACTION_DOWN: - SAPP_LOG("Touch: down"); case AMOTION_EVENT_ACTION_POINTER_DOWN: - SAPP_LOG("Touch: ptr down"); type = SAPP_EVENTTYPE_TOUCHES_BEGAN; break; case AMOTION_EVENT_ACTION_MOVE: type = SAPP_EVENTTYPE_TOUCHES_MOVED; break; case AMOTION_EVENT_ACTION_UP: - SAPP_LOG("Touch: up"); case AMOTION_EVENT_ACTION_POINTER_UP: - SAPP_LOG("Touch: ptr up"); type = SAPP_EVENTTYPE_TOUCHES_ENDED; break; case AMOTION_EVENT_ACTION_CANCEL: - SAPP_LOG("Touch: cancel"); type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; break; default: @@ -9000,7 +8112,7 @@ _SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { _SOKOL_UNUSED(fd); _SOKOL_UNUSED(data); if ((events & ALOOPER_EVENT_INPUT) == 0) { - SAPP_LOG("_sapp_android_input_cb() encountered unsupported event"); + _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB); return 1; } SOKOL_ASSERT(_sapp.android.current.input); @@ -9021,13 +8133,13 @@ _SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { _SOKOL_UNUSED(data); if ((events & ALOOPER_EVENT_INPUT) == 0) { - SAPP_LOG("_sapp_android_main_cb() encountered unsupported event"); + _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB); return 1; } _sapp_android_msg_t msg; if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) { - SAPP_LOG("Could not write to read_from_main_fd"); + _SAPP_ERROR(ANDROID_READ_MSG_FAILED); return 1; } @@ -9035,7 +8147,7 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { switch (msg) { case _SOKOL_ANDROID_MSG_CREATE: { - SAPP_LOG("MSG_CREATE"); + _SAPP_INFO(ANDROID_MSG_CREATE); SOKOL_ASSERT(!_sapp.valid); bool result = _sapp_android_init_egl(); SOKOL_ASSERT(result); _SOKOL_UNUSED(result); @@ -9044,36 +8156,33 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { } break; case _SOKOL_ANDROID_MSG_RESUME: - SAPP_LOG("MSG_RESUME"); + _SAPP_INFO(ANDROID_MSG_RESUME); _sapp.android.has_resumed = true; _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED); break; case _SOKOL_ANDROID_MSG_PAUSE: - SAPP_LOG("MSG_PAUSE"); + _SAPP_INFO(ANDROID_MSG_PAUSE); _sapp.android.has_resumed = false; _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED); break; case _SOKOL_ANDROID_MSG_FOCUS: - SAPP_LOG("MSG_FOCUS"); + _SAPP_INFO(ANDROID_MSG_FOCUS); _sapp.android.has_focus = true; break; case _SOKOL_ANDROID_MSG_NO_FOCUS: - SAPP_LOG("MSG_NO_FOCUS"); + _SAPP_INFO(ANDROID_MSG_NO_FOCUS); _sapp.android.has_focus = false; break; case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW: - SAPP_LOG("MSG_SET_NATIVE_WINDOW"); + _SAPP_INFO(ANDROID_MSG_SET_NATIVE_WINDOW); if (_sapp.android.current.window != _sapp.android.pending.window) { if (_sapp.android.current.window != NULL) { _sapp_android_cleanup_egl_surface(); } if (_sapp.android.pending.window != NULL) { - SAPP_LOG("Creating egl surface ..."); if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) { - SAPP_LOG("... ok!"); _sapp_android_update_dimensions(_sapp.android.pending.window, true); } else { - SAPP_LOG("... failed!"); _sapp_android_shutdown(); } } @@ -9081,7 +8190,7 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { _sapp.android.current.window = _sapp.android.pending.window; break; case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE: - SAPP_LOG("MSG_SET_INPUT_QUEUE"); + _SAPP_INFO(ANDROID_MSG_SET_INPUT_QUEUE); if (_sapp.android.current.input != _sapp.android.pending.input) { if (_sapp.android.current.input != NULL) { AInputQueue_detachLooper(_sapp.android.current.input); @@ -9098,13 +8207,13 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { _sapp.android.current.input = _sapp.android.pending.input; break; case _SOKOL_ANDROID_MSG_DESTROY: - SAPP_LOG("MSG_DESTROY"); + _SAPP_INFO(ANDROID_MSG_DESTROY); _sapp_android_cleanup(); _sapp.valid = false; _sapp.android.is_thread_stopping = true; break; default: - SAPP_LOG("Unknown msg type received"); + _SAPP_WARN(ANDROID_UNKNOWN_MSG); break; } pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */ @@ -9122,17 +8231,15 @@ _SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) { SOKOL_ASSERT(_sapp.valid); /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */ if (shown) { - SAPP_LOG("Showing keyboard"); ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); } else { - SAPP_LOG("Hiding keyboard"); ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); } } _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { _SOKOL_UNUSED(arg); - SAPP_LOG("Loop thread started"); + _SAPP_INFO(ANDROID_LOOP_THREAD_STARTED); _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/); ALooper_addFd(_sapp.android.looper, @@ -9177,38 +8284,39 @@ _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { _sapp.android.is_thread_stopped = true; pthread_cond_broadcast(&_sapp.android.pt.cond); pthread_mutex_unlock(&_sapp.android.pt.mutex); - SAPP_LOG("Loop thread done"); + + _SAPP_INFO(ANDROID_LOOP_THREAD_DONE); return NULL; } /* android main/ui thread */ _SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_msg_t msg) { if (write(_sapp.android.pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) { - SAPP_LOG("Could not write to write_from_main_fd"); + _SAPP_ERROR(ANDROID_WRITE_MSG_FAILED); } } _SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onStart()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTART); } _SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onResume()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONRESUME); _sapp_android_msg(_SOKOL_ANDROID_MSG_RESUME); } _SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onSaveInstanceState()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSAVEINSTANCESTATE); *out_size = 0; return NULL; } _SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onWindowFocusChanged()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED); if (has_focus) { _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS); } else { @@ -9218,13 +8326,13 @@ _SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activ _SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onPause()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONPAUSE); _sapp_android_msg(_SOKOL_ANDROID_MSG_PAUSE); } _SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onStop()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTOP); } _SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { @@ -9239,14 +8347,14 @@ _SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { _SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onNativeWindowCreated()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWCREATED); _sapp_android_msg_set_native_window(window); } _SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) { _SOKOL_UNUSED(activity); _SOKOL_UNUSED(window); - SAPP_LOG("NativeActivity onNativeWindowDestroyed()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED); _sapp_android_msg_set_native_window(NULL); } @@ -9262,26 +8370,26 @@ _SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) { _SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onInputQueueCreated()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUECREATED); _sapp_android_msg_set_input_queue(queue); } _SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) { _SOKOL_UNUSED(activity); _SOKOL_UNUSED(queue); - SAPP_LOG("NativeActivity onInputQueueDestroyed()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUEDESTROYED); _sapp_android_msg_set_input_queue(NULL); } _SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onConfigurationChanged()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED); /* see android:configChanges in manifest */ } _SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onLowMemory()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY); } _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { @@ -9294,7 +8402,7 @@ _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity? */ _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onDestroy()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONDESTROY); /* send destroy msg */ pthread_mutex_lock(&_sapp.android.pt.mutex); @@ -9311,7 +8419,7 @@ _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { close(_sapp.android.pt.read_from_main_fd); close(_sapp.android.pt.write_from_main_fd); - SAPP_LOG("NativeActivity done"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_DONE); /* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */ exit(0); @@ -9321,7 +8429,7 @@ JNIEXPORT void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) { _SOKOL_UNUSED(saved_state); _SOKOL_UNUSED(saved_state_size); - SAPP_LOG("NativeActivity onCreate()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONCREATE); // the NativeActity pointer needs to be available inside sokol_main() // (see https://github.com/floooh/sokol/issues/708), however _sapp_init_state() @@ -9335,7 +8443,7 @@ void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size int pipe_fd[2]; if (pipe(pipe_fd) != 0) { - SAPP_LOG("Could not create thread pipe"); + _SAPP_ERROR(ANDROID_CREATE_THREAD_PIPE_FAILED); return; } _sapp.android.pt.read_from_main_fd = pipe_fd[0]; @@ -9383,14 +8491,20 @@ void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; activity->callbacks->onLowMemory = _sapp_android_on_low_memory; - SAPP_LOG("NativeActivity successfully created"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS); /* NOT A BUG: do NOT call sapp_discard_state() */ } #endif /* _SAPP_ANDROID */ -/*== LINUX ==================================================================*/ +// ██ ██ ███ ██ ██ ██ ██ ██ +// ██ ██ ████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ████ ██████ ██ ██ +// +// >>linux #if defined(_SAPP_LINUX) /* see GLFW's xkb_unicode.c */ @@ -10309,7 +9423,7 @@ _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { // fallback if querying DPI had failed: assume the standard DPI 96.0f if (!dpi_ok) { _sapp.x11.dpi = 96.0f; - SAPP_LOG("sokol_app.h: failed to query system dpi value, assuming default 96.0"); + _SAPP_WARN(LINUX_X11_QUERY_SYSTEM_DPI_FAILED); } } @@ -10365,7 +9479,7 @@ _SOKOL_PRIVATE void _sapp_glx_init() { } } if (!_sapp.glx.libgl) { - _sapp_fail("GLX: failed to load libGL"); + _SAPP_PANIC(LINUX_GLX_LOAD_LIBGL_FAILED); } _sapp.glx.GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigs"); _sapp.glx.GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigAttrib"); @@ -10396,17 +9510,17 @@ _SOKOL_PRIVATE void _sapp_glx_init() { !_sapp.glx.GetProcAddressARB || !_sapp.glx.GetVisualFromFBConfig) { - _sapp_fail("GLX: failed to load required entry points"); + _SAPP_PANIC(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED); } if (!_sapp.glx.QueryExtension(_sapp.x11.display, &_sapp.glx.error_base, &_sapp.glx.event_base)) { - _sapp_fail("GLX: GLX extension not found"); + _SAPP_PANIC(LINUX_GLX_EXTENSION_NOT_FOUND); } if (!_sapp.glx.QueryVersion(_sapp.x11.display, &_sapp.glx.major, &_sapp.glx.minor)) { - _sapp_fail("GLX: Failed to query GLX version"); + _SAPP_PANIC(LINUX_GLX_QUERY_VERSION_FAILED); } if (_sapp.glx.major == 1 && _sapp.glx.minor < 3) { - _sapp_fail("GLX: GLX version 1.3 is required"); + _SAPP_PANIC(LINUX_GLX_VERSION_TOO_LOW); } const char* exts = _sapp.glx.QueryExtensionsString(_sapp.x11.display, _sapp.x11.screen); if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) { @@ -10449,7 +9563,7 @@ _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { native_configs = _sapp.glx.GetFBConfigs(_sapp.x11.display, _sapp.x11.screen, &native_count); if (!native_configs || !native_count) { - _sapp_fail("GLX: No GLXFBConfigs returned"); + _SAPP_PANIC(LINUX_GLX_NO_GLXFBCONFIGS); } usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig)); @@ -10507,11 +9621,11 @@ _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { GLXFBConfig native = _sapp_glx_choosefbconfig(); if (0 == native) { - _sapp_fail("GLX: Failed to find a suitable GLXFBConfig"); + _SAPP_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG); } XVisualInfo* result = _sapp.glx.GetVisualFromFBConfig(_sapp.x11.display, native); if (!result) { - _sapp_fail("GLX: Failed to retrieve Visual for GLXFBConfig"); + _SAPP_PANIC(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED); } *visual = result->visual; *depth = result->depth; @@ -10521,10 +9635,10 @@ _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { _SOKOL_PRIVATE void _sapp_glx_create_context(void) { GLXFBConfig native = _sapp_glx_choosefbconfig(); if (0 == native){ - _sapp_fail("GLX: Failed to find a suitable GLXFBConfig (2)"); + _SAPP_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG); } if (!(_sapp.glx.ARB_create_context && _sapp.glx.ARB_create_context_profile)) { - _sapp_fail("GLX: ARB_create_context and ARB_create_context_profile required"); + _SAPP_PANIC(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING); } _sapp_x11_grab_error_handler(); const int attribs[] = { @@ -10536,12 +9650,12 @@ _SOKOL_PRIVATE void _sapp_glx_create_context(void) { }; _sapp.glx.ctx = _sapp.glx.CreateContextAttribsARB(_sapp.x11.display, native, NULL, True, attribs); if (!_sapp.glx.ctx) { - _sapp_fail("GLX: failed to create GL context"); + _SAPP_PANIC(LINUX_GLX_CREATE_CONTEXT_FAILED); } _sapp_x11_release_error_handler(); _sapp.glx.window = _sapp.glx.CreateWindow(_sapp.x11.display, native, _sapp.x11.window, NULL); if (!_sapp.glx.window) { - _sapp_fail("GLX: failed to create window"); + _SAPP_PANIC(LINUX_GLX_CREATE_WINDOW_FAILED); } } @@ -10846,7 +9960,7 @@ _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { &wa); _sapp_x11_release_error_handler(); if (!_sapp.x11.window) { - _sapp_fail("X11: Failed to create window"); + _SAPP_PANIC(LINUX_X11_CREATE_WINDOW_FAILED); } Atom protocols[] = { _sapp.x11.WM_DELETE_WINDOW @@ -11009,11 +10123,14 @@ _SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) } } -_SOKOL_PRIVATE void _sapp_x11_mouse_update(int x, int y) { +_SOKOL_PRIVATE void _sapp_x11_mouse_update(int x, int y, bool clear_dxdy) { if (!_sapp.mouse.locked) { const float new_x = (float) x; const float new_y = (float) y; - if (_sapp.mouse.pos_valid) { + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } else if (_sapp.mouse.pos_valid) { _sapp.mouse.dx = new_x - _sapp.mouse.x; _sapp.mouse.dy = new_y - _sapp.mouse.y; } @@ -11272,7 +10389,7 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { ((src_count == 6) && (src_chr != '/')) || ((src_count == 7) && (src_chr != '/'))) { - SAPP_LOG("sokol_app.h: dropped file URI doesn't start with file://"); + _SAPP_ERROR(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME); err = true; break; } @@ -11305,7 +10422,7 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { *dst_ptr++ = dst_chr; } else { - SAPP_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); err = true; break; } @@ -11352,7 +10469,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { } break; case FocusIn: - // NOTE: ingnoring NotifyGrab and NotifyUngrab is same behaviour as GLFW + // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { _sapp_x11_app_event(SAPP_EVENTTYPE_FOCUSED); } @@ -11362,7 +10479,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { if (_sapp.mouse.locked) { _sapp_x11_lock_mouse(false); } - // NOTE: ingnoring NotifyGrab and NotifyUngrab is same behaviour as GLFW + // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { _sapp_x11_app_event(SAPP_EVENTTYPE_UNFOCUSED); } @@ -11402,7 +10519,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { break; case ButtonPress: { - _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y); + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); const sapp_mousebutton btn = _sapp_x11_translate_button(event); uint32_t mods = _sapp_x11_mods(event->xbutton.state); // X11 doesn't set modifier bit on button down, so emulate that @@ -11424,7 +10541,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { break; case ButtonRelease: { - _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y); + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); const sapp_mousebutton btn = _sapp_x11_translate_button(event); if (btn != SAPP_MOUSEBUTTON_INVALID) { uint32_t mods = _sapp_x11_mods(event->xbutton.state); @@ -11438,19 +10555,19 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { case EnterNotify: /* don't send enter/leave events while mouse button held down */ if (0 == _sapp.x11.mouse_buttons) { - _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y); + _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true); _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); } break; case LeaveNotify: if (0 == _sapp.x11.mouse_buttons) { - _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y); + _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true); _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); } break; case MotionNotify: if (!_sapp.mouse.locked) { - _sapp_x11_mouse_update(event->xmotion.x, event->xmotion.y); + _sapp_x11_mouse_update(event->xmotion.x, event->xmotion.y, false); _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); } break; @@ -11581,7 +10698,12 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { (unsigned char**) &data); if (_sapp.drop.enabled && result) { if (_sapp_x11_parse_dropped_files_list(data)) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; if (_sapp_events_enabled()) { + // FIXME: Figure out how to get modifier key state here. + // The XSelection event has no 'state' item, and + // XQueryKeymap() always returns a zeroed array. _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); _sapp_call_event(&_sapp.event); } @@ -11612,22 +10734,22 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { _SOKOL_PRIVATE void _sapp_egl_init(void) { #if defined(SOKOL_GLCORE33) if (!eglBindAPI(EGL_OPENGL_API)) { - _sapp_fail("EGL: failed to bind API"); + _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_API_FAILED); } #else if (!eglBindAPI(EGL_OPENGL_ES_API)) { - _sapp_fail("EGL: failed to bind API"); + _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_ES_API_FAILED); } #endif _sapp.egl.display = eglGetDisplay((EGLNativeDisplayType)_sapp.x11.display); if (EGL_NO_DISPLAY == _sapp.egl.display) { - _sapp_fail("EGL: failed to get display"); + _SAPP_PANIC(LINUX_EGL_GET_DISPLAY_FAILED); } EGLint major, minor; if (!eglInitialize(_sapp.egl.display, &major, &minor)) { - _sapp_fail("EGL: failed to initialize"); + _SAPP_PANIC(LINUX_EGL_INITIALIZE_FAILED); } EGLint sample_count = _sapp.desc.sample_count > 1 ? _sapp.desc.sample_count : 0; @@ -11637,9 +10759,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { #if defined(SOKOL_GLCORE33) EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, #elif defined(SOKOL_GLES3) - EGL_RENDERABLE_TYPE, _sapp.desc.gl_force_gles2 ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES3_BIT, - #else - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, #endif EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, @@ -11655,7 +10775,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { EGLConfig egl_configs[32]; EGLint config_count; if (!eglChooseConfig(_sapp.egl.display, config_attrs, egl_configs, 32, &config_count) || config_count == 0) { - _sapp_fail("EGL: no available configs"); + _SAPP_PANIC(LINUX_EGL_NO_CONFIGS); } EGLConfig config = egl_configs[0]; @@ -11677,7 +10797,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { EGLint visual_id; if (!eglGetConfigAttrib(_sapp.egl.display, config, EGL_NATIVE_VISUAL_ID, &visual_id)) { - _sapp_fail("EGL: failed to get native visual"); + _SAPP_PANIC(LINUX_EGL_NO_NATIVE_VISUAL); } XVisualInfo visual_info_template; @@ -11687,7 +10807,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { int num_visuals; XVisualInfo* visual_info = XGetVisualInfo(_sapp.x11.display, VisualIDMask, &visual_info_template, &num_visuals); if (!visual_info) { - _sapp_fail("EGL: failed to get x11 visual"); + _SAPP_PANIC(LINUX_EGL_GET_VISUAL_INFO_FAILED); } _sapp_x11_create_window(visual_info->visual, visual_info->depth); @@ -11695,7 +10815,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { _sapp.egl.surface = eglCreateWindowSurface(_sapp.egl.display, config, (EGLNativeWindowType)_sapp.x11.window, NULL); if (EGL_NO_SURFACE == _sapp.egl.surface) { - _sapp_fail("EGL: failed to create EGL surface"); + _SAPP_PANIC(LINUX_EGL_CREATE_WINDOW_SURFACE_FAILED); } EGLint ctx_attrs[] = { @@ -11704,27 +10824,21 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl_minor_version, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, #elif defined(SOKOL_GLES3) - EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3, - #else - EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_CONTEXT_CLIENT_VERSION, 3, #endif EGL_NONE, }; _sapp.egl.context = eglCreateContext(_sapp.egl.display, config, EGL_NO_CONTEXT, ctx_attrs); if (EGL_NO_CONTEXT == _sapp.egl.context) { - _sapp_fail("EGL: failed to create GL context"); + _SAPP_PANIC(LINUX_EGL_CREATE_CONTEXT_FAILED); } if (!eglMakeCurrent(_sapp.egl.display, _sapp.egl.surface, _sapp.egl.surface, _sapp.egl.context)) { - _sapp_fail("EGL: failed to set current context"); + _SAPP_PANIC(LINUX_EGL_MAKE_CURRENT_FAILED); } eglSwapInterval(_sapp.egl.display, _sapp.swap_interval); - -#if defined(SOKOL_GLES3) - _sapp.gles2_fallback = _sapp.desc.gl_force_gles2; -#endif } _SOKOL_PRIVATE void _sapp_egl_destroy(void) { @@ -11764,7 +10878,7 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { XrmInitialize(); _sapp.x11.display = XOpenDisplay(NULL); if (!_sapp.x11.display) { - _sapp_fail("XOpenDisplay() failed!\n"); + _SAPP_PANIC(LINUX_X11_OPEN_DISPLAY_FAILED); } _sapp.x11.screen = DefaultScreen(_sapp.x11.display); _sapp.x11.root = DefaultRootWindow(_sapp.x11.display); @@ -11838,7 +10952,13 @@ int main(int argc, char* argv[]) { #endif /* SOKOL_NO_ENTRY */ #endif /* _SAPP_LINUX */ -/*== PUBLIC API FUNCTIONS ====================================================*/ +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public #if defined(SOKOL_NO_ENTRY) SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { SOKOL_ASSERT(desc); @@ -11850,13 +10970,10 @@ SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { _sapp_emsc_run(desc); #elif defined(_SAPP_WIN32) _sapp_win32_run(desc); - #elif defined(_SAPP_UWP) - _sapp_uwp_run(desc); #elif defined(_SAPP_LINUX) _sapp_linux_run(desc); #else - // calling sapp_run() directly is not supported on Android) - _sapp_fail("sapp_run() not supported on this platform!"); + #error "sapp_run() not supported on this platform" #endif } @@ -11930,11 +11047,7 @@ SOKOL_API_IMPL int sapp_color_format(void) { } SOKOL_API_IMPL int sapp_depth_format(void) { - #ifdef SOKOL_GLES2 - return _SAPP_PIXELFORMAT_DEPTH; - #else return _SAPP_PIXELFORMAT_DEPTH_STENCIL; - #endif } SOKOL_API_IMPL int sapp_sample_count(void) { @@ -11949,7 +11062,7 @@ SOKOL_API_IMPL float sapp_dpi_scale(void) { return _sapp.dpi_scale; } -SOKOL_APP_IMPL const void* sapp_egl_get_display(void) { +SOKOL_API_IMPL const void* sapp_egl_get_display(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_ANDROID) return _sapp.android.display; @@ -11960,7 +11073,7 @@ SOKOL_APP_IMPL const void* sapp_egl_get_display(void) { #endif } -SOKOL_APP_IMPL const void* sapp_egl_get_context(void) { +SOKOL_API_IMPL const void* sapp_egl_get_context(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_ANDROID) return _sapp.android.context; @@ -11971,10 +11084,6 @@ SOKOL_APP_IMPL const void* sapp_egl_get_context(void) { #endif } -SOKOL_API_IMPL bool sapp_gles2(void) { - return _sapp.gles2_fallback; -} - SOKOL_API_IMPL void sapp_show_keyboard(bool show) { #if defined(_SAPP_IOS) _sapp_ios_show_keyboard(show); @@ -12000,8 +11109,6 @@ SOKOL_API_IMPL void sapp_toggle_fullscreen(void) { _sapp_macos_toggle_fullscreen(); #elif defined(_SAPP_WIN32) _sapp_win32_toggle_fullscreen(); - #elif defined(_SAPP_UWP) - _sapp_uwp_toggle_fullscreen(); #elif defined(_SAPP_LINUX) _sapp_x11_toggle_fullscreen(); #endif @@ -12016,8 +11123,6 @@ SOKOL_API_IMPL void sapp_show_mouse(bool show) { _sapp_win32_update_cursor(_sapp.mouse.current_cursor, show, false); #elif defined(_SAPP_LINUX) _sapp_x11_update_cursor(_sapp.mouse.current_cursor, show); - #elif defined(_SAPP_UWP) - _sapp_uwp_update_cursor(_sapp.mouse.current_cursor, show); #elif defined(_SAPP_EMSCRIPTEN) _sapp_emsc_update_cursor(_sapp.mouse.current_cursor, show); #endif @@ -12056,8 +11161,6 @@ SOKOL_API_IMPL void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) { _sapp_win32_update_cursor(cursor, _sapp.mouse.shown, false); #elif defined(_SAPP_LINUX) _sapp_x11_update_cursor(cursor, _sapp.mouse.shown); - #elif defined(_SAPP_UWP) - _sapp_uwp_update_cursor(cursor, _sapp.mouse.shown); #elif defined(_SAPP_EMSCRIPTEN) _sapp_emsc_update_cursor(cursor, _sapp.mouse.shown); #endif diff --git a/thirdparty/sokol_gfx.h b/thirdparty/sokol_gfx.h index c75b9c9..19a29d6 100644 --- a/thirdparty/sokol_gfx.h +++ b/thirdparty/sokol_gfx.h @@ -18,7 +18,6 @@ In the same place define one of the following to select the rendering backend: #define SOKOL_GLCORE33 - #define SOKOL_GLES2 #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL @@ -89,11 +88,36 @@ sg_setup(const sg_desc*) + Depending on the selected 3D backend, sokol-gfx requires some + information, like a device pointer framebuffer pixel formats + and so on. If you are using sokol_app.h for the window system + glue, you can use a helper function provided in the sokol_glue.h + header: + + #include "sokol_gfx.h" + #include "sokol_app.h" + #include "sokol_glue.h" + //... + sg_setup(&(sg_desc){ + .context = sapp_sgcontext(), + }); + + To get any logging output for errors and from the validation layer, you + need to provide a logging callback. Easiest way is through sokol_log.h: + + #include "sokol_log.h" + //... + sg_setup(&(sg_desc){ + //... + .logger.func = slog_func, + }); + --- create resource objects (at least buffers, shaders and pipelines, - and optionally images and passes): + and optionally images, samplers and passes): 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*) @@ -117,8 +141,8 @@ 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 - to use as textures each on the vertex-shader- and fragment-shader-stage + 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 sg_apply_bindings(const sg_bindings* bindings) @@ -162,6 +186,7 @@ sg_destroy_buffer(sg_buffer buf) sg_destroy_image(sg_image img) + sg_destroy_sampler(sg_sampler smp) sg_destroy_shader(sg_shader shd) sg_destroy_pipeline(sg_pipeline pip) sg_destroy_pass(sg_pass pass) @@ -170,7 +195,7 @@ sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) - ...or if you want to specifiy the viewport rectangle with float values: + ...or if you want to specify the viewport rectangle with float values: sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) @@ -277,27 +302,32 @@ by calling sg_query_desc(). This will return an sg_desc struct with the default values patched in instead of any zero-initialized values - --- you can inspect various internal resource attributes via: + --- you can get a desc struct matching the creation attributes of a + specific resource object via: - sg_buffer_info sg_query_buffer_info(sg_buffer buf) - sg_image_info sg_query_image_info(sg_image img) - 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) - - ...please note that the returned info-structs are tied quite closely - to sokol_gfx.h internals, and may change more often than other - public API functions and structs. + sg_buffer_desc sg_query_buffer_desc(sg_buffer buf) + sg_image_desc sg_query_image_desc(sg_image img) + sg_sampler_desc sg_query_sampler_desc(sg_sampler smp) + sg_shader_desc sq_query_shader_desc(sg_shader shd) + sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip) + sg_pass_desc sg_query_pass_desc(sg_pass pass) - --- you can ask at runtime what backend sokol_gfx.h has been compiled - for, or whether the GLES3 backend had to fall back to GLES2 with: + ...but NOTE that the returned desc structs may be incomplete, only + creation attributes that are kept around internally after resource + creation will be filled in, and in some cases (like shaders) that's + very little. Any missing attributes will be set to zero. The returned + desc structs might still be useful as partial blueprint for creating + similar resources if filled up with the missing attributes. - sg_backend sg_query_backend(void) + Calling the query-desc functions on an invalid resource will return + completely zeroed structs (it makes sense to check the resource state + with sg_query_*_state() first) --- you can query the default resource creation parameters through the functions sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) + sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc) sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) @@ -307,6 +337,23 @@ will be replaced with their concrete values in the returned desc struct. + --- you can inspect various internal resource runtime values via: + + sg_buffer_info sg_query_buffer_info(sg_buffer buf) + sg_image_info sg_query_image_info(sg_image img) + sg_sampler_info sg_query_sampler_info(sg_sampler smp) + sg_shader_info sg_query_shader_info(sg_shader shd) + sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip) + sg_pass_info sg_query_pass_info(sg_pass pass) + + ...please note that the returned info-structs are tied quite closely + to sokol_gfx.h internals, and may change more often than other + public API functions and structs. + + --- you can ask at runtime what backend sokol_gfx.h has been compiled for: + + sg_backend sg_query_backend(void) + ON INITIALIZATION: ================== @@ -324,8 +371,6 @@ per uniform update (this worst-case alignment is 256 bytes) - the max size of all dynamic resource updates (sg_update_buffer, sg_append_buffer and sg_update_image) per frame - - the max number of entries in the texture sampler cache - (how many unique texture sampler can exist at the same time) Not all of those limit values are used by all backends, but it is good practice to provide them none-the-less. @@ -341,7 +386,309 @@ a convenience function to get a sg_context_desc struct filled out with context information provided by sokol_app.h - See the documention block of the sg_desc struct below for more information. + See the documentation block of the sg_desc struct below for more information. + + + ON RENDER PASSES + ================ + Relevant samples: + - https://floooh.github.io/sokol-html5/offscreen-sapp.html + - https://floooh.github.io/sokol-html5/offscreen-msaa-sapp.html + - https://floooh.github.io/sokol-html5/mrt-sapp.html + - https://floooh.github.io/sokol-html5/mrt-pixelformats-sapp.html + + A render pass wraps rendering commands into a common set of render target images + (called 'pass attachments'). Render target images can be used in subsequent + passes as textures (it is invalid to use the same image both as render target + and as texture in the same pass). + + The following sokol-gfx functions must be called inside a render pass: + + sg_apply_viewport(f) + sg_apply_scissor_rect(f) + sg_apply_pipeline + sg_apply_bindings + sg_apply_uniforms + sg_draw + + A frame must have at least one 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. + + 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). + + For a simple offscreen scenario with one color-, one depth-stencil-render + target and without multisampling, creating a pass object looks like this: + + First create two render target images, one with a color pixel format, + and one with the depth- or depth-stencil pixel format. Both images + must have the same dimensions: + + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 1, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_DEPTH, + .sample_count = 1, + }); + + NOTE: when creating render target images, have in mind that some default values + are aligned with the default framebuffer attributes, this is sometimes not + what you want: + + - 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 + + Next create a pass object: + + const sg_pass pass = sg_make_pass(&(sg_pass_desc){ + .color_attachments[0].image = color_img, + .depth_stencil_attachment.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). + + A typical sg_pass_action object which clears the color attachment to black + might look like this: + + const sg_pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + } + }; + + This omits the defaults for the color attachment store action, and + the depth-stencil-attachments actions. The same pass action with the + defaults explicitly filled in would look like this: + + const sg_pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_STORE, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + }, + .depth = = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = 1.0f, + }, + .stencil = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = 0 + } + }; + + With the sg_pass object and sg_pass_action struct in place everything + is ready now for the actual render pass: + + sg_begin_pass(pass, &pass_action); + ... + sg_end_pass(); + + Offscreen rendering can also go into a mipmap, or a slice/face of + a cube-, array- or 3d-image (which some restrictions, for instance + it's not possible to create a 3D image with a depth/stencil pixel format, + these exceptions are generally caught by the sokol-gfx validation layer). + + The mipmap/slice selection happens at pass 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] = { + .image = color_img, + .mip_level = 2, + .slice = 3, + }, + .depth_stencil_attachment.image = depth_img, + }); + + If MSAA offscreen rendering is desired, the multi-sample rendering result + must be 'resolved' into a separate 'resolve image', before that image can + be used as texture. + + NOTE: currently multisample-images cannot be bound as textures. + + Creating a simple pass object for multisampled rendering requires + 3 attachment images: the color attachment image which has a sample + count > 1, a resolve attachment image of the same size and pixel format + but a sample count == 1, and a depth/stencil attachment image with + the same size and sample count as the color attachment image: + + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 1, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_DEPTH, + .sample_count = 4, + }); + + ...create the pass 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, + }); + + If a pass object defines a resolve image in a specific resolve attachment slot, + an 'msaa resolve operation' will happen in sg_end_pass(). + + In this scenario, the content of the MSAA color attachment doesn't need to be + preserved (since it's only needed inside sg_end_pass for the msaa-resolve), so + the .store_action should be set to "don't care": + + const sg_pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + } + }; + + The actual render pass looks as usual: + + sg_begin_pass(pass, &pass_action); + ... + sg_end_pass(); + + ...after sg_end_pass() the only difference to the non-msaa scenario is that the + rendering result which is going to be used as texture in a followup pass is + in 'resolve_img', not in 'color_img' (in fact, trying to bind color_img as a + texture would result in a validation error). + + + ON SHADER CREATION + ================== + sokol-gfx doesn't come with an integrated shader cross-compiler, instead + backend-specific shader sources or binary blobs need to be provided when + creating a shader object, along with information about the shader interface + needed in the sokol-gfx validation layer and to properly bind shader resources + on the CPU-side to be consumable by the GPU-side. + + The easiest way to provide all this shader creation data is to use the + sokol-shdc shader compiler tool to compile shaders from a common + GLSL syntax into backend-specific sources or binary blobs, along with + shader interface information and uniform blocks mapped to C structs. + + To create a shader using a C header which has been code-generated by sokol-shdc: + + // include the C header code-generated by sokol-shdc: + #include "myshader.glsl.h" + ... + + // create shader using a code-generated helper function from the C header: + sg_shader shd = sg_make_shader(myshader_shader_desc(sg_query_backend())); + + The samples in the 'sapp' subdirectory of the sokol-samples project + also use the sokol-shdc approach: + + https://github.com/floooh/sokol-samples/tree/master/sapp + + If you're planning to use sokol-shdc, you can stop reading here, instead + continue with the sokol-shdc documentation: + + https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md + + To create shaders with backend-specific shader code or binary blobs, + the sg_make_shader() function requires the following information: + + - Shader code or shader binary blobs for the vertex- and fragment- shader-stage: + - for the desktop GL backend, source code must be provided in '#version 330' syntax + - for the GLES3 backend, source code must be provided in '#version 300 es' syntax + - for the D3D11 backend, shaders can be provided as source or binary blobs, the + source code should be in HLSL4.0 (for best compatibility) or alternatively + in HLSL5.0 syntax (other versions may work but are not tested), NOTE: when + shader source code is provided for the D3D11 backend, sokol-gfx will dynamically + load 'd3dcompiler_47.dll' + - for the Metal backends, shaders can be provided as source or binary blobs, the + MSL version should be in 'metal-1.1' (other versions may work but are not tested) + - optionally the following shader-code related attributes can be provided: + - an entry function name (only on D3D11 or Metal, but not OpenGL) + - on D3D11 only, a compilation target (default is "vs_4_0" and "ps_4_0") + + - Depending on backend, information about the input vertex attributes used by the + vertex shader: + - Metal: no information needed since vertex attributes are always bound + by their attribute location defined in the shader via '[[attribute(N)]]' + - GLSL: vertex attribute names can be optionally provided, in that case their + location will be looked up by name, otherwise, the vertex attribute location + can be defined with 'layout(location = N)', PLEASE NOTE that the name-lookup method + may be removed at some point + - D3D11: a 'semantic name' and 'semantic index' must be provided for each vertex + attribute, e.g. if the vertex attribute is defined as 'TEXCOORD1' in the shader, + the semantic name would be 'TEXCOORD', and the semantic index would be '1' + + - Information about each uniform block used in the shader: + - The size of the uniform block in number of bytes. + - A memory layout hint (currently 'native' or 'std140') where 'native' defines a + backend-specific memory layout which shouldn't be used for cross-platform code. + Only std140 guarantees a backend-agnostic memory layout. + - For GLSL only: a description of the internal uniform block layout, which maps + member types and their offsets on the CPU side to uniform variable names + in the GLSL shader + - please also NOTE the documentation sections about UNIFORM DATA LAYOUT + and CROSS-BACKEND COMMON UNIFORM DATA LAYOUT below! + + - A description of each texture/image used in the shader: + - the expected image type (e.g. 2D, 3D, etc...) + - the 'image sample type' (e.g. float, depth, signed- or unsigned-int) + - a flag whether the texture is expected to be multisampled + (currently it's not supported to fetch data from multisampled + textures in shaders, but this is planned for a later time) + + - A description of each sampler used in the shader: + - just wether the sampler is a regular 'sampling sampler', + or a 'comparison sampler' (which is usually used for + shadow mapping) + + - An array of 'image-sampler-pairs' used by the shader to sample textures, + for D3D11 and Metal this is only used for validation purposes to check + whether the texture and sampler are compatible with each other. 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'. + + For example code of how to create backend-specific shader objects, + please refer to the following samples: + + - for D3D11: https://github.com/floooh/sokol-samples/tree/master/d3d11 + - for Metal: https://github.com/floooh/sokol-samples/tree/master/metal + - for OpenGL: https://github.com/floooh/sokol-samples/tree/master/glfw + - for GLES3: https://github.com/floooh/sokol-samples/tree/master/html5 UNIFORM DATA LAYOUT: @@ -421,7 +768,7 @@ layout on the CPU side can be used for all sokol-gfx backends. To achieve this, a common subset of the std140 layout must be used: - - The uniform block layout hint in sg_shader_desc must be explicitely set to + - The uniform block layout hint in sg_shader_desc must be explicitly set to SG_UNIFORMLAYOUT_STD140. - Only the following GLSL uniform types can be used (with their associated sokol-gfx enums): - float => SG_UNIFORMTYPE_FLOAT @@ -449,98 +796,10 @@ std140 layout rules, this means that the cbuffer declarations in HLSL code must be tweaked so that the layout is compatible with std140. - The by far easiest way to tacke the common uniform block layout problem is + The by far easiest way to tackle the common uniform block layout problem is to use the sokol-shdc shader cross-compiler tool! - BACKEND-SPECIFIC TOPICS: - ======================== - --- The GL backends need to know about the internal structure of uniform - blocks, and the texture sampler-name and -type. The uniform layout details - are described in the UNIFORM DATA LAYOUT section above. - - // uniform block structure and texture image definition in sg_shader_desc: - sg_shader_desc desc = { - // uniform block description (size and internal structure) - .vs.uniform_blocks[0] = { - ... - }, - // one texture on the fragment-shader-stage, GLES2/WebGL needs name and image type - .fs.images[0] = { .name="tex", .type=SG_IMAGETYPE_ARRAY } - ... - }; - - --- the Metal and D3D11 backends only need to know the size of uniform blocks, - not their internal member structure, and they only need to know - the type of a texture sampler, not its name: - - sg_shader_desc desc = { - .vs.uniform_blocks[0].size = sizeof(params_t), - .fs.images[0].type = SG_IMAGETYPE_ARRAY, - ... - }; - - --- when creating a shader object, GLES2/WebGL need to know the vertex - attribute names as used in the vertex shader: - - sg_shader_desc desc = { - .attrs = { - [0] = { .name="position" }, - [1] = { .name="color1" } - } - }; - - The vertex attribute names provided when creating a shader will be - used later in sg_create_pipeline() for matching the vertex layout - to vertex shader inputs. - - --- on D3D11 you need to provide a semantic name and semantic index in the - shader description struct instead (see the D3D11 documentation on - D3D11_INPUT_ELEMENT_DESC for details): - - sg_shader_desc desc = { - .attrs = { - [0] = { .sem_name="POSITION", .sem_index=0 } - [1] = { .sem_name="COLOR", .sem_index=1 } - } - }; - - The provided semantic information will be used later in sg_create_pipeline() - to match the vertex layout to vertex shader inputs. - - --- on D3D11, and when passing HLSL source code (instead of byte code) to shader - creation, you can optionally define the shader model targets on the vertex - stage: - - sg_shader_Desc desc = { - .vs = { - ... - .d3d11_target = "vs_5_0" - }, - .fs = { - ... - .d3d11_target = "ps_5_0" - } - }; - - The default targets are "ps_4_0" and "fs_4_0". Note that those target names - are only used when compiling shaders from source. They are ignored when - creating a shader from bytecode. - - --- on Metal, GL 3.3 or GLES3/WebGL2, you don't need to provide an attribute - name or semantic name, since vertex attributes can be bound by their slot index - (this is mandatory in Metal, and optional in GL): - - sg_pipeline_desc desc = { - .layout = { - .attrs = { - [0] = { .format=SG_VERTEXFORMAT_FLOAT3 }, - [1] = { .format=SG_VERTEXFORMAT_FLOAT4 } - } - } - }; - - WORKING WITH CONTEXTS ===================== sokol-gfx allows to switch between different rendering contexts and @@ -643,13 +902,7 @@ - SG_VERTEXFORMAT_SHORT2 - SG_VERTEXFORMAT_SHORT4 - - WebGL/GLES2 cannot use integer vertex shader inputs (int or ivecn) or the following: - - - SG_VERTEXFORMAT_UINT10_N2 - - SG_VERTEXFORMAT_HALF2, SG_VERTEXFORMAT_HALF4 - (commonly supported extension: OES_vertex_half_float) - - So for a vertex input layout which works on all platforms, only use the following + For a vertex input layout which works on all platforms, only use the following vertex formats, and if needed "expand" the normalized vertex shader inputs in the vertex shader by multiplying with 127.0, 255.0, 32767.0 or 65535.0: @@ -664,6 +917,9 @@ - SG_VERTEXFORMAT_USHORT2N - SG_VERTEXFORMAT_SHORT4N, - SG_VERTEXFORMAT_USHORT4N + - SG_VERTEXFORMAT_UINT10_N2 + - SG_VERTEXFORMAT_HALF2 + - SG_VERTEXFORMAT_HALF4 MEMORY ALLOCATION OVERRIDE @@ -696,26 +952,43 @@ itself though, not any allocations in OS libraries. - LOG FUNCTION OVERRIDE - ===================== - You can override the log function at initialization time like this: + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sg_setup(&(sg_desc){ .logger.func = slog_func }); + + To override logging with your own callback, first write a logging function like this: - void my_log(const char* message, void* user_data) { - printf("sg says: \s\n", message); + void my_log(const char* tag, // e.g. 'sg' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SG_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gfx.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... } - ... - sg_setup(&(sg_desc){ - // ... - .logger = { - .log_cb = my_log, - .user_data = ..., - } - }); - ... + ...and then setup sokol-gfx like this: + + sg_setup(&(sg_desc){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). - If no overrides are provided, puts will be used on most platforms. - On Android, __android_log_write will be used instead. + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. COMMIT LISTENERS @@ -769,6 +1042,7 @@ sg_buffer sg_make_buffer(const sg_buffer_desc* desc) sg_image sg_make_image(const sg_image_desc* desc) + sg_sampler sg_make_sampler(const sg_sampler_desc* desc) sg_shader sg_make_shader(const sg_shader_desc* desc) sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) sg_pass sg_make_pass(const sg_pass_desc* desc) @@ -826,6 +1100,7 @@ sg_buffer sg_alloc_buffer(void) sg_image sg_alloc_image(void) + sg_sampler sg_alloc_sampler(void) sg_shader sg_alloc_shader(void) sg_pipeline sg_alloc_pipeline(void) sg_pass sg_alloc_pass(void) @@ -850,6 +1125,7 @@ void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc) void sg_init_image(sg_image img, const sg_image_desc* desc) + void sg_init_sampler(sg_sampler smp, const sg_sampler_desc* desc) void sg_init_shader(sg_shader shd, const sg_shader_desc* desc) void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc) void sg_init_pass(sg_pass pass, const sg_pass_desc* desc) @@ -865,6 +1141,7 @@ void sg_uninit_buffer(sg_buffer buf) void sg_uninit_image(sg_image img) + void sg_uninit_sampler(sg_sampler smp) void sg_uninit_shader(sg_shader shd) void sg_uninit_pipeline(sg_pipeline pip) void sg_uninit_pass(sg_pass pass) @@ -876,6 +1153,7 @@ void sg_dealloc_buffer(sg_buffer buf) void sg_dealloc_image(sg_image img) + void sg_dealloc_sampler(sg_sampler smp) void sg_dealloc_shader(sg_shader shd) void sg_dealloc_pipeline(sg_pipeline pip) void sg_dealloc_pass(sg_pass pass) @@ -888,6 +1166,7 @@ void sg_destroy_buffer(sg_buffer buf) void sg_destroy_image(sg_image img) + void sg_destroy_sampler(sg_sampler smp) void sg_destroy_shader(sg_shader shd) void sg_destroy_pipeline(sg_pipeline pip) void sg_destroy_pass(sg_pass pass) @@ -901,23 +1180,24 @@ sg_fail_buffer(sg_buffer buf) sg_fail_image(sg_image img) + sg_fail_sampler(sg_sampler smp) sg_fail_shader(sg_shader shd) sg_fail_pipeline(sg_pipeline pip) sg_fail_pass(sg_pass pass) This is recommended if anything went wrong outside of sokol-gfx during asynchronous - resource creation (for instance the file loading operation failed). In this case, + resource creation (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 shaders, pipelines or passes. Most notably, trying + and images, but not for samplers, shaders, pipelines or passes. 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 image invalid image objects. + a pass object with invalid image objects. LICENSE ======= @@ -970,7 +1250,8 @@ extern "C" { Resource id typedefs: sg_buffer: vertex- and index-buffers - sg_image: textures and render targets + sg_image: images used as textures and render targets + sg_sampler sampler object describing how a texture is sampled in a shader sg_shader: vertex- and fragment-shaders, uniform blocks sg_pipeline: associated shader and vertex-layouts, and render states sg_pass: a bundle of render targets and actions on them @@ -990,6 +1271,7 @@ extern "C" { */ 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; @@ -1009,8 +1291,8 @@ typedef struct sg_range { // disabling this for every includer isn't great, but the warnings are also quite pointless #if defined(_MSC_VER) -#pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */ -#pragma warning(disable:4204) /* VS2015: nonstandard extension used: non-constant aggregate initializer */ +#pragma warning(disable:4221) // /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' +#pragma warning(disable:4204) // VS2015: nonstandard extension used: non-constant aggregate initializer #endif #if defined(__cplusplus) #define SG_RANGE(x) sg_range{ &x, sizeof(x) } @@ -1026,11 +1308,13 @@ enum { SG_NUM_SHADER_STAGES = 2, SG_NUM_INFLIGHT_FRAMES = 2, SG_MAX_COLOR_ATTACHMENTS = 4, - SG_MAX_SHADERSTAGE_BUFFERS = 8, + SG_MAX_VERTEX_BUFFERS = 8, SG_MAX_SHADERSTAGE_IMAGES = 12, + SG_MAX_SHADERSTAGE_SAMPLERS = 8, + SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS = 12, SG_MAX_SHADERSTAGE_UBS = 4, SG_MAX_UB_MEMBERS = 16, - SG_MAX_VERTEX_ATTRIBUTES = 16, /* NOTE: actual max vertex attrs can be less on GLES2, see sg_limits! */ + SG_MAX_VERTEX_ATTRIBUTES = 16, SG_MAX_MIPMAPS = 16, SG_MAX_TEXTUREARRAY_LAYERS = 128 }; @@ -1047,14 +1331,9 @@ typedef struct sg_color { float r, g, b, a; } sg_color; The active 3D-API backend, use the function sg_query_backend() to get the currently active backend. - - NOTE that SG_BACKEND_GLES2 will be returned if sokol-gfx was - compiled with SOKOL_GLES3, but the runtime platform doesn't support - GLES3/WebGL2 and sokol-gfx had to fallback to GLES2/WebGL. */ typedef enum sg_backend { SG_BACKEND_GLCORE33, - SG_BACKEND_GLES2, SG_BACKEND_GLES3, SG_BACKEND_D3D11, SG_BACKEND_METAL_IOS, @@ -1068,10 +1347,7 @@ typedef enum sg_backend { sg_pixel_format sokol_gfx.h basically uses the same pixel formats as WebGPU, since these - are supported on most newer GPUs. GLES2 and WebGL only supports a much - smaller subset of actually available pixel formats. Call - sg_query_pixelformat() to check at runtime if a pixel format supports the - desired features. + are supported on most newer GPUs. A pixelformat name consist of three parts: @@ -1099,11 +1375,6 @@ typedef enum sg_backend { pixelformat for render targets - depth: the pixelformat can be used for depth-stencil attachments - When targeting GLES2/WebGL, the only safe formats to use - as texture are SG_PIXELFORMAT_R8 and SG_PIXELFORMAT_RGBA8. For rendering - in GLES2/WebGL, only SG_PIXELFORMAT_RGBA8 is safe. All other formats - must be checked via sg_query_pixelformats(). - The default pixel format for texture images is SG_PIXELFORMAT_RGBA8. The default pixel format for render target images is platform-dependent: @@ -1116,7 +1387,7 @@ typedef enum sg_backend { use whatever renderable pixel format is convenient for you. */ typedef enum sg_pixel_format { - _SG_PIXELFORMAT_DEFAULT, /* value 0 reserved for default-init */ + _SG_PIXELFORMAT_DEFAULT, // value 0 reserved for default-init SG_PIXELFORMAT_NONE, SG_PIXELFORMAT_R8, @@ -1198,8 +1469,8 @@ typedef enum sg_pixel_format { by sg_query_pixelformat(). */ typedef struct sg_pixelformat_info { - bool sample; // pixel format can be sampled in shaders - bool filter; // pixel format can be sampled with filtering + 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 @@ -1214,12 +1485,7 @@ typedef struct sg_pixelformat_info { returned by sg_query_features() */ typedef struct sg_features { - bool instancing; // hardware instancing supported bool origin_top_left; // framebuffer and texture origin is in top left corner - bool multiple_render_targets; // offscreen render passes can have multiple render targets attached - bool msaa_render_targets; // offscreen render passes support MSAA antialiasing - bool imagetype_3d; // creation of SG_IMAGETYPE_3D images is supported - bool imagetype_array; // creation of SG_IMAGETYPE_ARRAY images is supported 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 @@ -1237,8 +1503,9 @@ typedef struct sg_limits { int max_image_size_3d; // max width/height/depth of SG_IMAGETYPE_3D images int max_image_size_array; // max width/height of SG_IMAGETYPE_ARRAY images int max_image_array_layers; // max number of layers in SG_IMAGETYPE_ARRAY images - int max_vertex_attrs; // <= SG_MAX_VERTEX_ATTRIBUTES or less (on some GLES2 impls) + 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_combined_texture_image_units; // <= GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS (only on GL backends) } sg_limits; /* @@ -1303,7 +1570,7 @@ typedef enum sg_resource_state { The default usage is SG_USAGE_IMMUTABLE. */ typedef enum sg_usage { - _SG_USAGE_DEFAULT, /* value 0 reserved for default-init */ + _SG_USAGE_DEFAULT, // value 0 reserved for default-init SG_USAGE_IMMUTABLE, SG_USAGE_DYNAMIC, SG_USAGE_STREAM, @@ -1320,7 +1587,7 @@ typedef enum sg_usage { The default value is SG_BUFFERTYPE_VERTEXBUFFER. */ typedef enum sg_buffer_type { - _SG_BUFFERTYPE_DEFAULT, /* value 0 reserved for default-init */ + _SG_BUFFERTYPE_DEFAULT, // value 0 reserved for default-init SG_BUFFERTYPE_VERTEXBUFFER, SG_BUFFERTYPE_INDEXBUFFER, _SG_BUFFERTYPE_NUM, @@ -1338,7 +1605,7 @@ typedef enum sg_buffer_type { The default index type is SG_INDEXTYPE_NONE. */ typedef enum sg_index_type { - _SG_INDEXTYPE_DEFAULT, /* value 0 reserved for default-init */ + _SG_INDEXTYPE_DEFAULT, // value 0 reserved for default-init SG_INDEXTYPE_NONE, SG_INDEXTYPE_UINT16, SG_INDEXTYPE_UINT32, @@ -1350,16 +1617,15 @@ typedef enum sg_index_type { sg_image_type Indicates the basic type of an image object (2D-texture, cubemap, - 3D-texture or 2D-array-texture). 3D- and array-textures are not supported - on the GLES2/WebGL backend (use sg_query_features().imagetype_3d and - sg_query_features().imagetype_array to check for support). The image type - is used in the sg_image_desc.type member when creating an image, and - in sg_shader_image_desc when describing a shader's texture sampler binding. + 3D-texture or 2D-array-texture). Used in the sg_image_desc.type member when + creating an image, and in sg_shader_image_desc to describe a sampled texture + in the shader (both must match and will be checked in the validation layer + when calling sg_apply_bindings). The default image type when creating an image is SG_IMAGETYPE_2D. */ typedef enum sg_image_type { - _SG_IMAGETYPE_DEFAULT, /* value 0 reserved for default-init */ + _SG_IMAGETYPE_DEFAULT, // value 0 reserved for default-init SG_IMAGETYPE_2D, SG_IMAGETYPE_CUBE, SG_IMAGETYPE_3D, @@ -1369,20 +1635,36 @@ typedef enum sg_image_type { } sg_image_type; /* - sg_sampler_type + sg_image_sample_type + + The basic data type of a texture sample as expected by a shader. + Must be provided in sg_shader_image_desc and used by the validation + 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. +*/ +typedef enum sg_image_sample_type { + _SG_IMAGESAMPLETYPE_DEFAULT, // value 0 reserved for default-init + SG_IMAGESAMPLETYPE_FLOAT, + SG_IMAGESAMPLETYPE_DEPTH, + SG_IMAGESAMPLETYPE_SINT, + SG_IMAGESAMPLETYPE_UINT, + _SG_IMAGESAMPLETYPE_NUM, + _SG_IMAGESAMPLETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_image_sample_type; - Indicates the basic data type of a shader's texture sampler which - can be float , unsigned integer or signed integer. The sampler - type is used in the sg_shader_image_desc to describe the - sampler type of a shader's texture sampler binding. +/* + sg_sampler_type - The default sampler type is SG_SAMPLERTYPE_FLOAT. + The basic type of a texture sampler (sampling vs comparison) as + defined in a shader. Must be provided in sg_shader_sampler_desc. */ typedef enum sg_sampler_type { - _SG_SAMPLERTYPE_DEFAULT, /* value 0 reserved for default-init */ - SG_SAMPLERTYPE_FLOAT, - SG_SAMPLERTYPE_SINT, - SG_SAMPLERTYPE_UINT, + _SG_SAMPLERTYPE_DEFAULT, + SG_SAMPLERTYPE_SAMPLE, + SG_SAMPLERTYPE_COMPARE, + _SG_SAMPLERTYPE_NUM, + _SG_SAMPLERTYPE_FORCE_U32, } sg_sampler_type; /* @@ -1429,7 +1711,7 @@ typedef enum sg_shader_stage { The default primitive type is SG_PRIMITIVETYPE_TRIANGLES. */ typedef enum sg_primitive_type { - _SG_PRIMITIVETYPE_DEFAULT, /* value 0 reserved for default-init */ + _SG_PRIMITIVETYPE_DEFAULT, // value 0 reserved for default-init SG_PRIMITIVETYPE_POINTS, SG_PRIMITIVETYPE_LINES, SG_PRIMITIVETYPE_LINE_STRIP, @@ -1443,19 +1725,25 @@ typedef enum sg_primitive_type { sg_filter The filtering mode when sampling a texture image. This is - used in the sg_image_desc.min_filter and sg_image_desc.mag_filter - members when creating an image object. + 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: - The default filter mode is SG_FILTER_NEAREST. + - 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. */ typedef enum sg_filter { - _SG_FILTER_DEFAULT, /* value 0 reserved for default-init */ + _SG_FILTER_DEFAULT, // value 0 reserved for default-init + SG_FILTER_NONE, SG_FILTER_NEAREST, SG_FILTER_LINEAR, - SG_FILTER_NEAREST_MIPMAP_NEAREST, - SG_FILTER_NEAREST_MIPMAP_LINEAR, - SG_FILTER_LINEAR_MIPMAP_NEAREST, - SG_FILTER_LINEAR_MIPMAP_LINEAR, _SG_FILTER_NUM, _SG_FILTER_FORCE_U32 = 0x7FFFFFFF } sg_filter; @@ -1476,20 +1764,9 @@ typedef enum sg_filter { Platforms which don't support SG_WRAP_CLAMP_TO_BORDER will silently fall back to SG_WRAP_CLAMP_TO_EDGE without a validation error. - - Platforms which support clamp-to-border are: - - - all desktop GL platforms - - Metal on macOS - - D3D11 - - Platforms which do not support clamp-to-border: - - - GLES2/3 and WebGL/WebGL2 - - Metal on iOS */ typedef enum sg_wrap { - _SG_WRAP_DEFAULT, /* value 0 reserved for default-init */ + _SG_WRAP_DEFAULT, // value 0 reserved for default-init SG_WRAP_REPEAT, SG_WRAP_CLAMP_TO_EDGE, SG_WRAP_CLAMP_TO_BORDER, @@ -1507,7 +1784,7 @@ typedef enum sg_wrap { The default border color is SG_BORDERCOLOR_OPAQUE_BLACK */ typedef enum sg_border_color { - _SG_BORDERCOLOR_DEFAULT, /* value 0 reserved for default-init */ + _SG_BORDERCOLOR_DEFAULT, // value 0 reserved for default-init SG_BORDERCOLOR_TRANSPARENT_BLACK, SG_BORDERCOLOR_OPAQUE_BLACK, SG_BORDERCOLOR_OPAQUE_WHITE, @@ -1556,7 +1833,7 @@ typedef enum sg_vertex_format { when creating pipeline objects. */ typedef enum sg_vertex_step { - _SG_VERTEXSTEP_DEFAULT, /* value 0 reserved for default-init */ + _SG_VERTEXSTEP_DEFAULT, // value 0 reserved for default-init SG_VERTEXSTEP_PER_VERTEX, SG_VERTEXSTEP_PER_INSTANCE, _SG_VERTEXSTEP_NUM, @@ -1620,9 +1897,9 @@ typedef enum sg_uniform_type { at the start of the header. */ typedef enum sg_uniform_layout { - _SG_UNIFORMLAYOUT_DEFAULT, /* value 0 reserved for default-init */ - SG_UNIFORMLAYOUT_NATIVE, /* default: layout depends on currently active backend */ - SG_UNIFORMLAYOUT_STD140, /* std140: memory layout according to std140 */ + _SG_UNIFORMLAYOUT_DEFAULT, // value 0 reserved for default-init + SG_UNIFORMLAYOUT_NATIVE, // default: layout depends on currently active backend + SG_UNIFORMLAYOUT_STD140, // std140: memory layout according to std140 _SG_UNIFORMLAYOUT_NUM, _SG_UNIFORMLAYOUT_FORCE_U32 = 0x7FFFFFFF } sg_uniform_layout; @@ -1637,7 +1914,7 @@ typedef enum sg_uniform_layout { The default cull mode is SG_CULLMODE_NONE */ typedef enum sg_cull_mode { - _SG_CULLMODE_DEFAULT, /* value 0 reserved for default-init */ + _SG_CULLMODE_DEFAULT, // value 0 reserved for default-init SG_CULLMODE_NONE, SG_CULLMODE_FRONT, SG_CULLMODE_BACK, @@ -1655,7 +1932,7 @@ typedef enum sg_cull_mode { The default winding is SG_FACEWINDING_CW (clockwise) */ typedef enum sg_face_winding { - _SG_FACEWINDING_DEFAULT, /* value 0 reserved for default-init */ + _SG_FACEWINDING_DEFAULT, // value 0 reserved for default-init SG_FACEWINDING_CCW, SG_FACEWINDING_CW, _SG_FACEWINDING_NUM, @@ -1665,8 +1942,9 @@ typedef enum sg_face_winding { /* sg_compare_func - The compare-function for depth- and stencil-ref tests. - This is used when creating pipeline objects in the members: + The compare-function for configuring depth- and stencil-ref tests + in pipeline objects, and for texture samplers which perform a comparison + instead of regular sampling operation. sg_pipeline_desc .depth @@ -1675,11 +1953,16 @@ typedef enum sg_face_winding { .front.compare .back.compar + sg_sampler_desc + .compare + The default compare func for depth- and stencil-tests is SG_COMPAREFUNC_ALWAYS. + + The default compare func for sampler is SG_COMPAREFUNC_NEVER. */ typedef enum sg_compare_func { - _SG_COMPAREFUNC_DEFAULT, /* value 0 reserved for default-init */ + _SG_COMPAREFUNC_DEFAULT, // value 0 reserved for default-init SG_COMPAREFUNC_NEVER, SG_COMPAREFUNC_LESS, SG_COMPAREFUNC_EQUAL, @@ -1713,7 +1996,7 @@ typedef enum sg_compare_func { The default value is SG_STENCILOP_KEEP. */ typedef enum sg_stencil_op { - _SG_STENCILOP_DEFAULT, /* value 0 reserved for default-init */ + _SG_STENCILOP_DEFAULT, // value 0 reserved for default-init SG_STENCILOP_KEEP, SG_STENCILOP_ZERO, SG_STENCILOP_REPLACE, @@ -1744,7 +2027,7 @@ typedef enum sg_stencil_op { factors, and SG_BLENDFACTOR_ZERO for destination factors. */ typedef enum sg_blend_factor { - _SG_BLENDFACTOR_DEFAULT, /* value 0 reserved for default-init */ + _SG_BLENDFACTOR_DEFAULT, // value 0 reserved for default-init SG_BLENDFACTOR_ZERO, SG_BLENDFACTOR_ONE, SG_BLENDFACTOR_SRC_COLOR, @@ -1780,7 +2063,7 @@ typedef enum sg_blend_factor { The default value is SG_BLENDOP_ADD. */ typedef enum sg_blend_op { - _SG_BLENDOP_DEFAULT, /* value 0 reserved for default-init */ + _SG_BLENDOP_DEFAULT, // value 0 reserved for default-init SG_BLENDOP_ADD, SG_BLENDOP_SUBTRACT, SG_BLENDOP_REVERSE_SUBTRACT, @@ -1802,8 +2085,8 @@ typedef enum sg_blend_op { should be disabled. */ typedef enum sg_color_mask { - _SG_COLORMASK_DEFAULT = 0, /* value 0 reserved for default-init */ - SG_COLORMASK_NONE = 0x10, /* special value for 'all channels disabled */ + _SG_COLORMASK_DEFAULT = 0, // value 0 reserved for default-init + SG_COLORMASK_NONE = 0x10, // special value for 'all channels disabled SG_COLORMASK_R = 0x1, SG_COLORMASK_G = 0x2, SG_COLORMASK_RG = 0x3, @@ -1823,64 +2106,75 @@ typedef enum sg_color_mask { } sg_color_mask; /* - sg_action + sg_load_action - Defines what action should be performed at the start of a render pass: + Defines the load action that should be performed at the start of a render pass: - SG_ACTION_CLEAR: clear the render target image - SG_ACTION_LOAD: load the previous content of the render target image - SG_ACTION_DONTCARE: leave the render target image content undefined + SG_LOADACTION_CLEAR: clear the render target + SG_LOADACTION_LOAD: load the previous content of the render target + SG_LOADACTION_DONTCARE: leave the render target in an undefined state This is used in the sg_pass_action structure. - The default action for all pass attachments is SG_ACTION_CLEAR, with the - clear color rgba = {0.5f, 0.5f, 0.5f, 1.0f], depth=1.0 and stencil=0. + The default load action for all pass attachments is SG_LOADACTION_CLEAR, + with the values rgba = { 0.5f, 0.5f, 0.5f, 1.0f }, depth=1.0f and stencil=0. If you want to override the default behaviour, it is important to not only set the clear color, but the 'action' field as well (as long as this - is in its _SG_ACTION_DEFAULT, the value fields will be ignored). + is _SG_LOADACTION_DEFAULT, the value fields will be ignored). */ -typedef enum sg_action { - _SG_ACTION_DEFAULT, - SG_ACTION_CLEAR, - SG_ACTION_LOAD, - SG_ACTION_DONTCARE, - _SG_ACTION_NUM, - _SG_ACTION_FORCE_U32 = 0x7FFFFFFF -} sg_action; +typedef enum sg_load_action { + _SG_LOADACTION_DEFAULT, + SG_LOADACTION_CLEAR, + SG_LOADACTION_LOAD, + SG_LOADACTION_DONTCARE, + _SG_LOADACTION_FORCE_U32 = 0x7FFFFFFF +} sg_load_action; /* - sg_pass_action + sg_store_action + + Defines the store action that be performed at the end of a render pass: + + SG_STOREACTION_STORE: store the rendered content to the color attachment image + SG_STOREACTION_DONTCARE: allows the GPU to discard the rendered content +*/ +typedef enum sg_store_action { + _SG_STOREACTION_DEFAULT, + SG_STOREACTION_STORE, + SG_STOREACTION_DONTCARE, + _SG_STOREACTION_FORCE_U32 = 0x7FFFFFFF +} sg_store_action; - The sg_pass_action struct defines the actions to be performed - at the start of a rendering pass in the functions sg_begin_pass() - and sg_begin_default_pass(). - A separate action and clear values can be defined for each - color attachment, and for the depth-stencil attachment. +/* + sg_pass_action - The default clear values are defined by the macros: + The sg_pass_action struct defines the actions to be performed + at the start of and end of a render pass. - - SG_DEFAULT_CLEAR_RED: 0.5f - - SG_DEFAULT_CLEAR_GREEN: 0.5f - - SG_DEFAULT_CLEAR_BLUE: 0.5f - - SG_DEFAULT_CLEAR_ALPHA: 1.0f - - SG_DEFAULT_CLEAR_DEPTH: 1.0f - - SG_DEFAULT_CLEAR_STENCIL: 0 + - 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 */ typedef struct sg_color_attachment_action { - sg_action action; - sg_color value; + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_STORE + sg_color clear_value; // default: { 0.5f, 0.5f, 0.5f, 1.0f } } sg_color_attachment_action; typedef struct sg_depth_attachment_action { - sg_action action; - float value; + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_DONTCARE + float clear_value; // default: 1.0 } sg_depth_attachment_action; typedef struct sg_stencil_attachment_action { - sg_action action; - uint8_t value; + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_DONTCARE + uint8_t clear_value; // default: 0 } sg_stencil_attachment_action; typedef struct sg_pass_action { @@ -1905,23 +2199,30 @@ typedef struct sg_pass_action { - 0..1 index buffers - 0..1 index buffer offsets - 0..N vertex shader stage images + - 0..N vertex shader stage samplers - 0..N fragment shader stage images + - 0..N fragment shader stage samplers The max number of vertex buffer and shader stage images - are defined by the SG_MAX_SHADERSTAGE_BUFFERS and + are defined by the SG_MAX_VERTEX_BUFFERS and SG_MAX_SHADERSTAGE_IMAGES configuration constants. The optional buffer offsets can be used to put different unrelated chunks of vertex- and/or index-data into the same buffer objects. */ +typedef struct sg_stage_bindings { + sg_image images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_sampler samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; +} sg_stage_bindings; + typedef struct sg_bindings { uint32_t _start_canary; - sg_buffer vertex_buffers[SG_MAX_SHADERSTAGE_BUFFERS]; - int vertex_buffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + sg_buffer vertex_buffers[SG_MAX_VERTEX_BUFFERS]; + int vertex_buffer_offsets[SG_MAX_VERTEX_BUFFERS]; sg_buffer index_buffer; int index_buffer_offset; - sg_image vs_images[SG_MAX_SHADERSTAGE_IMAGES]; - sg_image fs_images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_stage_bindings vs; + sg_stage_bindings fs; uint32_t _end_canary; } sg_bindings; @@ -1985,13 +2286,10 @@ typedef struct sg_buffer_desc { sg_usage usage; sg_range data; const char* label; - /* GL specific */ + // optionally inject backend-specific resources uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES]; - /* Metal specific */ const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES]; - /* D3D11 specific */ const void* d3d11_buffer; - /* WebGPU specific */ const void* wgpu_buffer; uint32_t _end_canary; } sg_buffer_desc; @@ -2010,8 +2308,7 @@ typedef struct sg_image_data { /* sg_image_desc - Creation parameters for sg_image objects, used in the sg_make_image() - call. + Creation parameters for sg_image objects, used in the sg_make_image() call. The default configuration is: @@ -2024,17 +2321,8 @@ typedef struct sg_image_data { .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 - .min_filter: SG_FILTER_NEAREST - .mag_filter: SG_FILTER_NEAREST - .wrap_u: SG_WRAP_REPEAT - .wrap_v: SG_WRAP_REPEAT - .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) - .border_color SG_BORDERCOLOR_OPAQUE_BLACK - .max_anisotropy 1 (must be 1..16) - .min_lod 0.0f - .max_lod FLT_MAX .data an sg_image_data struct to define the initial content - .label 0 (optional string label for trace hooks) + .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? @@ -2047,11 +2335,6 @@ typedef struct sg_image_data { NOTE: - SG_IMAGETYPE_ARRAY and SG_IMAGETYPE_3D are not supported on WebGL/GLES2, - use sg_query_features().imagetype_array and - sg_query_features().imagetype_3d at runtime to check if array- and - 3D-textures are supported. - Images with usage SG_USAGE_IMMUTABLE must be fully initialized by providing a valid .data member which points to initialization data. @@ -2088,30 +2371,58 @@ typedef struct sg_image_desc { sg_usage usage; sg_pixel_format pixel_format; int sample_count; - sg_filter min_filter; - sg_filter mag_filter; - sg_wrap wrap_u; - sg_wrap wrap_v; - sg_wrap wrap_w; - sg_border_color border_color; - uint32_t max_anisotropy; - float min_lod; - float max_lod; sg_image_data data; const char* label; - /* GL specific */ + // optionally inject backend-specific resources uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES]; uint32_t gl_texture_target; - /* Metal specific */ const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES]; - /* D3D11 specific */ const void* d3d11_texture; const void* d3d11_shader_resource_view; - /* WebGPU specific */ const void* wgpu_texture; uint32_t _end_canary; } sg_image_desc; +/* + sg_sampler_desc + + Creation parameters for sg_sampler objects, used in the sg_make_sampler() call + + .min_filter: SG_FILTER_NEAREST + .mag_filter: SG_FILTER_NEAREST + .mipmap_filter SG_FILTER_NONE + .wrap_u: SG_WRAP_REPEAT + .wrap_v: SG_WRAP_REPEAT + .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) + .min_lod 0.0f + .max_lod FLT_MAX + .border_color SG_BORDERCOLOR_OPAQUE_BLACK + .compare SG_COMPAREFUNC_NEVER + .max_anisotropy 1 (must be 1..16) + +*/ +typedef struct sg_sampler_desc { + uint32_t _start_canary; + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + float min_lod; + float max_lod; + sg_border_color border_color; + sg_compare_func compare; + uint32_t max_anisotropy; + const char* label; + // optionally inject backend-specific resources + uint32_t gl_sampler; + const void* mtl_sampler; + const void* d3d11_sampler; + const void* wgpu_sampler; + uint32_t _end_canary; +} sg_sampler_desc; + /* sg_shader_desc @@ -2119,7 +2430,7 @@ typedef struct sg_image_desc { programs, used as input to the sg_make_shader() function: - reflection information for vertex attributes (vertex shader inputs): - - vertex attribute name (required for GLES2, optional for GLES3 and GL) + - vertex attribute name (only optionally used by GLES3 and GL) - a semantic name and index (required for D3D11) - for each shader-stage (vertex and fragment): - the shader source or bytecode @@ -2133,10 +2444,16 @@ typedef struct sg_image_desc { - member name - member type (SG_UNIFORMTYPE_xxx) - if the member is an array, the number of array items - - reflection info for the texture images used by the shader stage: + - reflection info for textures used in the shader stage: - the image type (SG_IMAGETYPE_xxx) - - the sampler type (SG_SAMPLERTYPE_xxx, default is SG_SAMPLERTYPE_FLOAT) - - the name of the texture sampler (required for GLES2, optional everywhere else) + - the image-sample type (SG_IMAGESAMPLETYPE_xxx, default is SG_IMAGESAMPLETYPE_FLOAT) + - whether the shader expects a multisampled texture + - reflection info for samplers used in the shader stage: + - the sampler type (SG_SAMPLERTYPE_xxx) + - reflection info for each image-sampler-pair used by the shader: + - 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 For all GL backends, shader source-code must be provided. For D3D11 and Metal, either shader source-code or byte-code can be provided. @@ -2148,7 +2465,7 @@ typedef struct sg_image_desc { vertex shader stage and "ps_4_0" for the pixel shader stage. */ typedef struct sg_shader_attr_desc { - const char* name; // GLSL vertex attribute name (only strictly required for GLES2) + const char* name; // GLSL vertex attribute name (optional) const char* sem_name; // HLSL semantic name int sem_index; // HLSL semantic index } sg_shader_attr_desc; @@ -2166,11 +2483,24 @@ typedef struct sg_shader_uniform_block_desc { } sg_shader_uniform_block_desc; typedef struct sg_shader_image_desc { - const char* name; + bool used; + bool multisampled; sg_image_type image_type; - sg_sampler_type sampler_type; + sg_image_sample_type sample_type; } sg_shader_image_desc; +typedef struct sg_shader_sampler_desc { + bool used; + sg_sampler_type sampler_type; +} sg_shader_sampler_desc; + +typedef struct sg_shader_image_sampler_pair_desc { + bool used; + int image_slot; + int sampler_slot; + const char* glsl_name; +} sg_shader_image_sampler_pair_desc; + typedef struct sg_shader_stage_desc { const char* source; sg_range bytecode; @@ -2178,6 +2508,8 @@ typedef struct sg_shader_stage_desc { const char* d3d11_target; sg_shader_uniform_block_desc uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; 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]; } sg_shader_stage_desc; typedef struct sg_shader_desc { @@ -2258,28 +2590,28 @@ typedef struct sg_shader_desc { .alpha_to_coverage_enabled: false .label 0 (optional string label for trace hooks) */ -typedef struct sg_buffer_layout_desc { +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_buffer_layout_desc; +} sg_vertex_buffer_layout_state; -typedef struct sg_vertex_attr_desc { +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_desc; +} sg_vertex_attr_state; -typedef struct sg_layout_desc { - sg_buffer_layout_desc buffers[SG_MAX_SHADERSTAGE_BUFFERS]; - sg_vertex_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; -} sg_layout_desc; +typedef struct sg_vertex_layout_state { + sg_vertex_buffer_layout_state buffers[SG_MAX_VERTEX_BUFFERS]; + sg_vertex_attr_state attrs[SG_MAX_VERTEX_ATTRIBUTES]; +} sg_vertex_layout_state; typedef struct sg_stencil_face_state { sg_compare_func compare; @@ -2316,20 +2648,20 @@ typedef struct sg_blend_state { sg_blend_op op_alpha; } sg_blend_state; -typedef struct sg_color_state { +typedef struct sg_color_target_state { sg_pixel_format pixel_format; sg_color_mask write_mask; sg_blend_state blend; -} sg_color_state; +} sg_color_target_state; typedef struct sg_pipeline_desc { uint32_t _start_canary; sg_shader shader; - sg_layout_desc layout; + sg_vertex_layout_state layout; sg_depth_state depth; sg_stencil_state stencil; int color_count; - sg_color_state colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_color_target_state colors[SG_MAX_COLOR_ATTACHMENTS]; sg_primitive_type primitive_type; sg_index_type index_type; sg_cull_mode cull_mode; @@ -2344,34 +2676,41 @@ typedef struct sg_pipeline_desc { /* sg_pass_desc - Creation parameters for an sg_pass object, used as argument - to the sg_make_pass() function. + Creation parameters for an sg_pass object, used as argument to the + sg_make_pass() function. + + A pass object contains 1..4 color attachments, 0..4 msaa-resolve + attachemnts, and none or one depth-stencil attachment. - A pass object contains 1..4 color-attachments and none, or one, - depth-stencil-attachment. Each attachment consists of - an image, and two additional indices describing - which subimage the pass will render to: one mipmap index, and - if the image is a cubemap, array-texture or 3D-texture, the - face-index, array-layer or depth-slice. + 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 + is a cubemap, array-texture or 3D-texture, the face-index, array-layer or + depth-slice. - Pass images must fulfill the following requirements: + All attachments must have the same width and height. - All images must have: - - been created as render target (sg_image_desc.render_target = true) - - the same size - - the same sample count + All color attachments and the depth-stencil attachment must have the + same sample count. - In addition, all color-attachment images must have the same pixel format. + If a resolve attachment is set, an MSAA-resolve operation from the + associated color attachment into the resolve attachment image will take + place in the sg_end_pass() function. In this case, the color attachment + must have a (sample_count>1), and the resolve attachment a + (sample_count==1). The resolve attachment also must have the same pixel + format as the color attachment. + + NOTE that MSAA depth-stencil attachments cannot be msaa-resolved! */ typedef struct sg_pass_attachment_desc { sg_image image; int mip_level; - int slice; /* cube texture: face; array texture: layer; 3D texture: slice */ + int slice; // cube texture: face; array texture: layer; 3D texture: slice } sg_pass_attachment_desc; typedef struct sg_pass_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; const char* label; uint32_t _end_canary; @@ -2394,11 +2733,13 @@ typedef struct sg_trace_hooks { void (*reset_state_cache)(void* user_data); void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data); void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data); + void (*make_sampler)(const sg_sampler_desc* desc, sg_sampler result, void* user_data); void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data); void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data); void (*make_pass)(const sg_pass_desc* desc, sg_pass 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); @@ -2417,45 +2758,42 @@ typedef struct sg_trace_hooks { void (*commit)(void* user_data); void (*alloc_buffer)(sg_buffer result, void* user_data); void (*alloc_image)(sg_image result, void* user_data); + void (*alloc_sampler)(sg_sampler result, void* user_data); void (*alloc_shader)(sg_shader result, void* user_data); void (*alloc_pipeline)(sg_pipeline result, void* user_data); void (*alloc_pass)(sg_pass 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 (*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 (*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 (*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 (*push_debug_group)(const char* name, void* user_data); void (*pop_debug_group)(void* user_data); - void (*err_buffer_pool_exhausted)(void* user_data); - void (*err_image_pool_exhausted)(void* user_data); - void (*err_shader_pool_exhausted)(void* user_data); - void (*err_pipeline_pool_exhausted)(void* user_data); - void (*err_pass_pool_exhausted)(void* user_data); - void (*err_context_mismatch)(void* user_data); - void (*err_pass_invalid)(void* user_data); - void (*err_draw_invalid)(void* user_data); - void (*err_bindings_invalid)(void* user_data); } sg_trace_hooks; /* sg_buffer_info sg_image_info + sg_sampler_info sg_shader_info sg_pipeline_info sg_pass_info @@ -2470,47 +2808,298 @@ typedef struct sg_trace_hooks { sg_query_buffer_info() sg_query_image_info() + sg_query_sampler_info() sg_query_shader_info() sg_query_pipeline_info() sg_query_pass_info() */ typedef struct sg_slot_info { - sg_resource_state state; /* the current state of this resource slot */ - uint32_t res_id; /* type-neutral resource if (e.g. sg_buffer.id) */ - uint32_t ctx_id; /* the context this resource belongs to */ + 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 { - sg_slot_info slot; /* resource pool slot info */ - uint32_t update_frame_index; /* frame index of last sg_update_buffer() */ - uint32_t append_frame_index; /* frame index of last sg_append_buffer() */ - int append_pos; /* current position in buffer for sg_append_buffer() */ - bool append_overflow; /* is buffer in overflow state (due to sg_append_buffer) */ - int num_slots; /* number of renaming-slots for dynamically updated buffers */ - int active_slot; /* currently active write-slot for dynamically updated buffers */ + sg_slot_info slot; // resource pool slot info + uint32_t update_frame_index; // frame index of last sg_update_buffer() + uint32_t append_frame_index; // frame index of last sg_append_buffer() + int append_pos; // current position in buffer for sg_append_buffer() + bool append_overflow; // is buffer in overflow state (due to sg_append_buffer) + int num_slots; // number of renaming-slots for dynamically updated buffers + int active_slot; // currently active write-slot for dynamically updated buffers } sg_buffer_info; typedef struct sg_image_info { - sg_slot_info slot; /* resource pool slot info */ - uint32_t upd_frame_index; /* frame index of last sg_update_image() */ - int num_slots; /* number of renaming-slots for dynamically updated images */ - int active_slot; /* currently active write-slot for dynamically updated images */ - int width; /* image width */ - int height; /* image height */ + sg_slot_info slot; // resource pool slot info + uint32_t upd_frame_index; // frame index of last sg_update_image() + int num_slots; // number of renaming-slots for dynamically updated images + int active_slot; // currently active write-slot for dynamically updated images } sg_image_info; +typedef struct sg_sampler_info { + sg_slot_info slot; // resource pool slot info +} sg_sampler_info; + typedef struct sg_shader_info { - sg_slot_info slot; /* resoure pool slot info */ + sg_slot_info slot; // resource pool slot info } sg_shader_info; typedef struct sg_pipeline_info { - sg_slot_info slot; /* resource pool slot info */ + sg_slot_info slot; // resource pool slot info } sg_pipeline_info; typedef struct sg_pass_info { - sg_slot_info slot; /* resource pool slot info */ + sg_slot_info slot; // resource pool slot info } sg_pass_info; +/* + sg_log_item + + An enum with a unique item for each log message, warning, error + and validation layer message. +*/ +#define _SG_LOG_ITEMS \ + _SG_LOGITEM_XMACRO(OK, "Ok") \ + _SG_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SG_LOGITEM_XMACRO(GL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (gl)") \ + _SG_LOGITEM_XMACRO(GL_3D_TEXTURES_NOT_SUPPORTED, "3d textures not supported (gl)") \ + _SG_LOGITEM_XMACRO(GL_ARRAY_TEXTURES_NOT_SUPPORTED, "array textures not supported (gl)") \ + _SG_LOGITEM_XMACRO(GL_SHADER_COMPILATION_FAILED, "shader compilation failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_SHADER_LINKING_FAILED, "shader linking failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, "vertex attribute not found in shader (gl)") \ + _SG_LOGITEM_XMACRO(GL_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(D3D11_CREATE_BUFFER_FAILED, "CreateBuffer() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for depth-stencil texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_FAILED, "CreateTexture2D() failed for depth-stencil texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_FAILED, "CreateTexture2D() failed for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_SRV_FAILED, "CreateShaderResourceView() failed for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 3D texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_FAILED, "CreateTexture3D() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_SRV_FAILED, "CreateShaderResourceView() failed for 3d texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_MSAA_TEXTURE_FAILED, "CreateTexture2D() failed for MSAA render target texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_SAMPLER_STATE_FAILED, "CreateSamplerState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED, "loading d3dcompiler_47.dll failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_FAILED, "shader compilation failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_CONSTANT_BUFFER_FAILED, "CreateBuffer() failed for uniform constant buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_INPUT_LAYOUT_FAILED, "CreateInputLayout() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_RASTERIZER_STATE_FAILED, "CreateRasterizerState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED, "CreateDepthStencilState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BLEND_STATE_FAILED, "CreateBlendState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_RTV_FAILED, "CreateRenderTargetView() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DSV_FAILED, "CreateDepthStencilView() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED, "Map() failed when updating buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_APPEND_BUFFER_FAILED, "Map() failed when appending to buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED, "Map() failed when updating image (d3d11)") \ + _SG_LOGITEM_XMACRO(METAL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_FAILED, "shader compilation failed (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_CREATION_FAILED, "shader creation failed (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(METAL_VERTEX_SHADER_ENTRY_NOT_FOUND, "vertex shader entry function not found (metal)") \ + _SG_LOGITEM_XMACRO(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND, "fragment shader entry not found (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_FAILED, "failed to create render pipeline state (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(WGPU_MAP_UNIFORM_BUFFER_FAILED, "mapping uniform buffer failed (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_STAGING_BUFFER_FULL_COPY_TO_BUFFER, "per frame staging buffer full when copying to buffer (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_STAGING_BUFFER_FULL_COPY_TO_TEXTURE, "per frame staging buffer full when copying to texture (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_RESET_STATE_CACHE_FIXME, "_sg_wgpu_reset_state_cache: fixme") \ + _SG_LOGITEM_XMACRO(WGPU_ACTIVATE_CONTEXT_FIXME, "_sg_wgpu_activate_context: fixme") \ + _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(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(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(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(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(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(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(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_IMAGEDATA_NODATA, "sg_image_data: no data (.ptr and/or .size is zero)") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_DATA_SIZE, "sg_image_data: data size doesn't match expected surface size") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_CANARY, "sg_image_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WIDTH, "sg_image_desc.width must be > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_HEIGHT, "sg_image_desc.height must be > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_PIXELFORMAT, "invalid pixel format for render-target image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, "invalid pixel format for non-render-target image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, "non-render-target images cannot be multisampled") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, "MSAA not supported for this pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS, "MSAA images must have num_mipmaps == 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_3D_IMAGE, "3D images cannot have a sample_count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE, "3D images cannot have a depth/stencil image format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_IMMUTABLE, "render target images must be SG_USAGE_IMMUTABLE") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_NO_DATA, "render target images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_INJECTED_NO_DATA, "images with injected textures cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, "dynamic/stream images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, "compressed images must be immutable") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_CANARY, "sg_sampler_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_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_SHADERDESC_CANARY, "sg_shader_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE, "shader source code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_BYTECODE, "shader byte code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, "shader source or byte code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, "shader byte code length (in bytes) required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_UBS, "shader uniform blocks must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, "uniform block members must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_UB_MEMBERS, "GL backend requires uniform block member declarations") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_MEMBER_NAME, "uniform block member name missing") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, "size of uniform block members doesn't match uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_ARRAY_COUNT, "uniform array count must be >= 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_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)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_SAMPLER_SLOT_OUT_OF_RANGE, "shader stage: image-sampler-pair image slot index is out of range (sg_shader_desc.vs|fs.image_sampler_pairs[].sampler_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_NAME_REQUIRED_FOR_GL, "shader stage: image-sampler-pairs must be named in GL (sg_shader_desc.vs|fs.image_sampler_pairs[].name)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_NAME_BUT_NOT_USED, "shader stage: image-sampler-pair has name but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_IMAGE_BUT_NOT_USED, "shader stage: image-sampler-pair has .image_slot != 0 but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_SAMPLER_BUT_NOT_USED, "shader stage: image-sampler-pair .sampler_slot != 0 but .used field not true") \ + _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_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_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_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_ATT_COUNT, "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLOR_FORMAT, "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTH_FORMAT, "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SAMPLE_COUNT, "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE, "sg_apply_bindings: must be called after sg_apply_pipeline") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_EXISTS, "sg_apply_bindings: currently applied pipeline object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_VALID, "sg_apply_bindings: currently applied pipeline object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VBS, "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_EXISTS, "sg_apply_bindings: vertex buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_TYPE, "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_OVERFLOW, "sg_apply_bindings: buffer in vertex buffer slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_NO_IB, "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB, "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_EXISTS, "sg_apply_bindings: index buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_TYPE, "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_OVERFLOW, "sg_apply_bindings: buffer in index buffer slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: missing image binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMG_EXISTS, "sg_apply_bindings: image bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of image bound to vertex stage doesn't match shader desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1 to vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_IMAGE_BINDING, "sg_apply_bindings: unexpected image binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: missing sampler binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARE on vertex stage but sampler has SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_SAMPLE on vertex stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _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_FS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: missing image binding on fragment stage") \ + _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") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1 to fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_IMAGE_BINDING, "sg_apply_bindings: unexpected image binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: missing sampler binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARE on fragment stage but sampler has SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_SAMPLE on fragment stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _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_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 exceeds declared uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_USAGE, "sg_update_buffer: cannot update immutable buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_SIZE, "sg_update_buffer: update size is bigger than buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_ONCE, "sg_update_buffer: only one update allowed per buffer and frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_APPEND, "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_USAGE, "sg_append_buffer: cannot append to immutable buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_SIZE, "sg_append_buffer: overall appended size is bigger than buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_UPDATE, "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_USAGE, "sg_update_image: cannot update immutable image") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_ONCE, "sg_update_image: only one update allowed per image and frame") \ + _SG_LOGITEM_XMACRO(VALIDATION_FAILED, "validation layer checks failed") \ + +#define _SG_LOGITEM_XMACRO(item,msg) SG_LOGITEM_##item, +typedef enum sg_log_item { + _SG_LOG_ITEMS +} sg_log_item; +#undef _SG_LOGITEM_XMACRO + /* sg_desc @@ -2528,13 +3117,13 @@ typedef struct sg_pass_info { .buffer_pool_size 128 .image_pool_size 128 + .sampler_pool_size 64 .shader_pool_size 32 .pipeline_pool_size 64 .pass_pool_size 16 .context_pool_size 16 .uniform_buffer_size 4 MB (4*1024*1024) .staging_buffer_size 8 MB (8*1024*1024) - .sampler_cache_size 64 .max_commit_listeners 1024 .disable_validation false @@ -2549,12 +3138,6 @@ typedef struct sg_pass_info { .context.depth_format SG_PIXELFORMAT_DEPTH_STENCIL .context.sample_count 1 - GL specific: - .context.gl.force_gles2 - if this is true the GL backend will act in "GLES2 fallback mode" even - when compiled with SOKOL_GLES3, this is useful to fall back - to traditional WebGL if a browser doesn't support a WebGL2 context - Metal specific: (NOTE: All Objective-C object references are transferred through a bridged (const void*) to sokol_gfx, which will use a unretained @@ -2628,10 +3211,6 @@ typedef struct sg_pass_info { a completely initialized sg_context_desc struct with information provided by sokol_app.h. */ -typedef struct sg_gl_context_desc { - bool force_gles2; -} sg_gl_context_desc; - typedef struct sg_metal_context_desc { const void* device; const void* (*renderpass_descriptor_cb)(void); @@ -2652,12 +3231,12 @@ typedef struct sg_d3d11_context_desc { } sg_d3d11_context_desc; typedef struct sg_wgpu_context_desc { - const void* device; /* WGPUDevice */ - const void* (*render_view_cb)(void); /* returns WGPUTextureView */ + 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_cb)(void); // returns WGPUTextureView const void* (*resolve_view_userdata_cb)(void*); - const void* (*depth_stencil_view_cb)(void); /* returns WGPUTextureView, must be WGPUTextureFormat_Depth24Plus8 */ + const void* (*depth_stencil_view_cb)(void); // returns WGPUTextureView, must be WGPUTextureFormat_Depth24Plus8 const void* (*depth_stencil_view_userdata_cb)(void*); void* user_data; } sg_wgpu_context_desc; @@ -2666,7 +3245,6 @@ typedef struct sg_context_desc { sg_pixel_format color_format; sg_pixel_format depth_format; int sample_count; - sg_gl_context_desc gl; sg_metal_context_desc metal; sg_d3d11_context_desc d3d11; sg_wgpu_context_desc wgpu; @@ -2703,11 +3281,22 @@ typedef struct sg_allocator { /* sg_logger - Used in sg_desc to provide custom log callbacks to sokol_gfx.h. - Default behavior is SOKOL_LOG(message). + Used in sg_desc to provide a logging function. Please be aware + that without logging function, sokol-gfx will be completely + silent, e.g. it will not report errors, warnings and + validation layer messages. For maximum error verbosity, + compile in debug mode (e.g. NDEBUG *not* defined) and install + a logger (for instance the standard logging function from sokol_log.h). */ typedef struct sg_logger { - void (*log_cb)(const char* message, void* user_data); + void (*func)( + const char* tag, // always "sg" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SG_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gfx.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); void* user_data; } sg_logger; @@ -2715,22 +3304,23 @@ typedef struct sg_desc { uint32_t _start_canary; int buffer_pool_size; int image_pool_size; + int sampler_pool_size; int shader_pool_size; int pipeline_pool_size; int pass_pool_size; int context_pool_size; int uniform_buffer_size; int staging_buffer_size; - int sampler_cache_size; int max_commit_listeners; bool disable_validation; // disable validation layer even in debug mode, useful for tests + bool mtl_force_managed_storage_mode; // for debugging: use Metal managed storage mode for resources even with UMA sg_allocator allocator; sg_logger logger; // optional log function override sg_context_desc context; uint32_t _end_canary; } sg_desc; -/* setup and misc functions */ +// setup and misc functions SOKOL_GFX_API_DECL void sg_setup(const sg_desc* desc); SOKOL_GFX_API_DECL void sg_shutdown(void); SOKOL_GFX_API_DECL bool sg_isvalid(void); @@ -2741,14 +3331,16 @@ SOKOL_GFX_API_DECL void sg_pop_debug_group(void); SOKOL_GFX_API_DECL bool sg_add_commit_listener(sg_commit_listener listener); SOKOL_GFX_API_DECL bool sg_remove_commit_listener(sg_commit_listener listener); -/* resource creation, destruction and updating */ +// resource creation, destruction and updating SOKOL_GFX_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc); SOKOL_GFX_API_DECL sg_image sg_make_image(const sg_image_desc* desc); +SOKOL_GFX_API_DECL sg_sampler sg_make_sampler(const sg_sampler_desc* desc); SOKOL_GFX_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc); SOKOL_GFX_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc); SOKOL_GFX_API_DECL sg_pass sg_make_pass(const sg_pass_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); @@ -2758,7 +3350,7 @@ SOKOL_GFX_API_DECL int sg_append_buffer(sg_buffer buf, const sg_range* data); SOKOL_GFX_API_DECL bool sg_query_buffer_overflow(sg_buffer buf); SOKOL_GFX_API_DECL bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size); -/* rendering functions */ +// 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); @@ -2773,59 +3365,74 @@ SOKOL_GFX_API_DECL void sg_draw(int base_element, int num_elements, int num_inst SOKOL_GFX_API_DECL void sg_end_pass(void); SOKOL_GFX_API_DECL void sg_commit(void); -/* getting information */ +// getting information SOKOL_GFX_API_DECL sg_desc sg_query_desc(void); SOKOL_GFX_API_DECL sg_backend sg_query_backend(void); SOKOL_GFX_API_DECL sg_features sg_query_features(void); SOKOL_GFX_API_DECL sg_limits sg_query_limits(void); SOKOL_GFX_API_DECL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt); -/* get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID) */ +// 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); -/* get runtime information about a resource */ +// 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); -/* get resource creation desc struct with their default values replaced */ +// 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); +// 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); -/* separate resource allocation and initialization (for async setup) */ +// separate resource allocation and initialization (for async setup) SOKOL_GFX_API_DECL sg_buffer sg_alloc_buffer(void); SOKOL_GFX_API_DECL sg_image sg_alloc_image(void); +SOKOL_GFX_API_DECL sg_sampler sg_alloc_sampler(void); SOKOL_GFX_API_DECL sg_shader sg_alloc_shader(void); SOKOL_GFX_API_DECL sg_pipeline sg_alloc_pipeline(void); SOKOL_GFX_API_DECL sg_pass sg_alloc_pass(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_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_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_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); -/* rendering contexts (optional) */ +// 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); @@ -2836,23 +3443,24 @@ SOKOL_GFX_API_DECL void sg_discard_context(sg_context ctx_id); This group of functions will be expanded as needed. */ -/* D3D11: return ID3D11Device */ +// D3D11: return ID3D11Device SOKOL_GFX_API_DECL const void* sg_d3d11_device(void); -/* Metal: return __bridge-casted MTLDevice */ +// 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) */ +// 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); #ifdef __cplusplus -} /* extern "C" */ +} // extern "C" -/* reference-based equivalents for c++ */ +// reference-based equivalents for c++ inline void sg_setup(const sg_desc& desc) { return sg_setup(&desc); } inline sg_buffer sg_make_buffer(const sg_buffer_desc& desc) { return sg_make_buffer(&desc); } inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image(&desc); } +inline sg_sampler sg_make_sampler(const sg_sampler_desc& desc) { return sg_make_sampler(&desc); } inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); } inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); } inline sg_pass sg_make_pass(const sg_pass_desc& desc) { return sg_make_pass(&desc); } @@ -2866,27 +3474,35 @@ inline void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_rang inline sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc& desc) { return sg_query_buffer_defaults(&desc); } inline sg_image_desc sg_query_image_defaults(const sg_image_desc& desc) { return sg_query_image_defaults(&desc); } +inline sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc& desc) { return sg_query_sampler_defaults(&desc); } inline sg_shader_desc sg_query_shader_defaults(const sg_shader_desc& desc) { return sg_query_shader_defaults(&desc); } inline sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc& desc) { return sg_query_pipeline_defaults(&desc); } inline sg_pass_desc sg_query_pass_defaults(const sg_pass_desc& desc) { return sg_query_pass_defaults(&desc); } -inline void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc& desc) { return sg_init_buffer(buf_id, &desc); } -inline void sg_init_image(sg_image img_id, const sg_image_desc& desc) { return sg_init_image(img_id, &desc); } -inline void sg_init_shader(sg_shader shd_id, const sg_shader_desc& desc) { return sg_init_shader(shd_id, &desc); } -inline void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc& desc) { return sg_init_pipeline(pip_id, &desc); } -inline void sg_init_pass(sg_pass pass_id, const sg_pass_desc& desc) { return sg_init_pass(pass_id, &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_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); } #endif #endif // SOKOL_GFX_INCLUDED -/*--- IMPLEMENTATION ---------------------------------------------------------*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_GFX_IMPL #define SOKOL_GFX_IMPL_INCLUDED (1) -#if !(defined(SOKOL_GLCORE33)||defined(SOKOL_GLES2)||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_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" +#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" #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" @@ -2908,34 +3524,10 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #include #define SOKOL_ASSERT(c) assert(c) #endif -#ifndef SOKOL_VALIDATE_BEGIN - #define SOKOL_VALIDATE_BEGIN() _sg_validate_begin() -#endif -#ifndef SOKOL_VALIDATE - #define SOKOL_VALIDATE(cond, err) _sg_validate((cond), err) -#endif -#ifndef SOKOL_VALIDATE_END - #define SOKOL_VALIDATE_END() _sg_validate_end() -#endif #ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif -#if !defined(SOKOL_DEBUG) - #define SG_LOG(s) -#else - #define SG_LOG(s) _sg_log(s) - #ifndef SOKOL_LOG - #if defined(__ANDROID__) - #include - #define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_GFX", s) - #else - #include - #define SOKOL_LOG(s) puts(s) - #endif - #endif -#endif - #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static @@ -2956,7 +3548,7 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define _SG_TRACE_NOARGS(fn) #endif -/* default clear values */ +// default clear values #ifndef SG_DEFAULT_CLEAR_RED #define SG_DEFAULT_CLEAR_RED (0.5f) #endif @@ -2978,11 +3570,11 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable:4115) /* named type definition in parentheses */ -#pragma warning(disable:4505) /* unreferenced local function has been removed */ -#pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union (needed by d3d11.h) */ -#pragma warning(disable:4054) /* 'type cast': from function pointer */ -#pragma warning(disable:4055) /* 'type cast': from data pointer */ +#pragma warning(disable:4115) // named type definition in parentheses +#pragma warning(disable:4505) // unreferenced local function has been removed +#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union (needed by d3d11.h) +#pragma warning(disable:4054) // 'type cast': from function pointer +#pragma warning(disable:4055) // 'type cast': from data pointer #endif #if defined(SOKOL_D3D11) @@ -2998,14 +3590,10 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #include #include #ifdef _MSC_VER - #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - #pragma comment (lib, "WindowsApp") - #else - #pragma comment (lib, "kernel32") - #pragma comment (lib, "user32") - #pragma comment (lib, "dxgi") - #pragma comment (lib, "d3d11") - #endif + #pragma comment (lib, "kernel32") + #pragma comment (lib, "user32") + #pragma comment (lib, "dxgi") + #pragma comment (lib, "d3d11") #endif #elif defined(SOKOL_METAL) // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting @@ -3015,6 +3603,7 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #endif #endif #include + #include #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE #define _SG_TARGET_MACOS (1) #else @@ -3030,7 +3619,7 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #else #include #endif -#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) #define _SOKOL_ANY_GL (1) // include platform specific GL headers (or on Win32: use an embedded GL loader) @@ -3061,12 +3650,6 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #elif defined(__EMSCRIPTEN__) || defined(__ANDROID__) #if defined(SOKOL_GLES3) #include - #elif defined(SOKOL_GLES2) - #ifndef GL_EXT_PROTOTYPES - #define GL_GLEXT_PROTOTYPES - #endif - #include - #include #endif #elif defined(__linux__) || defined(__unix__) #define GL_GLEXT_PROTOTYPES @@ -3107,13 +3690,12 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ typedef void GLvoid; typedef int64_t GLint64; typedef float GLfloat; - typedef struct __GLsync * GLsync; typedef int GLint; #define GL_INT_2_10_10_10_REV 0x8D9F #define GL_R32F 0x822E #define GL_PROGRAM_POINT_SIZE 0x8642 - #define GL_STENCIL_ATTACHMENT 0x8D20 #define GL_DEPTH_ATTACHMENT 0x8D00 + #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A #define GL_COLOR_ATTACHMENT2 0x8CE2 #define GL_COLOR_ATTACHMENT0 0x8CE0 #define GL_R16F 0x822D @@ -3222,7 +3804,7 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define GL_LEQUAL 0x0203 #define GL_STENCIL_TEST 0x0B90 #define GL_DITHER 0x0BD0 - #define GL_DEPTH_COMPONENT16 0x81A5 + #define GL_DEPTH_COMPONENT32F 0x8CAC #define GL_EQUAL 0x0202 #define GL_FRAMEBUFFER 0x8D40 #define GL_RGB5 0x8050 @@ -3312,6 +3894,9 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB #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 #endif #ifndef GL_UNSIGNED_INT_2_10_10_10_REV @@ -3398,40 +3983,24 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #ifndef GL_LUMINANCE #define GL_LUMINANCE 0x1909 #endif - - #ifdef SOKOL_GLES2 - #ifdef GL_ANGLE_instanced_arrays - #define _SOKOL_GL_INSTANCING_ENABLED - #define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedANGLE(mode, first, count, instancecount) - #define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedANGLE(mode, count, type, indices, instancecount) - #define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorANGLE(index, divisor) - #elif defined(GL_EXT_draw_instanced) && defined(GL_EXT_instanced_arrays) - #define _SOKOL_GL_INSTANCING_ENABLED - #define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedEXT(mode, first, count, instancecount) - #define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedEXT(mode, count, type, indices, instancecount) - #define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorEXT(index, divisor) - #else - #define _SOKOL_GLES2_INSTANCING_ERROR "Select GL_ANGLE_instanced_arrays or (GL_EXT_draw_instanced & GL_EXT_instanced_arrays) to enable instancing in GLES2" - #define glDrawArraysInstanced(mode, first, count, instancecount) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) - #define glDrawElementsInstanced(mode, count, type, indices, instancecount) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) - #define glVertexAttribDivisor(index, divisor) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) - #endif - #else - #define _SOKOL_GL_INSTANCING_ENABLED - #endif #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } #endif -/*=== COMMON BACKEND STUFF ===================================================*/ - -/* resource pool slots */ +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs +// resource pool slots typedef struct { uint32_t id; uint32_t ctx_id; sg_resource_state state; } _sg_slot_t; -/* constants */ +// constants enum { _SG_STRING_SIZE = 16, _SG_SLOT_SHIFT = 16, @@ -3439,22 +4008,22 @@ enum { _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), _SG_DEFAULT_BUFFER_POOL_SIZE = 128, _SG_DEFAULT_IMAGE_POOL_SIZE = 128, + _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_SAMPLER_CACHE_CAPACITY = 64, _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, _SG_DEFAULT_STAGING_SIZE = 8 * 1024 * 1024, _SG_DEFAULT_MAX_COMMIT_LISTENERS = 1024, }; -/* fixed-size string */ +// fixed-size string typedef struct { char buf[_SG_STRING_SIZE]; } _sg_str_t; -/* helper macros */ +// helper macros #define _sg_def(val, def) (((val) == 0) ? (def) : (val)) #define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) #define _sg_min(a,b) (((a)<(b))?(a):(b)) @@ -3470,27 +4039,30 @@ typedef struct { int size; int append_pos; bool append_overflow; - sg_buffer_type type; - sg_usage usage; uint32_t update_frame_index; uint32_t append_frame_index; int num_slots; int active_slot; + sg_buffer_type type; + sg_usage usage; } _sg_buffer_common_t; _SOKOL_PRIVATE void _sg_buffer_common_init(_sg_buffer_common_t* cmn, const sg_buffer_desc* desc) { cmn->size = (int)desc->size; cmn->append_pos = 0; cmn->append_overflow = false; - cmn->type = desc->type; - cmn->usage = desc->usage; cmn->update_frame_index = 0; cmn->append_frame_index = 0; - cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->num_slots = (desc->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; cmn->active_slot = 0; + cmn->type = desc->type; + cmn->usage = desc->usage; } typedef struct { + uint32_t upd_frame_index; + int num_slots; + int active_slot; sg_image_type type; bool render_target; int width; @@ -3500,19 +4072,12 @@ typedef struct { sg_usage usage; sg_pixel_format pixel_format; int sample_count; - sg_filter min_filter; - sg_filter mag_filter; - sg_wrap wrap_u; - sg_wrap wrap_v; - sg_wrap wrap_w; - sg_border_color border_color; - uint32_t max_anisotropy; - uint32_t upd_frame_index; - int num_slots; - int active_slot; } _sg_image_common_t; _SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_image_desc* desc) { + cmn->upd_frame_index = 0; + cmn->num_slots = (desc->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; cmn->type = desc->type; cmn->render_target = desc->render_target; cmn->width = desc->width; @@ -3522,32 +4087,65 @@ _SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_imag cmn->usage = desc->usage; cmn->pixel_format = desc->pixel_format; cmn->sample_count = desc->sample_count; - cmn->min_filter = desc->min_filter; - cmn->mag_filter = desc->mag_filter; - cmn->wrap_u = desc->wrap_u; - cmn->wrap_v = desc->wrap_v; - cmn->wrap_w = desc->wrap_w; +} + +typedef struct { + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + float min_lod; + float max_lod; + sg_border_color border_color; + sg_compare_func compare; + uint32_t max_anisotropy; +} _sg_sampler_common_t; + +_SOKOL_PRIVATE void _sg_sampler_common_init(_sg_sampler_common_t* cmn, const sg_sampler_desc* desc) { + cmn->min_filter = desc->min_filter; + cmn->mag_filter = desc->mag_filter; + cmn->mipmap_filter = desc->mipmap_filter; + cmn->wrap_u = desc->wrap_u; + cmn->wrap_v = desc->wrap_v; + cmn->wrap_w = desc->wrap_w; + cmn->min_lod = desc->min_lod; + cmn->max_lod = desc->max_lod; cmn->border_color = desc->border_color; + cmn->compare = desc->compare; cmn->max_anisotropy = desc->max_anisotropy; - cmn->upd_frame_index = 0; - cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; - cmn->active_slot = 0; } typedef struct { size_t size; -} _sg_uniform_block_t; +} _sg_shader_uniform_block_t; typedef struct { sg_image_type image_type; - sg_sampler_type sampler_type; + sg_image_sample_type sample_type; + bool multisampled; } _sg_shader_image_t; +typedef struct { + sg_sampler_type sampler_type; +} _sg_shader_sampler_t; + +// combined image sampler mappings, only needed on GL +typedef struct { + int image_slot; + int sampler_slot; +} _sg_shader_image_sampler_t; + typedef struct { int num_uniform_blocks; int num_images; - _sg_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + int num_samplers; + int num_image_samplers; + _sg_shader_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; _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]; } _sg_shader_stage_t; typedef struct { @@ -3570,49 +4168,77 @@ _SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_sh SOKOL_ASSERT(stage->num_images == 0); for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - if (img_desc->image_type == _SG_IMAGETYPE_DEFAULT) { + if (!img_desc->used) { break; } + stage->images[img_index].multisampled = img_desc->multisampled; stage->images[img_index].image_type = img_desc->image_type; - stage->images[img_index].sampler_type = img_desc->sampler_type; + stage->images[img_index].sample_type = img_desc->sample_type; stage->num_images++; } + SOKOL_ASSERT(stage->num_samplers == 0); + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (!smp_desc->used) { + break; + } + stage->samplers[smp_index].sampler_type = smp_desc->sampler_type; + stage->num_samplers++; + } + SOKOL_ASSERT(stage->num_image_samplers == 0); + for (int img_smp_index = 0; img_smp_index < SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + if (!img_smp_desc->used) { + break; + } + SOKOL_ASSERT((img_smp_desc->image_slot >= 0) && (img_smp_desc->image_slot < stage->num_images)); + stage->image_samplers[img_smp_index].image_slot = img_smp_desc->image_slot; + SOKOL_ASSERT((img_smp_desc->sampler_slot >= 0) && (img_smp_desc->sampler_slot < stage->num_samplers)); + stage->image_samplers[img_smp_index].sampler_slot = img_smp_desc->sampler_slot; + stage->num_image_samplers++; + } } } typedef struct { + bool vertex_buffer_layout_active[SG_MAX_VERTEX_BUFFERS]; + bool use_instanced_draw; sg_shader shader_id; + sg_vertex_layout_state layout; + sg_depth_state depth; + sg_stencil_state stencil; + int color_count; + sg_color_target_state colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_primitive_type primitive_type; sg_index_type index_type; - bool use_instanced_draw; - bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS]; - int color_attachment_count; - sg_pixel_format color_formats[SG_MAX_COLOR_ATTACHMENTS]; - sg_pixel_format depth_format; + sg_cull_mode cull_mode; + sg_face_winding face_winding; int sample_count; - float depth_bias; - float depth_bias_slope_scale; - float depth_bias_clamp; sg_color blend_color; + bool alpha_to_coverage_enabled; } _sg_pipeline_common_t; _SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const sg_pipeline_desc* desc) { - SOKOL_ASSERT((desc->color_count >= 1) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS)); + SOKOL_ASSERT((desc->color_count >= 0) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS)); + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++) { + cmn->vertex_buffer_layout_active[i] = false; + } + cmn->use_instanced_draw = false; cmn->shader_id = desc->shader; + cmn->layout = desc->layout; + cmn->depth = desc->depth; + cmn->stencil = desc->stencil; + cmn->color_count = desc->color_count; + for (int i = 0; i < desc->color_count; i++) { + cmn->colors[i] = desc->colors[i]; + } + cmn->primitive_type = desc->primitive_type; cmn->index_type = desc->index_type; - cmn->use_instanced_draw = false; - for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { - cmn->vertex_layout_valid[i] = false; - } - cmn->color_attachment_count = desc->color_count; - for (int i = 0; i < cmn->color_attachment_count; i++) { - cmn->color_formats[i] = desc->colors[i].pixel_format; - } - cmn->depth_format = desc->depth.pixel_format; + cmn->cull_mode = desc->cull_mode; + cmn->face_winding = desc->face_winding; cmn->sample_count = desc->sample_count; - cmn->depth_bias = desc->depth.bias; - cmn->depth_bias_slope_scale = desc->depth.bias_slope_scale; - cmn->depth_bias_clamp = desc->depth.bias_clamp; cmn->blend_color = desc->blend_color; + cmn->alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; } typedef struct { @@ -3622,132 +4248,36 @@ typedef struct { } _sg_pass_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; -_SOKOL_PRIVATE void _sg_pass_common_init(_sg_pass_common_t* cmn, const sg_pass_desc* desc) { - const sg_pass_attachment_desc* att_desc; - _sg_pass_attachment_common_t* att; +_SOKOL_PRIVATE void _sg_pass_attachment_common_init(_sg_pass_attachment_common_t* cmn, const sg_pass_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_ASSERT((width > 0) && (height > 0)); + cmn->width = width; + cmn->height = height; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - att_desc = &desc->color_attachments[i]; - if (att_desc->image.id != SG_INVALID_ID) { + if (desc->color_attachments[i].image.id != SG_INVALID_ID) { cmn->num_color_atts++; - att = &cmn->color_atts[i]; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; + _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]); } } - att_desc = &desc->depth_stencil_attachment; - if (att_desc->image.id != SG_INVALID_ID) { - att = &cmn->ds_att; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; - } -} - -/*=== GENERIC SAMPLER CACHE ==================================================*/ - -/* - this is used by the Metal and WGPU backends to reduce the - number of sampler state objects created through the backend API -*/ -typedef struct { - sg_filter min_filter; - sg_filter mag_filter; - sg_wrap wrap_u; - sg_wrap wrap_v; - sg_wrap wrap_w; - sg_border_color border_color; - uint32_t max_anisotropy; - int min_lod; /* orig min/max_lod is float, this is int(min/max_lod*1000.0) */ - int max_lod; - uintptr_t sampler_handle; -} _sg_sampler_cache_item_t; - -typedef struct { - int capacity; - int num_items; - _sg_sampler_cache_item_t* items; -} _sg_sampler_cache_t; - -_SOKOL_PRIVATE void _sg_smpcache_init(_sg_sampler_cache_t* cache, int capacity) { - SOKOL_ASSERT(cache && (capacity > 0)); - _sg_clear(cache, sizeof(_sg_sampler_cache_t)); - cache->capacity = capacity; - const size_t size = (size_t)cache->capacity * sizeof(_sg_sampler_cache_item_t); - cache->items = (_sg_sampler_cache_item_t*) _sg_malloc_clear(size); -} - -_SOKOL_PRIVATE void _sg_smpcache_discard(_sg_sampler_cache_t* cache) { - SOKOL_ASSERT(cache && cache->items); - _sg_free(cache->items); - cache->items = 0; - cache->num_items = 0; - cache->capacity = 0; -} - -_SOKOL_PRIVATE int _sg_smpcache_minlod_int(float min_lod) { - return (int) (min_lod * 1000.0f); -} - -_SOKOL_PRIVATE int _sg_smpcache_maxlod_int(float max_lod) { - return (int) (_sg_clamp(max_lod, 0.0f, 1000.0f) * 1000.0f); -} - -_SOKOL_PRIVATE int _sg_smpcache_find_item(const _sg_sampler_cache_t* cache, const sg_image_desc* img_desc) { - /* return matching sampler cache item index or -1 */ - SOKOL_ASSERT(cache && cache->items); - SOKOL_ASSERT(img_desc); - const int min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); - const int max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); - for (int i = 0; i < cache->num_items; i++) { - const _sg_sampler_cache_item_t* item = &cache->items[i]; - if ((img_desc->min_filter == item->min_filter) && - (img_desc->mag_filter == item->mag_filter) && - (img_desc->wrap_u == item->wrap_u) && - (img_desc->wrap_v == item->wrap_v) && - (img_desc->wrap_w == item->wrap_w) && - (img_desc->max_anisotropy == item->max_anisotropy) && - (img_desc->border_color == item->border_color) && - (min_lod == item->min_lod) && - (max_lod == item->max_lod)) - { - return i; - } + if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { + _sg_pass_attachment_common_init(&cmn->ds_att, &desc->depth_stencil_attachment); } - /* fallthrough: no matching cache item found */ - return -1; } -_SOKOL_PRIVATE void _sg_smpcache_add_item(_sg_sampler_cache_t* cache, const sg_image_desc* img_desc, uintptr_t sampler_handle) { - SOKOL_ASSERT(cache && cache->items); - SOKOL_ASSERT(img_desc); - SOKOL_ASSERT(cache->num_items < cache->capacity); - const int item_index = cache->num_items++; - _sg_sampler_cache_item_t* item = &cache->items[item_index]; - item->min_filter = img_desc->min_filter; - item->mag_filter = img_desc->mag_filter; - item->wrap_u = img_desc->wrap_u; - item->wrap_v = img_desc->wrap_v; - item->wrap_w = img_desc->wrap_w; - item->border_color = img_desc->border_color; - item->max_anisotropy = img_desc->max_anisotropy; - item->min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); - item->max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); - item->sampler_handle = sampler_handle; -} - -_SOKOL_PRIVATE uintptr_t _sg_smpcache_sampler(_sg_sampler_cache_t* cache, int item_index) { - SOKOL_ASSERT(cache && cache->items); - SOKOL_ASSERT(item_index < cache->num_items); - return cache->items[item_index].sampler_handle; -} - -/*=== DUMMY BACKEND DECLARATIONS =============================================*/ #if defined(SOKOL_DUMMY_BACKEND) typedef struct { _sg_slot_t slot; @@ -3761,6 +4291,12 @@ typedef struct { } _sg_dummy_image_t; typedef _sg_dummy_image_t _sg_image_t; +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; +} _sg_dummy_sampler_t; +typedef _sg_dummy_sampler_t _sg_sampler_t; + typedef struct { _sg_slot_t slot; _sg_shader_common_t cmn; @@ -3783,6 +4319,7 @@ typedef struct { _sg_pass_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; } dmy; } _sg_dummy_pass_t; @@ -3794,14 +4331,13 @@ typedef struct { } _sg_dummy_context_t; typedef _sg_dummy_context_t _sg_context_t; -/*== GL BACKEND DECLARATIONS =================================================*/ #elif defined(_SOKOL_ANY_GL) typedef struct { _sg_slot_t slot; _sg_buffer_common_t cmn; struct { GLuint buf[SG_NUM_INFLIGHT_FRAMES]; - bool ext_buffers; /* if true, external buffers were injected with sg_buffer_desc.gl_buffers */ + bool injected; // if true, external buffers were injected with sg_buffer_desc.gl_buffers } gl; } _sg_gl_buffer_t; typedef _sg_gl_buffer_t _sg_buffer_t; @@ -3811,14 +4347,23 @@ typedef struct { _sg_image_common_t cmn; struct { GLenum target; - GLuint depth_render_buffer; GLuint msaa_render_buffer; GLuint tex[SG_NUM_INFLIGHT_FRAMES]; - bool ext_textures; /* if true, external textures were injected with sg_image_desc.gl_textures */ + bool injected; // if true, external textures were injected with sg_image_desc.gl_textures } gl; } _sg_gl_image_t; typedef _sg_gl_image_t _sg_image_t; +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + GLuint smp; + bool injected; // true if external sampler was injects in sg_sampler_desc.gl_sampler + } gl; +} _sg_gl_sampler_t; +typedef _sg_gl_sampler_t _sg_sampler_t; + typedef struct { GLint gl_loc; sg_uniform_type type; @@ -3833,7 +4378,7 @@ typedef struct { typedef struct { int gl_tex_slot; -} _sg_gl_shader_image_t; +} _sg_gl_shader_image_sampler_t; typedef struct { _sg_str_t name; @@ -3841,7 +4386,7 @@ typedef struct { typedef struct { _sg_gl_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; - _sg_gl_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_gl_shader_image_sampler_t image_samplers[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; } _sg_gl_shader_stage_t; typedef struct { @@ -3856,8 +4401,8 @@ typedef struct { typedef _sg_gl_shader_t _sg_shader_t; typedef struct { - int8_t vb_index; /* -1 if attr is not enabled */ - int8_t divisor; /* -1 if not initialized */ + int8_t vb_index; // -1 if attr is not enabled + int8_t divisor; // -1 if not initialized uint8_t stride; uint8_t size; uint8_t normalized; @@ -3886,7 +4431,6 @@ typedef _sg_gl_pipeline_t _sg_pipeline_t; typedef struct { _sg_image_t* image; - GLuint gl_msaa_resolve_buffer; } _sg_gl_attachment_t; typedef struct { @@ -3895,7 +4439,9 @@ typedef struct { 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; + GLuint msaa_resolve_framebuffer[SG_MAX_COLOR_ATTACHMENTS]; } gl; } _sg_gl_pass_t; typedef _sg_gl_pass_t _sg_pass_t; @@ -3903,9 +4449,7 @@ typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; typedef struct { _sg_slot_t slot; - #if !defined(SOKOL_GLES2) GLuint vao; - #endif GLuint default_framebuffer; } _sg_gl_context_t; typedef _sg_gl_context_t _sg_context_t; @@ -3918,7 +4462,10 @@ typedef struct { typedef struct { GLenum target; GLuint texture; -} _sg_gl_texture_bind_slot; + 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; @@ -3937,8 +4484,8 @@ typedef struct { GLuint stored_vertex_buffer; GLuint stored_index_buffer; GLuint prog; - _sg_gl_texture_bind_slot textures[SG_MAX_SHADERSTAGE_IMAGES]; - _sg_gl_texture_bind_slot stored_texture; + _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; int cur_ib_offset; GLenum cur_primitive_type; GLenum cur_index_type; @@ -3949,7 +4496,6 @@ typedef struct { typedef struct { bool valid; - bool gles2; bool in_pass; int cur_pass_width; int cur_pass_height; @@ -3959,13 +4505,14 @@ typedef struct { _sg_gl_state_cache_t cache; bool ext_anisotropic; GLint max_anisotropy; - GLint max_combined_texture_image_units; + sg_store_action color_store_actions[SG_MAX_COLOR_ATTACHMENTS]; + sg_store_action depth_store_action; + sg_store_action stencil_store_action; #if _SOKOL_USE_WIN32_GL_LOADER HINSTANCE opengl32_dll; #endif } _sg_gl_backend_t; -/*== D3D11 BACKEND DECLARATIONS ==============================================*/ #elif defined(SOKOL_D3D11) typedef struct { @@ -3984,14 +4531,21 @@ typedef struct { DXGI_FORMAT format; ID3D11Texture2D* tex2d; ID3D11Texture3D* tex3d; - ID3D11Texture2D* texds; - ID3D11Texture2D* texmsaa; + ID3D11Resource* res; // either tex2d or tex3d ID3D11ShaderResourceView* srv; - ID3D11SamplerState* smp; } d3d11; } _sg_d3d11_image_t; typedef _sg_d3d11_image_t _sg_image_t; +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + ID3D11SamplerState* smp; + } d3d11; +} _sg_d3d11_sampler_t; +typedef _sg_d3d11_sampler_t _sg_sampler_t; + typedef struct { _sg_str_t sem_name; int sem_index; @@ -4021,7 +4575,7 @@ typedef struct { _sg_shader_t* shader; struct { UINT stencil_ref; - UINT vb_strides[SG_MAX_SHADERSTAGE_BUFFERS]; + UINT vb_strides[SG_MAX_VERTEX_BUFFERS]; D3D_PRIMITIVE_TOPOLOGY topology; DXGI_FORMAT index_format; ID3D11InputLayout* il; @@ -4034,20 +4588,19 @@ typedef _sg_d3d11_pipeline_t _sg_pipeline_t; typedef struct { _sg_image_t* image; - ID3D11RenderTargetView* rtv; -} _sg_d3d11_color_attachment_t; - -typedef struct { - _sg_image_t* image; - ID3D11DepthStencilView* dsv; -} _sg_d3d11_ds_attachment_t; + union { + ID3D11RenderTargetView* rtv; + ID3D11DepthStencilView* dsv; + } view; +} _sg_d3d11_attachment_t; typedef struct { _sg_slot_t slot; _sg_pass_common_t cmn; struct { - _sg_d3d11_color_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_d3d11_ds_attachment_t ds_att; + _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; } d3d11; } _sg_d3d11_pass_t; typedef _sg_d3d11_pass_t _sg_pass_t; @@ -4079,15 +4632,14 @@ typedef struct { sg_pipeline cur_pipeline_id; ID3D11RenderTargetView* cur_rtvs[SG_MAX_COLOR_ATTACHMENTS]; ID3D11DepthStencilView* cur_dsv; - /* on-demand loaded d3dcompiler_47.dll handles */ + // on-demand loaded d3dcompiler_47.dll handles HINSTANCE d3dcompiler_dll; bool d3dcompiler_dll_load_failed; pD3DCompile D3DCompile_func; - /* global subresourcedata array for texture updates */ + // global subresourcedata array for texture updates D3D11_SUBRESOURCE_DATA subres_data[SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS]; } _sg_d3d11_backend_t; -/*=== METAL BACKEND DECLARATIONS =============================================*/ #elif defined(SOKOL_METAL) #if defined(_SG_TARGET_MACOS) || defined(_SG_TARGET_IOS_SIMULATOR) @@ -4098,7 +4650,7 @@ typedef struct { #define _SG_MTL_INVALID_SLOT_INDEX (0) typedef struct { - uint32_t frame_index; /* frame index at which it is safe to release this resource */ + uint32_t frame_index; // frame index at which it is safe to release this resource int slot_index; } _sg_mtl_release_item_t; @@ -4116,7 +4668,7 @@ typedef struct { _sg_slot_t slot; _sg_buffer_common_t cmn; struct { - int buf[SG_NUM_INFLIGHT_FRAMES]; /* index into _sg_mtl_pool */ + int buf[SG_NUM_INFLIGHT_FRAMES]; // index into _sg_mtl_pool } mtl; } _sg_mtl_buffer_t; typedef _sg_mtl_buffer_t _sg_buffer_t; @@ -4126,13 +4678,19 @@ typedef struct { _sg_image_common_t cmn; struct { int tex[SG_NUM_INFLIGHT_FRAMES]; - int depth_tex; - int msaa_tex; - int sampler_state; } mtl; } _sg_mtl_image_t; typedef _sg_mtl_image_t _sg_image_t; +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + int sampler_state; + } mtl; +} _sg_mtl_sampler_t; +typedef _sg_mtl_sampler_t _sg_sampler_t; + typedef struct { int mtl_lib; int mtl_func; @@ -4173,6 +4731,7 @@ typedef struct { _sg_pass_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; } mtl; } _sg_mtl_pass_t; @@ -4184,24 +4743,30 @@ typedef struct { } _sg_mtl_context_t; typedef _sg_mtl_context_t _sg_context_t; -/* resouce binding state cache */ +// resouce 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_SHADERSTAGE_BUFFERS]; - int cur_vertexbuffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; - sg_buffer cur_vertexbuffer_ids[SG_MAX_SHADERSTAGE_BUFFERS]; + const _sg_buffer_t* cur_vertexbuffers[SG_MAX_VERTEX_BUFFERS]; + 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_mtl_state_cache_t; typedef struct { bool valid; + bool has_unified_memory; + bool force_managed_storage_mode; const void*(*renderpass_descriptor_cb)(void); const void*(*renderpass_descriptor_userdata_cb)(void*); const void*(*drawable_cb)(void); @@ -4217,7 +4782,6 @@ typedef struct { int cur_width; int cur_height; _sg_mtl_state_cache_t state_cache; - _sg_sampler_cache_t sampler_cache; _sg_mtl_idpool_t idpool; dispatch_semaphore_t sem; id device; @@ -4228,7 +4792,6 @@ typedef struct { id uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; } _sg_mtl_backend_t; -/*=== WGPU BACKEND DECLARATIONS ==============================================*/ #elif defined(SOKOL_WGPU) #define _SG_WGPU_STAGING_ALIGN (256) @@ -4306,33 +4869,33 @@ typedef struct { } _sg_wgpu_context_t; typedef _sg_wgpu_context_t _sg_context_t; -/* a pool of per-frame uniform buffers */ +// a pool of per-frame uniform buffers typedef struct { WGPUBindGroupLayout bindgroup_layout; uint32_t num_bytes; - uint32_t offset; /* current offset into current frame's mapped uniform buffer */ + uint32_t offset; // current offset into current frame's mapped uniform buffer uint32_t bind_offsets[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; - WGPUBuffer buf; /* the GPU-side uniform buffer */ + WGPUBuffer buf; // the GPU-side uniform buffer WGPUBindGroup bindgroup; struct { int num; int cur; - WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* CPU-side staging buffers */ - uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* if != 0, staging buffer currently mapped */ + WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; // CPU-side staging buffers + uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; // if != 0, staging buffer currently mapped } stage; } _sg_wgpu_ubpool_t; -/* ...a similar pool (like uniform buffer pool) of dynamic-resource staging buffers */ +// ...a similar pool (like uniform buffer pool) of dynamic-resource staging buffers typedef struct { uint32_t num_bytes; - uint32_t offset; /* current offset into current frame's staging buffer */ - int num; /* number of staging buffers */ - int cur; /* this frame's staging buffer */ - WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* CPU-side staging buffers */ - uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* if != 0, staging buffer currently mapped */ + uint32_t offset; // current offset into current frame's staging buffer + int num; // number of staging buffers + int cur; // this frame's staging buffer + WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; // CPU-side staging buffers + uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; // if != 0, staging buffer currently mapped } _sg_wgpu_stagingpool_t; -/* the WGPU backend state */ +// the WGPU backend state typedef struct { bool valid; bool in_pass; @@ -4354,15 +4917,14 @@ typedef struct { WGPUBindGroup empty_bind_group; const _sg_pipeline_t* cur_pipeline; sg_pipeline cur_pipeline_id; - _sg_sampler_cache_t sampler_cache; _sg_wgpu_ubpool_t ub; _sg_wgpu_stagingpool_t staging; } _sg_wgpu_backend_t; #endif -/*=== RESOURCE POOL DECLARATIONS =============================================*/ +// POOL STRUCTS -/* this *MUST* remain 0 */ +// this *MUST* remain 0 #define _SG_INVALID_SLOT_INDEX (0) typedef struct { @@ -4375,149 +4937,20 @@ typedef struct { typedef struct { _sg_pool_t buffer_pool; _sg_pool_t image_pool; + _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_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_pools_t; -/*=== VALIDATION LAYER DECLARATIONS ==========================================*/ -typedef enum { - /* special case 'validation was successful' */ - _SG_VALIDATE_SUCCESS, - - /* buffer creation */ - _SG_VALIDATE_BUFFERDESC_CANARY, - _SG_VALIDATE_BUFFERDESC_SIZE, - _SG_VALIDATE_BUFFERDESC_DATA, - _SG_VALIDATE_BUFFERDESC_DATA_SIZE, - _SG_VALIDATE_BUFFERDESC_NO_DATA, - - /* image data (for image creation and updating) */ - _SG_VALIDATE_IMAGEDATA_NODATA, - _SG_VALIDATE_IMAGEDATA_DATA_SIZE, - - /* image creation */ - _SG_VALIDATE_IMAGEDESC_CANARY, - _SG_VALIDATE_IMAGEDESC_WIDTH, - _SG_VALIDATE_IMAGEDESC_HEIGHT, - _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT, - _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, - _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, - _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, - _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE, - _SG_VALIDATE_IMAGEDESC_RT_NO_DATA, - _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA, - _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, - _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, - - /* shader creation */ - _SG_VALIDATE_SHADERDESC_CANARY, - _SG_VALIDATE_SHADERDESC_SOURCE, - _SG_VALIDATE_SHADERDESC_BYTECODE, - _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, - _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, - _SG_VALIDATE_SHADERDESC_NO_CONT_UBS, - _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS, - _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, - _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS, - _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME, - _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, - _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT, - _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, - _SG_VALIDATE_SHADERDESC_IMG_NAME, - _SG_VALIDATE_SHADERDESC_ATTR_NAMES, - _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS, - _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, - - /* pipeline creation */ - _SG_VALIDATE_PIPELINEDESC_CANARY, - _SG_VALIDATE_PIPELINEDESC_SHADER, - _SG_VALIDATE_PIPELINEDESC_NO_ATTRS, - _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, - _SG_VALIDATE_PIPELINEDESC_ATTR_NAME, - _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, - - /* pass creation */ - _SG_VALIDATE_PASSDESC_CANARY, - _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS, - _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS, - _SG_VALIDATE_PASSDESC_IMAGE, - _SG_VALIDATE_PASSDESC_MIPLEVEL, - _SG_VALIDATE_PASSDESC_FACE, - _SG_VALIDATE_PASSDESC_LAYER, - _SG_VALIDATE_PASSDESC_SLICE, - _SG_VALIDATE_PASSDESC_IMAGE_NO_RT, - _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT, - _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT, - _SG_VALIDATE_PASSDESC_IMAGE_SIZES, - _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS, - - /* sg_begin_pass validation */ - _SG_VALIDATE_BEGINPASS_PASS, - _SG_VALIDATE_BEGINPASS_IMAGE, - - /* sg_apply_pipeline validation */ - _SG_VALIDATE_APIP_PIPELINE_VALID_ID, - _SG_VALIDATE_APIP_PIPELINE_EXISTS, - _SG_VALIDATE_APIP_PIPELINE_VALID, - _SG_VALIDATE_APIP_SHADER_EXISTS, - _SG_VALIDATE_APIP_SHADER_VALID, - _SG_VALIDATE_APIP_ATT_COUNT, - _SG_VALIDATE_APIP_COLOR_FORMAT, - _SG_VALIDATE_APIP_DEPTH_FORMAT, - _SG_VALIDATE_APIP_SAMPLE_COUNT, - - /* sg_apply_bindings validation */ - _SG_VALIDATE_ABND_PIPELINE, - _SG_VALIDATE_ABND_PIPELINE_EXISTS, - _SG_VALIDATE_ABND_PIPELINE_VALID, - _SG_VALIDATE_ABND_VBS, - _SG_VALIDATE_ABND_VB_EXISTS, - _SG_VALIDATE_ABND_VB_TYPE, - _SG_VALIDATE_ABND_VB_OVERFLOW, - _SG_VALIDATE_ABND_NO_IB, - _SG_VALIDATE_ABND_IB, - _SG_VALIDATE_ABND_IB_EXISTS, - _SG_VALIDATE_ABND_IB_TYPE, - _SG_VALIDATE_ABND_IB_OVERFLOW, - _SG_VALIDATE_ABND_VS_IMGS, - _SG_VALIDATE_ABND_VS_IMG_EXISTS, - _SG_VALIDATE_ABND_VS_IMG_TYPES, - _SG_VALIDATE_ABND_FS_IMGS, - _SG_VALIDATE_ABND_FS_IMG_EXISTS, - _SG_VALIDATE_ABND_FS_IMG_TYPES, - - /* sg_apply_uniforms validation */ - _SG_VALIDATE_AUB_NO_PIPELINE, - _SG_VALIDATE_AUB_NO_UB_AT_SLOT, - _SG_VALIDATE_AUB_SIZE, - - /* sg_update_buffer validation */ - _SG_VALIDATE_UPDATEBUF_USAGE, - _SG_VALIDATE_UPDATEBUF_SIZE, - _SG_VALIDATE_UPDATEBUF_ONCE, - _SG_VALIDATE_UPDATEBUF_APPEND, - - /* sg_append_buffer validation */ - _SG_VALIDATE_APPENDBUF_USAGE, - _SG_VALIDATE_APPENDBUF_SIZE, - _SG_VALIDATE_APPENDBUF_UPDATE, - - /* sg_update_image validation */ - _SG_VALIDATE_UPDIMG_USAGE, - _SG_VALIDATE_UPDIMG_NOTENOUGHDATA, - _SG_VALIDATE_UPDIMG_ONCE -} _sg_validate_error_t; - -/*=== GENERIC BACKEND STATE ==================================================*/ - typedef struct { int num; // number of allocated commit listener items int upper; // the current upper index (no valid items past this point) @@ -4526,7 +4959,7 @@ typedef struct { typedef struct { bool valid; - sg_desc desc; /* original desc with default values patched in */ + sg_desc desc; // original desc with default values patched in uint32_t frame_index; sg_context active_context; sg_pass cur_pass; @@ -4535,7 +4968,7 @@ typedef struct { bool bindings_valid; bool next_draw_valid; #if defined(SOKOL_DEBUG) - _sg_validate_error_t validate_error; + sg_log_item validate_error; #endif _sg_pools_t pools; sg_backend backend; @@ -4558,7 +4991,53 @@ typedef struct { } _sg_state_t; static _sg_state_t _sg; -/*-- helper functions --------------------------------------------------------*/ +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SG_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sg_log_messages[] = { + _SG_LOG_ITEMS +}; +#undef _SG_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SG_PANIC(code) _sg_log(SG_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SG_ERROR(code) _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SG_WARN(code) _sg_log(SG_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SG_INFO(code) _sg_log(SG_LOGITEM_ ##code, 3, 0, __LINE__) +#define _SG_LOGMSG(code,msg) _sg_log(SG_LOGITEM_ ##code, 3, msg, __LINE__) +#define _SG_VALIDATE(cond,code) if (!(cond)){ _sg.validate_error = SG_LOGITEM_ ##code; _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__); } + +static void _sg_log(sg_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_sg.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _sg_log_messages[log_item]; + } + #endif + _sg.desc.logger.func("sg", log_level, log_item, msg, line_nr, filename, _sg.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory // a helper macro to clear a struct with potentially ARC'ed ObjC references #if defined(SOKOL_METAL) @@ -4581,11 +5060,12 @@ _SOKOL_PRIVATE void* _sg_malloc(size_t size) { void* ptr; if (_sg.desc.allocator.alloc) { ptr = _sg.desc.allocator.alloc(size, _sg.desc.allocator.user_data); - } - else { + } else { ptr = malloc(size); } - SOKOL_ASSERT(ptr); + if (0 == ptr) { + _SG_PANIC(MALLOC_FAILED); + } return ptr; } @@ -4598,22 +5078,10 @@ _SOKOL_PRIVATE void* _sg_malloc_clear(size_t size) { _SOKOL_PRIVATE void _sg_free(void* ptr) { if (_sg.desc.allocator.free) { _sg.desc.allocator.free(ptr, _sg.desc.allocator.user_data); - } - else { - free(ptr); - } -} - -#if defined(SOKOL_DEBUG) -_SOKOL_PRIVATE void _sg_log(const char* msg) { - SOKOL_ASSERT(msg); - if (_sg.desc.logger.log_cb) { - _sg.desc.logger.log_cb(msg, _sg.desc.logger.user_data); } else { - SOKOL_LOG(msg); + free(ptr); } } -#endif _SOKOL_PRIVATE bool _sg_strempty(const _sg_str_t* str) { return 0 == str->buf[0]; @@ -4632,18 +5100,23 @@ _SOKOL_PRIVATE void _sg_strcpy(_sg_str_t* dst, const char* src) { strncpy(dst->buf, src, _SG_STRING_SIZE); #endif dst->buf[_SG_STRING_SIZE-1] = 0; - } - else { + } else { _sg_clear(dst->buf, _SG_STRING_SIZE); } } +// ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>helpers _SOKOL_PRIVATE uint32_t _sg_align_u32(uint32_t val, uint32_t align) { SOKOL_ASSERT((align > 0) && ((align & (align - 1)) == 0)); return (val + (align - 1)) & ~(align - 1); } -/* return byte size of a vertex format */ _SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { switch (fmt) { case SG_VERTEXFORMAT_FLOAT: return 4; @@ -4673,8 +5146,7 @@ _SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { _SOKOL_PRIVATE uint32_t _sg_uniform_alignment(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { return 1; - } - else { + } else { SOKOL_ASSERT(array_count > 0); if (array_count == 1) { switch (type) { @@ -4695,8 +5167,7 @@ _SOKOL_PRIVATE uint32_t _sg_uniform_alignment(sg_uniform_type type, int array_co SOKOL_UNREACHABLE; return 1; } - } - else { + } else { return 16; } } @@ -4724,8 +5195,7 @@ _SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, SOKOL_UNREACHABLE; return 0; } - } - else { + } else { if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { switch (type) { case SG_UNIFORMTYPE_FLOAT: @@ -4746,8 +5216,7 @@ _SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, SOKOL_UNREACHABLE; return 0; } - } - else { + } else { switch (type) { case SG_UNIFORMTYPE_FLOAT: case SG_UNIFORMTYPE_FLOAT2: @@ -4768,7 +5237,6 @@ _SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, } } -/* return true if pixel format is a compressed format */ _SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_BC1_RGBA: @@ -4796,26 +5264,26 @@ _SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { } } -/* return true if pixel format is a valid render target format */ _SOKOL_PRIVATE bool _sg_is_valid_rendertarget_color_format(sg_pixel_format fmt) { const int fmt_index = (int) fmt; SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); return _sg.formats[fmt_index].render && !_sg.formats[fmt_index].depth; } -/* return true if pixel format is a valid depth format */ _SOKOL_PRIVATE bool _sg_is_valid_rendertarget_depth_format(sg_pixel_format fmt) { const int fmt_index = (int) fmt; SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); return _sg.formats[fmt_index].render && _sg.formats[fmt_index].depth; } -/* return true if pixel format is a depth-stencil format */ +_SOKOL_PRIVATE bool _sg_is_depth_or_depth_stencil_format(sg_pixel_format fmt) { + return (SG_PIXELFORMAT_DEPTH == fmt) || (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); +} + _SOKOL_PRIVATE bool _sg_is_depth_stencil_format(sg_pixel_format fmt) { return (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); } -/* return the bytes-per-pixel for a pixel format */ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_R8: @@ -4823,7 +5291,6 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { case SG_PIXELFORMAT_R8UI: case SG_PIXELFORMAT_R8SI: return 1; - case SG_PIXELFORMAT_R16: case SG_PIXELFORMAT_R16SN: case SG_PIXELFORMAT_R16UI: @@ -4834,7 +5301,6 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG8UI: case SG_PIXELFORMAT_RG8SI: return 2; - case SG_PIXELFORMAT_R32UI: case SG_PIXELFORMAT_R32SI: case SG_PIXELFORMAT_R32F: @@ -4853,7 +5319,6 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG11B10F: case SG_PIXELFORMAT_RGB9E5: return 4; - case SG_PIXELFORMAT_RG32UI: case SG_PIXELFORMAT_RG32SI: case SG_PIXELFORMAT_RG32F: @@ -4863,12 +5328,10 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { case SG_PIXELFORMAT_RGBA16SI: case SG_PIXELFORMAT_RGBA16F: return 8; - case SG_PIXELFORMAT_RGBA32UI: case SG_PIXELFORMAT_RGBA32SI: case SG_PIXELFORMAT_RGBA32F: return 16; - default: SOKOL_UNREACHABLE; return 0; @@ -4936,7 +5399,7 @@ _SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) return pitch; } -/* compute the number of rows in a surface depending on pixel format */ +// compute the number of rows in a surface depending on pixel format _SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { int num_rows; switch (fmt) { @@ -4979,6 +5442,11 @@ _SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { return num_rows; } +// return size of a mipmap level +_SOKOL_PRIVATE int _sg_miplevel_dim(int base_dim, int mip_level) { + return _sg_max(base_dim >> mip_level, 1); +} + /* return pitch of a 2D subimage / texture slice see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp */ @@ -4987,7 +5455,7 @@ _SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height, return num_rows * _sg_row_pitch(fmt, width, row_align); } -/* capability table pixel format helper functions */ +// capability table pixel format helper functions _SOKOL_PRIVATE void _sg_pixelformat_all(sg_pixelformat_info* pfi) { pfi->sample = true; pfi->filter = true; @@ -5049,30 +5517,44 @@ _SOKOL_PRIVATE void _sg_pixelformat_sfbr(sg_pixelformat_info* pfi) { pfi->render = true; } -/* resolve pass action defaults into a new pass action struct */ _SOKOL_PRIVATE void _sg_resolve_default_pass_action(const sg_pass_action* from, sg_pass_action* to) { SOKOL_ASSERT(from && to); *to = *from; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (to->colors[i].action == _SG_ACTION_DEFAULT) { - to->colors[i].action = SG_ACTION_CLEAR; - to->colors[i].value.r = SG_DEFAULT_CLEAR_RED; - to->colors[i].value.g = SG_DEFAULT_CLEAR_GREEN; - to->colors[i].value.b = SG_DEFAULT_CLEAR_BLUE; - to->colors[i].value.a = SG_DEFAULT_CLEAR_ALPHA; + 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 (to->colors[i].store_action == _SG_STOREACTION_DEFAULT) { + to->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 (to->depth.action == _SG_ACTION_DEFAULT) { - to->depth.action = SG_ACTION_CLEAR; - to->depth.value = SG_DEFAULT_CLEAR_DEPTH; + if (to->depth.store_action == _SG_STOREACTION_DEFAULT) { + to->depth.store_action = SG_STOREACTION_DONTCARE; } - if (to->stencil.action == _SG_ACTION_DEFAULT) { - to->stencil.action = SG_ACTION_CLEAR; - to->stencil.value = SG_DEFAULT_CLEAR_STENCIL; + if (to->stencil.load_action == _SG_LOADACTION_DEFAULT) { + to->stencil.load_action = SG_LOADACTION_CLEAR; + to->stencil.clear_value = SG_DEFAULT_CLEAR_STENCIL; + } + if (to->stencil.store_action == _SG_STOREACTION_DEFAULT) { + to->stencil.store_action = SG_STOREACTION_DONTCARE; } } -/*== DUMMY BACKEND IMPL ======================================================*/ +// ██████ ██ ██ ███ ███ ███ ███ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ████ ████ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ████ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ██ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>dummy backend #if defined(SOKOL_DUMMY_BACKEND) _SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { @@ -5091,11 +5573,11 @@ _SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { } _SOKOL_PRIVATE void _sg_dummy_discard_backend(void) { - /* empty */ + // empty } _SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) { - /* empty*/ + // empty } _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_context(_sg_context_t* ctx) { @@ -5116,7 +5598,8 @@ _SOKOL_PRIVATE void _sg_dummy_activate_context(_sg_context_t* ctx) { _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); - _sg_buffer_common_init(&buf->cmn, desc); + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(desc); return SG_RESOURCESTATE_VALID; } @@ -5127,7 +5610,8 @@ _SOKOL_PRIVATE void _sg_dummy_discard_buffer(_sg_buffer_t* buf) { _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); - _sg_image_common_init(&img->cmn, desc); + _SOKOL_UNUSED(img); + _SOKOL_UNUSED(desc); return SG_RESOURCESTATE_VALID; } @@ -5136,9 +5620,22 @@ _SOKOL_PRIVATE void _sg_dummy_discard_image(_sg_image_t* img) { _SOKOL_UNUSED(img); } +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + _SOKOL_UNUSED(smp); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _SOKOL_UNUSED(smp); +} + _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); - _sg_shader_common_init(&shd->cmn, desc); + _SOKOL_UNUSED(shd); + _SOKOL_UNUSED(desc); return SG_RESOURCESTATE_VALID; } @@ -5150,14 +5647,13 @@ _SOKOL_PRIVATE void _sg_dummy_discard_shader(_sg_shader_t* shd) { _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && desc); pip->shader = shd; - _sg_pipeline_common_init(&pip->cmn, desc); for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; } return SG_RESOURCESTATE_VALID; } @@ -5167,29 +5663,34 @@ _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** att_images, const sg_pass_desc* desc) { +_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_ASSERT(att_images && att_images[0]); + SOKOL_ASSERT(color_images && resolve_images); - _sg_pass_common_init(&pass->cmn, desc); - - const sg_pass_attachment_desc* att_desc; for (int i = 0; i < pass->cmn.num_color_atts; i++) { - att_desc = &desc->color_attachments[i]; - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + const sg_pass_attachment_desc* color_desc = &desc->color_attachments[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(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); - pass->dmy.color_atts[i].image = att_images[i]; + 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]; + + const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == pass->dmy.resolve_atts[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]; + } } SOKOL_ASSERT(0 == pass->dmy.ds_att.image); - att_desc = &desc->depth_stencil_attachment; - if (att_desc->image.id != SG_INVALID_ID) { - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; - SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); - pass->dmy.ds_att.image = att_images[ds_img_index]; + const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + 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; } return SG_RESOURCESTATE_VALID; } @@ -5201,12 +5702,15 @@ _SOKOL_PRIVATE void _sg_dummy_discard_pass(_sg_pass_t* pass) { _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)); - /* NOTE: may return null */ return pass->dmy.color_atts[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_pass_ds_image(const _sg_pass_t* pass) { - /* NOTE: may return null */ SOKOL_ASSERT(pass); return pass->dmy.ds_att.image; } @@ -5220,11 +5724,11 @@ _SOKOL_PRIVATE void _sg_dummy_begin_pass(_sg_pass_t* pass, const sg_pass_action* } _SOKOL_PRIVATE void _sg_dummy_end_pass(void) { - /* empty */ + // empty } _SOKOL_PRIVATE void _sg_dummy_commit(void) { - /* empty */ + // empty } _SOKOL_PRIVATE void _sg_dummy_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { @@ -5253,17 +5757,23 @@ _SOKOL_PRIVATE void _sg_dummy_apply_bindings( _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) + _sg_image_t** fs_imgs, int num_fs_imgs, + _sg_sampler_t** vs_smps, int num_vs_smps, + _sg_sampler_t** fs_smps, int num_fs_smps) { SOKOL_ASSERT(pip); SOKOL_ASSERT(vbs && vb_offsets); SOKOL_ASSERT(vs_imgs); SOKOL_ASSERT(fs_imgs); + SOKOL_ASSERT(vs_smps); + SOKOL_ASSERT(fs_smps); _SOKOL_UNUSED(pip); _SOKOL_UNUSED(vbs); _SOKOL_UNUSED(vb_offsets); _SOKOL_UNUSED(num_vbs); _SOKOL_UNUSED(ib); _SOKOL_UNUSED(ib_offset); _SOKOL_UNUSED(vs_imgs); _SOKOL_UNUSED(num_vs_imgs); _SOKOL_UNUSED(fs_imgs); _SOKOL_UNUSED(num_fs_imgs); + _SOKOL_UNUSED(vs_smps); _SOKOL_UNUSED(num_vs_smps); + _SOKOL_UNUSED(fs_smps); _SOKOL_UNUSED(num_fs_smps); } _SOKOL_PRIVATE void _sg_dummy_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { @@ -5294,7 +5804,7 @@ _SOKOL_PRIVATE int _sg_dummy_append_buffer(_sg_buffer_t* buf, const sg_range* da buf->cmn.active_slot = 0; } } - /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + // NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend return _sg_roundup((int)data->size, 4); } @@ -5306,10 +5816,16 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data } } -/*== GL BACKEND ==============================================================*/ +// ██████ ██████ ███████ ███ ██ ██████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██████ █████ ██ ██ ██ ██ ███ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ███████ ██ ████ ██████ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>opengl backend #elif defined(_SOKOL_ANY_GL) -/*=== OPTIONAL GL LOADER FOR WIN32 ===========================================*/ +// optional GL loader for win32 #if defined(_SOKOL_USE_WIN32_GL_LOADER) // X Macro list of GL function names and signatures @@ -5370,7 +5886,6 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels)) \ _SG_XMACRO(glCreateShader, GLuint, (GLenum type)) \ _SG_XMACRO(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels)) \ - _SG_XMACRO(glClearDepth, void, (GLdouble depth)) \ _SG_XMACRO(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \ _SG_XMACRO(glCreateProgram, GLuint, (void)) \ _SG_XMACRO(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ @@ -5386,7 +5901,6 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glDeleteVertexArrays, void, (GLsizei n, const GLuint * arrays)) \ _SG_XMACRO(glDepthMask, void, (GLboolean flag)) \ _SG_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \ - _SG_XMACRO(glClearStencil, void, (GLint s)) \ _SG_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ _SG_XMACRO(glGenRenderbuffers, void, (GLsizei n, GLuint * renderbuffers)) \ _SG_XMACRO(glBufferData, void, (GLenum target, GLsizeiptr size, const void * data, GLenum usage)) \ @@ -5398,10 +5912,9 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glStencilMask, void, (GLuint mask)) \ _SG_XMACRO(glAttachShader, void, (GLuint program, GLuint shader)) \ _SG_XMACRO(glGetError, GLenum, (void)) \ - _SG_XMACRO(glClearColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ _SG_XMACRO(glBlendColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ _SG_XMACRO(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) \ - _SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, GLfloat* params)) \ + _SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, const GLfloat* params)) \ _SG_XMACRO(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ _SG_XMACRO(glDepthFunc, void, (GLenum func)) \ _SG_XMACRO(glStencilOp , void, (GLenum fail, GLenum zfail, GLenum zpass)) \ @@ -5409,13 +5922,17 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glEnableVertexAttribArray, void, (GLuint index)) \ _SG_XMACRO(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \ _SG_XMACRO(glReadBuffer, void, (GLenum src)) \ - _SG_XMACRO(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * data)) \ - _SG_XMACRO(glClear, void, (GLbitfield mask)) \ _SG_XMACRO(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels)) \ _SG_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \ _SG_XMACRO(glFrontFace, void, (GLenum mode)) \ _SG_XMACRO(glCullFace, void, (GLenum mode)) \ - _SG_XMACRO(glPixelStorei, void, (GLenum pname, GLint param)) + _SG_XMACRO(glPixelStorei, void, (GLenum pname, GLint param)) \ + _SG_XMACRO(glBindSampler, void, (GLuint unit, GLuint sampler)) \ + _SG_XMACRO(glGenSamplers, void, (GLsizei n, GLuint* samplers)) \ + _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)) // generate GL function pointer typedefs #define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; @@ -5457,7 +5974,7 @@ _SOKOL_PRIVATE void _sg_gl_unload_opengl(void) { } #endif // _SOKOL_USE_WIN32_GL_LOADER -/*-- type translation --------------------------------------------------------*/ +//-- type translation ---------------------------------------------------------- _SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { switch (t) { case SG_BUFFERTYPE_VERTEXBUFFER: return GL_ARRAY_BUFFER; @@ -5470,10 +5987,8 @@ _SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t) { switch (t) { case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; - #if !defined(SOKOL_GLES2) case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; - #endif default: SOKOL_UNREACHABLE; return 0; } } @@ -5642,15 +6157,31 @@ _SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) { } } -_SOKOL_PRIVATE GLenum _sg_gl_filter(sg_filter f) { - switch (f) { - case SG_FILTER_NEAREST: return GL_NEAREST; - case SG_FILTER_LINEAR: return GL_LINEAR; - case SG_FILTER_NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; - case SG_FILTER_LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; - case SG_FILTER_LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; - default: SOKOL_UNREACHABLE; return 0; +_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; + } + } else { + SOKOL_UNREACHABLE; return (GLenum)0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_mag_filter(sg_filter mag_f) { + if (mag_f == SG_FILTER_NEAREST) { + return GL_NEAREST; + } else { + return GL_LINEAR; } } @@ -5716,16 +6247,14 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG32F: case SG_PIXELFORMAT_RGBA32F: return GL_FLOAT; - #if !defined(SOKOL_GLES2) case SG_PIXELFORMAT_RGB10A2: return GL_UNSIGNED_INT_2_10_10_10_REV; case SG_PIXELFORMAT_RG11B10F: return GL_UNSIGNED_INT_10F_11F_11F_REV; case SG_PIXELFORMAT_RGB9E5: return GL_UNSIGNED_INT_5_9_9_9_REV; - #endif case SG_PIXELFORMAT_DEPTH: - return GL_UNSIGNED_SHORT; + return GL_FLOAT; case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_UNSIGNED_INT_24_8; default: @@ -5741,39 +6270,28 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_R16SN: case SG_PIXELFORMAT_R16F: case SG_PIXELFORMAT_R32F: - #if defined(SOKOL_GLES2) - return GL_LUMINANCE; - #else - if (_sg.gl.gles2) { - return GL_LUMINANCE; - } - else { - return GL_RED; - } - #endif - #if !defined(SOKOL_GLES2) - case SG_PIXELFORMAT_R8UI: - case SG_PIXELFORMAT_R8SI: - case SG_PIXELFORMAT_R16UI: - case SG_PIXELFORMAT_R16SI: - case SG_PIXELFORMAT_R32UI: - case SG_PIXELFORMAT_R32SI: - return GL_RED_INTEGER; - case SG_PIXELFORMAT_RG8: - case SG_PIXELFORMAT_RG8SN: - case SG_PIXELFORMAT_RG16: - case SG_PIXELFORMAT_RG16SN: - case SG_PIXELFORMAT_RG16F: - case SG_PIXELFORMAT_RG32F: - return GL_RG; - case SG_PIXELFORMAT_RG8UI: - case SG_PIXELFORMAT_RG8SI: - case SG_PIXELFORMAT_RG16UI: - case SG_PIXELFORMAT_RG16SI: - case SG_PIXELFORMAT_RG32UI: - case SG_PIXELFORMAT_RG32SI: - return GL_RG_INTEGER; - #endif + return GL_RED; + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + return GL_RED_INTEGER; + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RG32F: + return GL_RG; + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RG32SI: + return GL_RG_INTEGER; case SG_PIXELFORMAT_RGBA8: case SG_PIXELFORMAT_SRGB8A8: case SG_PIXELFORMAT_RGBA8SN: @@ -5783,15 +6301,13 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_RGBA32F: case SG_PIXELFORMAT_RGB10A2: return GL_RGBA; - #if !defined(SOKOL_GLES2) - case SG_PIXELFORMAT_RGBA8UI: - case SG_PIXELFORMAT_RGBA8SI: - case SG_PIXELFORMAT_RGBA16UI: - case SG_PIXELFORMAT_RGBA16SI: - case SG_PIXELFORMAT_RGBA32UI: - case SG_PIXELFORMAT_RGBA32SI: - return GL_RGBA_INTEGER; - #endif + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_RGBA8SI: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_RGBA_INTEGER; case SG_PIXELFORMAT_RG11B10F: case SG_PIXELFORMAT_RGB9E5: return GL_RGB; @@ -5843,85 +6359,76 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { } _SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { - #if defined(SOKOL_GLES2) - return _sg_gl_teximage_format(fmt); - #else - if (_sg.gl.gles2) { - return _sg_gl_teximage_format(fmt); - } - else { - switch (fmt) { - case SG_PIXELFORMAT_R8: return GL_R8; - case SG_PIXELFORMAT_R8SN: return GL_R8_SNORM; - case SG_PIXELFORMAT_R8UI: return GL_R8UI; - case SG_PIXELFORMAT_R8SI: return GL_R8I; - #if !defined(SOKOL_GLES3) - case SG_PIXELFORMAT_R16: return GL_R16; - case SG_PIXELFORMAT_R16SN: return GL_R16_SNORM; - #endif - case SG_PIXELFORMAT_R16UI: return GL_R16UI; - case SG_PIXELFORMAT_R16SI: return GL_R16I; - case SG_PIXELFORMAT_R16F: return GL_R16F; - case SG_PIXELFORMAT_RG8: return GL_RG8; - case SG_PIXELFORMAT_RG8SN: return GL_RG8_SNORM; - case SG_PIXELFORMAT_RG8UI: return GL_RG8UI; - case SG_PIXELFORMAT_RG8SI: return GL_RG8I; - case SG_PIXELFORMAT_R32UI: return GL_R32UI; - case SG_PIXELFORMAT_R32SI: return GL_R32I; - case SG_PIXELFORMAT_R32F: return GL_R32F; - #if !defined(SOKOL_GLES3) - case SG_PIXELFORMAT_RG16: return GL_RG16; - case SG_PIXELFORMAT_RG16SN: return GL_RG16_SNORM; - #endif - case SG_PIXELFORMAT_RG16UI: return GL_RG16UI; - case SG_PIXELFORMAT_RG16SI: return GL_RG16I; - case SG_PIXELFORMAT_RG16F: return GL_RG16F; - case SG_PIXELFORMAT_RGBA8: return GL_RGBA8; - case SG_PIXELFORMAT_SRGB8A8: return GL_SRGB8_ALPHA8; - case SG_PIXELFORMAT_RGBA8SN: return GL_RGBA8_SNORM; - case SG_PIXELFORMAT_RGBA8UI: return GL_RGBA8UI; - case SG_PIXELFORMAT_RGBA8SI: return GL_RGBA8I; - case SG_PIXELFORMAT_RGB10A2: return GL_RGB10_A2; - case SG_PIXELFORMAT_RG11B10F: return GL_R11F_G11F_B10F; - case SG_PIXELFORMAT_RGB9E5: return GL_RGB9_E5; - case SG_PIXELFORMAT_RG32UI: return GL_RG32UI; - case SG_PIXELFORMAT_RG32SI: return GL_RG32I; - case SG_PIXELFORMAT_RG32F: return GL_RG32F; - #if !defined(SOKOL_GLES3) - case SG_PIXELFORMAT_RGBA16: return GL_RGBA16; - case SG_PIXELFORMAT_RGBA16SN: return GL_RGBA16_SNORM; - #endif - case SG_PIXELFORMAT_RGBA16UI: return GL_RGBA16UI; - case SG_PIXELFORMAT_RGBA16SI: return GL_RGBA16I; - case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F; - case SG_PIXELFORMAT_RGBA32UI: return GL_RGBA32UI; - case SG_PIXELFORMAT_RGBA32SI: return GL_RGBA32I; - case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F; - case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; - case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; - 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_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; - case SG_PIXELFORMAT_BC5_RGSN: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; - 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_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_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; - default: SOKOL_UNREACHABLE; return 0; - } + switch (fmt) { + case SG_PIXELFORMAT_R8: return GL_R8; + case SG_PIXELFORMAT_R8SN: return GL_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return GL_R8UI; + case SG_PIXELFORMAT_R8SI: return GL_R8I; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_R16: return GL_R16; + case SG_PIXELFORMAT_R16SN: return GL_R16_SNORM; + #endif + case SG_PIXELFORMAT_R16UI: return GL_R16UI; + case SG_PIXELFORMAT_R16SI: return GL_R16I; + case SG_PIXELFORMAT_R16F: return GL_R16F; + case SG_PIXELFORMAT_RG8: return GL_RG8; + case SG_PIXELFORMAT_RG8SN: return GL_RG8_SNORM; + case SG_PIXELFORMAT_RG8UI: return GL_RG8UI; + case SG_PIXELFORMAT_RG8SI: return GL_RG8I; + case SG_PIXELFORMAT_R32UI: return GL_R32UI; + case SG_PIXELFORMAT_R32SI: return GL_R32I; + case SG_PIXELFORMAT_R32F: return GL_R32F; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RG16: return GL_RG16; + case SG_PIXELFORMAT_RG16SN: return GL_RG16_SNORM; + #endif + case SG_PIXELFORMAT_RG16UI: return GL_RG16UI; + case SG_PIXELFORMAT_RG16SI: return GL_RG16I; + case SG_PIXELFORMAT_RG16F: return GL_RG16F; + case SG_PIXELFORMAT_RGBA8: return GL_RGBA8; + case SG_PIXELFORMAT_SRGB8A8: return GL_SRGB8_ALPHA8; + case SG_PIXELFORMAT_RGBA8SN: return GL_RGBA8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: return GL_RGBA8UI; + case SG_PIXELFORMAT_RGBA8SI: return GL_RGBA8I; + case SG_PIXELFORMAT_RGB10A2: return GL_RGB10_A2; + case SG_PIXELFORMAT_RG11B10F: return GL_R11F_G11F_B10F; + case SG_PIXELFORMAT_RGB9E5: return GL_RGB9_E5; + case SG_PIXELFORMAT_RG32UI: return GL_RG32UI; + case SG_PIXELFORMAT_RG32SI: return GL_RG32I; + case SG_PIXELFORMAT_RG32F: return GL_RG32F; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RGBA16: return GL_RGBA16; + case SG_PIXELFORMAT_RGBA16SN: return GL_RGBA16_SNORM; + #endif + case SG_PIXELFORMAT_RGBA16UI: return GL_RGBA16UI; + case SG_PIXELFORMAT_RGBA16SI: return GL_RGBA16I; + case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F; + case SG_PIXELFORMAT_RGBA32UI: return GL_RGBA32UI; + case SG_PIXELFORMAT_RGBA32SI: return GL_RGBA32I; + case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F; + case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT32F; + case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; + 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_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; + case SG_PIXELFORMAT_BC5_RGSN: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + 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_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_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; + default: SOKOL_UNREACHABLE; return 0; } - #endif } _SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { @@ -5936,204 +6443,96 @@ _SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { } } -_SOKOL_PRIVATE GLenum _sg_gl_depth_attachment_format(sg_pixel_format fmt) { - switch (fmt) { - case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; - case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; - default: SOKOL_UNREACHABLE; return 0; - } -} - -/* see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml */ +// see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml _SOKOL_PRIVATE void _sg_gl_init_pixelformats(bool has_bgra) { - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8]); - } - #else - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); #endif - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); - #if !defined(SOKOL_GLES3) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); - _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); - #if !defined(SOKOL_GLES3) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); - } + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); - } - #endif + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); if (has_bgra) { _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); } - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); - #if !defined(SOKOL_GLES3) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); - } + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); #endif - // FIXME: WEBGL_depth_texture extension? + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); } -/* FIXME: OES_half_float_blend */ -_SOKOL_PRIVATE void _sg_gl_init_pixelformats_half_float(bool has_colorbuffer_half_float, bool has_texture_half_float_linear) { - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - if (has_texture_half_float_linear) { - if (has_colorbuffer_half_float) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - } - else { - if (has_colorbuffer_half_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - } - } - else { - #endif - /* GLES2 can only render to RGBA, and there's no RG format */ - if (has_texture_half_float_linear) { - if (has_colorbuffer_half_float) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); - } - else { - if (has_colorbuffer_half_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R16F]); - } - #if !defined(SOKOL_GLES2) +// FIXME: OES_half_float_blend +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_half_float(bool has_colorbuffer_half_float) { + if (has_colorbuffer_half_float) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); } - #endif } _SOKOL_PRIVATE void _sg_gl_init_pixelformats_float(bool has_colorbuffer_float, bool has_texture_float_linear, bool has_float_blend) { - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - if (has_texture_float_linear) { - if (has_colorbuffer_float) { - if (has_float_blend) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - else { - if (has_colorbuffer_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - } - else { - #endif - /* GLES2 can only render to RGBA, and there's no RG format */ - if (has_texture_float_linear) { - if (has_colorbuffer_float) { - if (has_float_blend) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + if (has_texture_float_linear) { + if (has_colorbuffer_float) { + if (has_float_blend) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); } + } else { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); } - else { - if (has_colorbuffer_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } + } else { + if (has_colorbuffer_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); } - #if !defined(SOKOL_GLES2) } - #endif } _SOKOL_PRIVATE void _sg_gl_init_pixelformats_s3tc(void) { @@ -6189,27 +6588,22 @@ _SOKOL_PRIVATE void _sg_gl_init_limits(void) { glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &gl_int); _SG_GL_CHECK_ERROR(); _sg.limits.gl_max_vertex_uniform_vectors = gl_int; - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int); - _SG_GL_CHECK_ERROR(); - _sg.limits.max_image_size_3d = gl_int; - glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gl_int); - _SG_GL_CHECK_ERROR(); - _sg.limits.max_image_array_layers = gl_int; - } - #endif + glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_3d = gl_int; + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_array_layers = gl_int; if (_sg.gl.ext_anisotropic) { glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_int); _SG_GL_CHECK_ERROR(); _sg.gl.max_anisotropy = gl_int; - } - else { + } else { _sg.gl.max_anisotropy = 1; } glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gl_int); _SG_GL_CHECK_ERROR(); - _sg.gl.max_combined_texture_image_units = gl_int; + _sg.limits.gl_max_combined_texture_image_units = gl_int; } #if defined(SOKOL_GLCORE33) @@ -6217,19 +6611,14 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { _sg.backend = SG_BACKEND_GLCORE33; _sg.features.origin_top_left = false; - _sg.features.instancing = true; - _sg.features.multiple_render_targets = true; - _sg.features.msaa_render_targets = true; - _sg.features.imagetype_3d = true; - _sg.features.imagetype_array = true; _sg.features.image_clamp_to_border = true; _sg.features.mrt_independent_blend_state = false; _sg.features.mrt_independent_write_mask = true; - /* scan extensions */ - bool has_s3tc = false; /* BC1..BC3 */ - bool has_rgtc = false; /* BC4 and BC5 */ - bool has_bptc = false; /* BC6H and BC7 */ + // scan extensions + bool has_s3tc = false; // BC1..BC3 + bool has_rgtc = false; // BC4 and BC5 + bool has_bptc = false; // BC6H and BC7 bool has_pvrtc = false; bool has_etc2 = false; GLint num_ext = 0; @@ -6239,38 +6628,32 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { if (ext) { if (strstr(ext, "_texture_compression_s3tc")) { has_s3tc = true; - } - else if (strstr(ext, "_texture_compression_rgtc")) { + } else if (strstr(ext, "_texture_compression_rgtc")) { has_rgtc = true; - } - else if (strstr(ext, "_texture_compression_bptc")) { + } else if (strstr(ext, "_texture_compression_bptc")) { has_bptc = true; - } - else if (strstr(ext, "_texture_compression_pvrtc")) { + } else if (strstr(ext, "_texture_compression_pvrtc")) { has_pvrtc = true; - } - else if (strstr(ext, "_ES3_compatibility")) { + } else if (strstr(ext, "_ES3_compatibility")) { has_etc2 = true; - } - else if (strstr(ext, "_texture_filter_anisotropic")) { + } else if (strstr(ext, "_texture_filter_anisotropic")) { _sg.gl.ext_anisotropic = true; } } } - /* limits */ + // limits _sg_gl_init_limits(); - /* pixel formats */ - const bool has_bgra = false; /* not a bug */ + // pixel formats + const bool has_bgra = false; // not a bug const bool has_colorbuffer_float = true; const bool has_colorbuffer_half_float = true; - const bool has_texture_float_linear = true; /* FIXME??? */ - const bool has_texture_half_float_linear = true; + const bool has_texture_float_linear = true; // FIXME??? const bool has_float_blend = true; _sg_gl_init_pixelformats(has_bgra); _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); - _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float); if (has_s3tc) { _sg_gl_init_pixelformats_s3tc(); } @@ -6294,18 +6677,13 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { _sg.backend = SG_BACKEND_GLES3; _sg.features.origin_top_left = false; - _sg.features.instancing = true; - _sg.features.multiple_render_targets = true; - _sg.features.msaa_render_targets = true; - _sg.features.imagetype_3d = true; - _sg.features.imagetype_array = true; _sg.features.image_clamp_to_border = false; _sg.features.mrt_independent_blend_state = false; _sg.features.mrt_independent_write_mask = false; - bool has_s3tc = false; /* BC1..BC3 */ - bool has_rgtc = false; /* BC4 and BC5 */ - bool has_bptc = false; /* BC6H and BC7 */ + bool has_s3tc = false; // BC1..BC3 + bool has_rgtc = false; // BC4 and BC5 + bool has_bptc = false; // BC6H and BC7 bool has_pvrtc = false; #if defined(__EMSCRIPTEN__) bool has_etc2 = false; @@ -6323,38 +6701,27 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { if (ext) { if (strstr(ext, "_texture_compression_s3tc")) { has_s3tc = true; - } - else if (strstr(ext, "_compressed_texture_s3tc")) { + } else if (strstr(ext, "_compressed_texture_s3tc")) { has_s3tc = true; - } - else if (strstr(ext, "_texture_compression_rgtc")) { + } else if (strstr(ext, "_texture_compression_rgtc")) { has_rgtc = true; - } - else if (strstr(ext, "_texture_compression_bptc")) { + } else if (strstr(ext, "_texture_compression_bptc")) { has_bptc = true; - } - else if (strstr(ext, "_texture_compression_pvrtc")) { + } else if (strstr(ext, "_texture_compression_pvrtc")) { has_pvrtc = true; - } - else if (strstr(ext, "_compressed_texture_pvrtc")) { + } else if (strstr(ext, "_compressed_texture_pvrtc")) { has_pvrtc = true; - } - else if (strstr(ext, "_compressed_texture_etc")) { + } else if (strstr(ext, "_compressed_texture_etc")) { has_etc2 = true; - } - else if (strstr(ext, "_color_buffer_float")) { + } else if (strstr(ext, "_color_buffer_float")) { has_colorbuffer_float = true; - } - else if (strstr(ext, "_color_buffer_half_float")) { + } else if (strstr(ext, "_color_buffer_half_float")) { has_colorbuffer_half_float = true; - } - else if (strstr(ext, "_texture_float_linear")) { + } else if (strstr(ext, "_texture_float_linear")) { has_texture_float_linear = true; - } - else if (strstr(ext, "_float_blend")) { + } else if (strstr(ext, "_float_blend")) { has_float_blend = true; - } - else if (strstr(ext, "_texture_filter_anisotropic")) { + } else if (strstr(ext, "_texture_filter_anisotropic")) { _sg.gl.ext_anisotropic = true; } } @@ -6364,97 +6731,19 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { see: https://developer.mozilla.org/en-US/docs/Web/API/EXT_color_buffer_float */ #if defined(__EMSCRIPTEN__) - has_colorbuffer_half_float = has_colorbuffer_float; - #endif - - /* limits */ - _sg_gl_init_limits(); - - /* pixel formats */ - const bool has_texture_half_float_linear = true; - const bool has_bgra = false; /* not a bug */ - _sg_gl_init_pixelformats(has_bgra); - _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); - _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); - if (has_s3tc) { - _sg_gl_init_pixelformats_s3tc(); - } - if (has_rgtc) { - _sg_gl_init_pixelformats_rgtc(); + if (!has_colorbuffer_half_float && has_colorbuffer_float) { + has_colorbuffer_half_float = has_colorbuffer_float; } - if (has_bptc) { - _sg_gl_init_pixelformats_bptc(); - } - if (has_pvrtc) { - _sg_gl_init_pixelformats_pvrtc(); - } - if (has_etc2) { - _sg_gl_init_pixelformats_etc2(); - } -} -#endif - -#if defined(SOKOL_GLES3) || defined(SOKOL_GLES2) -_SOKOL_PRIVATE void _sg_gl_init_caps_gles2(void) { - _sg.backend = SG_BACKEND_GLES2; - - bool has_s3tc = false; /* BC1..BC3 */ - bool has_rgtc = false; /* BC4 and BC5 */ - bool has_bptc = false; /* BC6H and BC7 */ - bool has_pvrtc = false; - bool has_etc2 = false; - bool has_texture_float = false; - bool has_texture_float_linear = false; - bool has_colorbuffer_float = false; - bool has_float_blend = false; - bool has_instancing = false; - const char* ext = (const char*) glGetString(GL_EXTENSIONS); - if (ext) { - has_s3tc = strstr(ext, "_texture_compression_s3tc") || strstr(ext, "_compressed_texture_s3tc"); - has_rgtc = strstr(ext, "_texture_compression_rgtc"); - has_bptc = strstr(ext, "_texture_compression_bptc"); - has_pvrtc = strstr(ext, "_texture_compression_pvrtc") || strstr(ext, "_compressed_texture_pvrtc"); - has_etc2 = strstr(ext, "_compressed_texture_etc"); - has_texture_float = strstr(ext, "_texture_float"); - has_texture_float_linear = strstr(ext, "_texture_float_linear"); - has_colorbuffer_float = strstr(ext, "_color_buffer_float"); - has_float_blend = strstr(ext, "_float_blend"); - /* don't bother with half_float support on WebGL1 - has_texture_half_float = strstr(ext, "_texture_half_float"); - has_texture_half_float_linear = strstr(ext, "_texture_half_float_linear"); - has_colorbuffer_half_float = strstr(ext, "_color_buffer_half_float"); - */ - has_instancing = strstr(ext, "_instanced_arrays"); - _sg.gl.ext_anisotropic = strstr(ext, "ext_anisotropic"); - } - - _sg.features.origin_top_left = false; - #if defined(_SOKOL_GL_INSTANCING_ENABLED) - _sg.features.instancing = has_instancing; #endif - _sg.features.multiple_render_targets = false; - _sg.features.msaa_render_targets = false; - _sg.features.imagetype_3d = false; - _sg.features.imagetype_array = false; - _sg.features.image_clamp_to_border = false; - _sg.features.mrt_independent_blend_state = false; - _sg.features.mrt_independent_write_mask = false; - /* limits */ + // limits _sg_gl_init_limits(); - /* pixel formats */ - const bool has_bgra = false; /* not a bug */ - const bool has_texture_half_float = false; - const bool has_texture_half_float_linear = false; - const bool has_colorbuffer_half_float = false; + // pixel formats + const bool has_bgra = false; // not a bug _sg_gl_init_pixelformats(has_bgra); - if (has_texture_float) { - _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); - } - if (has_texture_half_float) { - _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); - } + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float); if (has_s3tc) { _sg_gl_init_pixelformats_s3tc(); } @@ -6470,14 +6759,10 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles2(void) { if (has_etc2) { _sg_gl_init_pixelformats_etc2(); } - /* GLES2 doesn't allow multi-sampled render targets at all */ - for (int i = 0; i < _SG_PIXELFORMAT_NUM; i++) { - _sg.formats[i].msaa = false; - } } #endif -/*-- state cache implementation ----------------------------------------------*/ +//-- state cache implementation ------------------------------------------------ _SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { if (force || (_sg.gl.cache.vertex_buffer != 0)) { glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -6496,8 +6781,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { _sg.gl.cache.vertex_buffer = buffer; glBindBuffer(target, buffer); } - } - else { + } else { if (_sg.gl.cache.index_buffer != buffer) { _sg.gl.cache.index_buffer = buffer; glBindBuffer(target, buffer); @@ -6508,8 +6792,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { _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 { _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer; } } @@ -6517,21 +6800,20 @@ _SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) { _SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { if (target == GL_ARRAY_BUFFER) { if (_sg.gl.cache.stored_vertex_buffer != 0) { - /* we only care restoring valid ids */ + // we only care about restoring valid ids _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); _sg.gl.cache.stored_vertex_buffer = 0; } - } - else { + } else { if (_sg.gl.cache.stored_index_buffer != 0) { - /* we only care restoring valid ids */ + // 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; } } } -/* called when _sg_gl_deinit_buffer() */ +// called when _sg_gl_discard_buffer() _SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { if (buf == _sg.gl.cache.vertex_buffer) { _sg.gl.cache.vertex_buffer = 0; @@ -6555,92 +6837,109 @@ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { } _SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) { + _SG_GL_CHECK_ERROR(); if (_sg.gl.cache.cur_active_texture != texture) { _sg.gl.cache.cur_active_texture = texture; glActiveTexture(texture); } + _SG_GL_CHECK_ERROR(); } -_SOKOL_PRIVATE void _sg_gl_cache_clear_texture_bindings(bool force) { - for (int i = 0; (i < SG_MAX_SHADERSTAGE_IMAGES) && (i < _sg.gl.max_combined_texture_image_units); i++) { - if (force || (_sg.gl.cache.textures[i].texture != 0)) { - GLenum gl_texture_slot = (GLenum) (GL_TEXTURE0 + i); - glActiveTexture(gl_texture_slot); +_SOKOL_PRIVATE void _sg_gl_cache_clear_texture_sampler_bindings(bool force) { + _SG_GL_CHECK_ERROR(); + for (int i = 0; (i < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE) && (i < _sg.limits.gl_max_combined_texture_image_units); i++) { + if (force || (_sg.gl.cache.texture_samplers[i].texture != 0)) { + GLenum gl_texture_unit = (GLenum) (GL_TEXTURE0 + i); + glActiveTexture(gl_texture_unit); glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glBindTexture(GL_TEXTURE_3D, 0); - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); - } - #endif - _sg.gl.cache.textures[i].target = 0; - _sg.gl.cache.textures[i].texture = 0; - _sg.gl.cache.cur_active_texture = gl_texture_slot; + glBindTexture(GL_TEXTURE_3D, 0); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + glBindSampler((GLuint)i, 0); + _sg.gl.cache.texture_samplers[i].target = 0; + _sg.gl.cache.texture_samplers[i].texture = 0; + _sg.gl.cache.texture_samplers[i].sampler = 0; + _sg.gl.cache.cur_active_texture = gl_texture_unit; } } + _SG_GL_CHECK_ERROR(); } -_SOKOL_PRIVATE void _sg_gl_cache_bind_texture(int slot_index, GLenum target, GLuint texture) { +_SOKOL_PRIVATE void _sg_gl_cache_bind_texture_sampler(int slot_index, GLenum target, GLuint texture, GLuint sampler) { /* it's valid to call this function with target=0 and/or texture=0 target=0 will unbind the previous binding, texture=0 will clear the new binding */ - SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); - if (slot_index >= _sg.gl.max_combined_texture_image_units) { + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + if (slot_index >= _sg.limits.gl_max_combined_texture_image_units) { return; } - _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[slot_index]; - if ((slot->target != target) || (slot->texture != texture)) { + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[slot_index]; + if ((slot->target != target) || (slot->texture != texture) || (slot->sampler != sampler)) { _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + slot_index)); - /* if the target has changed, clear the previous binding on that target */ + // if the target has changed, clear the previous binding on that target if ((target != slot->target) && (slot->target != 0)) { glBindTexture(slot->target, 0); + _SG_GL_CHECK_ERROR(); } - /* apply new binding (texture can be 0 to unbind) */ + // apply new binding (can be 0 to unbind) if (target != 0) { glBindTexture(target, texture); + _SG_GL_CHECK_ERROR(); } + // apply new sampler (can be 0 to unbind) + glBindSampler((GLuint)slot_index, sampler); + _SG_GL_CHECK_ERROR(); + slot->target = target; slot->texture = texture; + slot->sampler = sampler; } } -_SOKOL_PRIVATE void _sg_gl_cache_store_texture_binding(int slot_index) { - SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); - _sg.gl.cache.stored_texture = _sg.gl.cache.textures[slot_index]; +_SOKOL_PRIVATE void _sg_gl_cache_store_texture_sampler_binding(int slot_index) { + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + _sg.gl.cache.stored_texture_sampler = _sg.gl.cache.texture_samplers[slot_index]; } -_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_binding(int slot_index) { - SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); - _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.stored_texture; +_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_sampler_binding(int slot_index) { + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.stored_texture_sampler; if (slot->texture != 0) { - /* we only care restoring valid ids */ + // we only care about restoring valid ids SOKOL_ASSERT(slot->target != 0); - _sg_gl_cache_bind_texture(slot_index, slot->target, slot->texture); + _sg_gl_cache_bind_texture_sampler(slot_index, slot->target, slot->texture, slot->sampler); slot->target = 0; slot->texture = 0; + slot->sampler = 0; } } -/* called from _sg_gl_destroy_texture() */ -_SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture(GLuint tex) { - for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[i]; - if (tex == slot->texture) { +// called from _sg_gl_discard_texture() and _sg_gl_discard_sampler() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture_sampler(GLuint tex, GLuint smp) { + _SG_GL_CHECK_ERROR(); + for (int i = 0; i < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE; i++) { + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[i]; + if ((0 != slot->target) && ((tex == slot->texture) || (smp == slot->sampler))) { _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + i)); glBindTexture(slot->target, 0); + _SG_GL_CHECK_ERROR(); + glBindSampler((GLuint)i, 0); + _SG_GL_CHECK_ERROR(); slot->target = 0; slot->texture = 0; + slot->sampler = 0; } } - if (tex == _sg.gl.cache.stored_texture.texture) { - _sg.gl.cache.stored_texture.target = 0; - _sg.gl.cache.stored_texture.texture = 0; + if ((tex == _sg.gl.cache.stored_texture_sampler.texture) || (smp == _sg.gl.cache.stored_texture_sampler.sampler)) { + _sg.gl.cache.stored_texture_sampler.target = 0; + _sg.gl.cache.stored_texture_sampler.texture = 0; + _sg.gl.cache.stored_texture_sampler.sampler = 0; } } -/* called from _sg_gl_discard_shader() */ +// called from _sg_gl_discard_shader() _SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { if (prog == _sg.gl.cache.prog) { _sg.gl.cache.prog = 0; @@ -6648,7 +6947,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { } } -/* called from _sg_gl_discard_pipeline() */ +// called from _sg_gl_discard_pipeline() _SOKOL_PRIVATE void _sg_gl_cache_invalidate_pipeline(_sg_pipeline_t* pip) { if (pip == _sg.gl.cache.cur_pipeline) { _sg.gl.cache.cur_pipeline = 0; @@ -6659,16 +6958,12 @@ _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(); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glBindVertexArray(_sg.gl.cur_context->vao); - _SG_GL_CHECK_ERROR(); - } - #endif + 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_bindings(true); + _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; @@ -6679,11 +6974,11 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { } _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; - /* shader program */ + // shader program glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog); _SG_GL_CHECK_ERROR(); - /* depth and stencil state */ + // 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; @@ -6701,7 +6996,7 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilMask(0); - /* blend state */ + // 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; @@ -6713,7 +7008,7 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); - /* standalone state */ + // standalone state for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; } @@ -6738,34 +7033,22 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { } _SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { - /* assumes that _sg.gl is already zero-initialized */ - _sg.gl.valid = true; - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - _sg.gl.gles2 = desc->context.gl.force_gles2; - #else _SOKOL_UNUSED(desc); - _sg.gl.gles2 = false; - #endif + // assumes that _sg.gl is already zero-initialized + _sg.gl.valid = true; #if defined(_SOKOL_USE_WIN32_GL_LOADER) _sg_gl_load_opengl(); #endif - /* clear initial GL error state */ + // clear initial GL error state #if defined(SOKOL_DEBUG) while (glGetError() != GL_NO_ERROR); #endif #if defined(SOKOL_GLCORE33) _sg_gl_init_caps_glcore33(); #elif defined(SOKOL_GLES3) - if (_sg.gl.gles2) { - _sg_gl_init_caps_gles2(); - } - else { - _sg_gl_init_caps_gles3(); - } - #else - _sg_gl_init_caps_gles2(); + _sg_gl_init_caps_gles3(); #endif } @@ -6779,26 +7062,22 @@ _SOKOL_PRIVATE void _sg_gl_discard_backend(void) { _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 */ + // 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 ----------------------------*/ +//-- 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(); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - SOKOL_ASSERT(0 == ctx->vao); - glGenVertexArrays(1, &ctx->vao); - glBindVertexArray(ctx->vao); - _SG_GL_CHECK_ERROR(); - } - #endif + SOKOL_ASSERT(0 == ctx->vao); + glGenVertexArrays(1, &ctx->vao); + glBindVertexArray(ctx->vao); + _SG_GL_CHECK_ERROR(); // incoming texture data is generally expected to be packed tightly glPixelStorei(GL_UNPACK_ALIGNMENT, 1); return SG_RESOURCESTATE_VALID; @@ -6806,32 +7085,24 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_context(_sg_context_t* ctx) { _SOKOL_PRIVATE void _sg_gl_discard_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - if (ctx->vao) { - glDeleteVertexArrays(1, &ctx->vao); - } - _SG_GL_CHECK_ERROR(); + if (ctx->vao) { + glDeleteVertexArrays(1, &ctx->vao); } - #else - _SOKOL_UNUSED(ctx); - #endif + _SG_GL_CHECK_ERROR(); } _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(); - _sg_buffer_common_init(&buf->cmn, desc); - buf->gl.ext_buffers = (0 != desc->gl_buffers[0]); - GLenum gl_target = _sg_gl_buffer_target(buf->cmn.type); - GLenum gl_usage = _sg_gl_usage(buf->cmn.usage); + buf->gl.injected = (0 != desc->gl_buffers[0]); + const GLenum gl_target = _sg_gl_buffer_target(buf->cmn.type); + const GLenum gl_usage = _sg_gl_usage(buf->cmn.usage); for (int slot = 0; slot < buf->cmn.num_slots; slot++) { GLuint gl_buf = 0; - if (buf->gl.ext_buffers) { + if (buf->gl.injected) { SOKOL_ASSERT(desc->gl_buffers[slot]); gl_buf = desc->gl_buffers[slot]; - } - else { + } else { glGenBuffers(1, &gl_buf); SOKOL_ASSERT(gl_buf); _sg_gl_cache_store_buffer_binding(gl_target); @@ -6855,7 +7126,7 @@ _SOKOL_PRIVATE void _sg_gl_discard_buffer(_sg_buffer_t* buf) { for (int slot = 0; slot < buf->cmn.num_slots; slot++) { if (buf->gl.buf[slot]) { _sg_gl_cache_invalidate_buffer(buf->gl.buf[slot]); - if (!buf->gl.ext_buffers) { + if (!buf->gl.injected) { glDeleteBuffers(1, &buf->gl.buf[slot]); } } @@ -6872,183 +7143,80 @@ _SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); _SG_GL_CHECK_ERROR(); - _sg_image_common_init(&img->cmn, desc); - img->gl.ext_textures = (0 != desc->gl_textures[0]); + img->gl.injected = (0 != desc->gl_textures[0]); - /* check if texture format is support */ + // check if texture format is support if (!_sg_gl_supported_texture_format(img->cmn.pixel_format)) { - SG_LOG("texture format not supported by GL context\n"); - return SG_RESOURCESTATE_FAILED; - } - /* check for optional texture types */ - if ((img->cmn.type == SG_IMAGETYPE_3D) && !_sg.features.imagetype_3d) { - SG_LOG("3D textures not supported by GL context\n"); - return SG_RESOURCESTATE_FAILED; - } - if ((img->cmn.type == SG_IMAGETYPE_ARRAY) && !_sg.features.imagetype_array) { - SG_LOG("array textures not supported by GL context\n"); + _SG_ERROR(GL_TEXTURE_FORMAT_NOT_SUPPORTED); return SG_RESOURCESTATE_FAILED; } + const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format); - #if !defined(SOKOL_GLES2) - bool msaa = false; - if (!_sg.gl.gles2) { - msaa = (img->cmn.sample_count > 1) && (_sg.features.msaa_render_targets); - } - #endif - - if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { - /* special case depth-stencil-buffer? */ - SOKOL_ASSERT((img->cmn.usage == SG_USAGE_IMMUTABLE) && (img->cmn.num_slots == 1)); - SOKOL_ASSERT(!img->gl.ext_textures); /* cannot provide external texture for depth images */ - glGenRenderbuffers(1, &img->gl.depth_render_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, img->gl.depth_render_buffer); - GLenum gl_depth_format = _sg_gl_depth_attachment_format(img->cmn.pixel_format); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && msaa) { - glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_depth_format, img->cmn.width, img->cmn.height); - } - else - #endif - { - glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, img->cmn.width, img->cmn.height); - } - } - else { - /* regular color texture */ + // if this is a MSAA render target, a render buffer object will be created instead of a regulat texture + // (since GLES3 has no multisampled texture objects) + if (img->cmn.render_target && (img->cmn.sample_count > 1)) { + glGenRenderbuffers(1, &img->gl.msaa_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, img->gl.msaa_render_buffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_internal_format, img->cmn.width, img->cmn.height); + } else if (img->gl.injected) { img->gl.target = _sg_gl_texture_target(img->cmn.type); - const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format); - - /* if this is a MSAA render target, need to create a separate render buffer */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && img->cmn.render_target && msaa) { - glGenRenderbuffers(1, &img->gl.msaa_render_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, img->gl.msaa_render_buffer); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_internal_format, img->cmn.width, img->cmn.height); + // inject externally GL textures + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + SOKOL_ASSERT(desc->gl_textures[slot]); + img->gl.tex[slot] = desc->gl_textures[slot]; } - #endif - - if (img->gl.ext_textures) { - /* inject externally GL textures */ - for (int slot = 0; slot < img->cmn.num_slots; slot++) { - SOKOL_ASSERT(desc->gl_textures[slot]); - img->gl.tex[slot] = desc->gl_textures[slot]; - } - if (desc->gl_texture_target) { - img->gl.target = (GLenum)desc->gl_texture_target; - } + if (desc->gl_texture_target) { + img->gl.target = (GLenum)desc->gl_texture_target; } - else { - /* create our own GL texture(s) */ - const GLenum gl_format = _sg_gl_teximage_format(img->cmn.pixel_format); - const bool is_compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format); - for (int slot = 0; slot < img->cmn.num_slots; slot++) { - glGenTextures(1, &img->gl.tex[slot]); - SOKOL_ASSERT(img->gl.tex[slot]); - _sg_gl_cache_store_texture_binding(0); - _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[slot]); - GLenum gl_min_filter = _sg_gl_filter(img->cmn.min_filter); - GLenum gl_mag_filter = _sg_gl_filter(img->cmn.mag_filter); - glTexParameteri(img->gl.target, GL_TEXTURE_MIN_FILTER, (GLint)gl_min_filter); - glTexParameteri(img->gl.target, GL_TEXTURE_MAG_FILTER, (GLint)gl_mag_filter); - if (_sg.gl.ext_anisotropic && (img->cmn.max_anisotropy > 1)) { - GLint max_aniso = (GLint) img->cmn.max_anisotropy; - if (max_aniso > _sg.gl.max_anisotropy) { - max_aniso = _sg.gl.max_anisotropy; - } - glTexParameteri(img->gl.target, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); - } - if (img->cmn.type == SG_IMAGETYPE_CUBE) { - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - else { - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_S, (GLint)_sg_gl_wrap(img->cmn.wrap_u)); - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(img->cmn.wrap_v)); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && (img->cmn.type == SG_IMAGETYPE_3D)) { - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(img->cmn.wrap_w)); - } - #endif - #if defined(SOKOL_GLCORE33) - float border[4]; - switch (img->cmn.border_color) { - case SG_BORDERCOLOR_TRANSPARENT_BLACK: - border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 0.0f; - break; - case SG_BORDERCOLOR_OPAQUE_WHITE: - border[0] = 1.0f; border[1] = 1.0f; border[2] = 1.0f; border[3] = 1.0f; - break; - default: - border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f; - break; + } else { + // create our own GL texture(s) + img->gl.target = _sg_gl_texture_target(img->cmn.type); + const GLenum gl_format = _sg_gl_teximage_format(img->cmn.pixel_format); + const bool is_compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + glGenTextures(1, &img->gl.tex[slot]); + 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); } - glTexParameterfv(img->gl.target, GL_TEXTURE_BORDER_COLOR, border); - #endif - } - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - /* GL spec has strange defaults for mipmap min/max lod: -1000 to +1000 */ - const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f); - const float max_lod = _sg_clamp(desc->max_lod, 0.0f, 1000.0f); - glTexParameterf(img->gl.target, GL_TEXTURE_MIN_LOD, min_lod); - glTexParameterf(img->gl.target, GL_TEXTURE_MAX_LOD, max_lod); - } - #endif - 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; - int mip_width = img->cmn.width >> mip_index; - if (mip_width == 0) { - mip_width = 1; + 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); } - int mip_height = img->cmn.height >> mip_index; - if (mip_height == 0) { - mip_height = 1; + } 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 ((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); - } - } - #if !defined(SOKOL_GLES2) - else if (!_sg.gl.gles2 && ((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 >>= mip_index; - } - if (mip_depth == 0) { - mip_depth = 1; - } - 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); - } + 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); } - #endif } } - _sg_gl_cache_restore_texture_binding(0); } + _sg_gl_cache_restore_texture_sampler_binding(0); } } _SG_GL_CHECK_ERROR(); @@ -7060,21 +7228,83 @@ _SOKOL_PRIVATE void _sg_gl_discard_image(_sg_image_t* img) { _SG_GL_CHECK_ERROR(); for (int slot = 0; slot < img->cmn.num_slots; slot++) { if (img->gl.tex[slot]) { - _sg_gl_cache_invalidate_texture(img->gl.tex[slot]); - if (!img->gl.ext_textures) { + _sg_gl_cache_invalidate_texture_sampler(img->gl.tex[slot], 0); + if (!img->gl.injected) { glDeleteTextures(1, &img->gl.tex[slot]); } } } - if (img->gl.depth_render_buffer) { - glDeleteRenderbuffers(1, &img->gl.depth_render_buffer); - } if (img->gl.msaa_render_buffer) { glDeleteRenderbuffers(1, &img->gl.msaa_render_buffer); } _SG_GL_CHECK_ERROR(); } +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + _SG_GL_CHECK_ERROR(); + smp->gl.injected = (0 != desc->gl_sampler); + if (smp->gl.injected) { + smp->gl.smp = (GLuint) desc->gl_sampler; + } else { + glGenSamplers(1, &smp->gl.smp); + SOKOL_ASSERT(smp->gl.smp); + + const GLenum gl_min_filter = _sg_gl_min_filter(smp->cmn.min_filter, smp->cmn.mipmap_filter); + const GLenum gl_mag_filter = _sg_gl_mag_filter(smp->cmn.mag_filter); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MIN_FILTER, (GLint)gl_min_filter); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAG_FILTER, (GLint)gl_mag_filter); + // GL spec has strange defaults for mipmap min/max lod: -1000 to +1000 + const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f); + const float max_lod = _sg_clamp(desc->max_lod, 0.0f, 1000.0f); + glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MIN_LOD, min_lod); + glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MAX_LOD, max_lod); + 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) + float border[4]; + switch (smp->cmn.border_color) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 0.0f; + break; + case SG_BORDERCOLOR_OPAQUE_WHITE: + border[0] = 1.0f; border[1] = 1.0f; border[2] = 1.0f; border[3] = 1.0f; + break; + default: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f; + break; + } + glSamplerParameterfv(smp->gl.smp, GL_TEXTURE_BORDER_COLOR, border); + #endif + if (smp->cmn.compare != SG_COMPAREFUNC_NEVER) { + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_FUNC, (GLint)_sg_gl_compare_func(smp->cmn.compare)); + } else { + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + if (_sg.gl.ext_anisotropic && (smp->cmn.max_anisotropy > 1)) { + GLint max_aniso = (GLint) smp->cmn.max_anisotropy; + if (max_aniso > _sg.gl.max_anisotropy) { + max_aniso = _sg.gl.max_anisotropy; + } + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_invalidate_texture_sampler(0, smp->gl.smp); + if (!smp->gl.injected) { + glDeleteSamplers(1, &smp->gl.smp); + } + _SG_GL_CHECK_ERROR(); +} + _SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* src) { SOKOL_ASSERT(src); _SG_GL_CHECK_ERROR(); @@ -7084,13 +7314,14 @@ _SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* s GLint compile_status = 0; glGetShaderiv(gl_shd, GL_COMPILE_STATUS, &compile_status); if (!compile_status) { - /* compilation failed, log error and delete shader */ + // compilation failed, log error and delete shader GLint log_len = 0; glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len); if (log_len > 0) { GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); glGetShaderInfoLog(gl_shd, log_len, &log_len, log_buf); - SG_LOG(log_buf); + _SG_ERROR(GL_SHADER_COMPILATION_FAILED); + _SG_LOGMSG(GL_SHADER_COMPILATION_FAILED, log_buf); _sg_free(log_buf); } glDeleteShader(gl_shd); @@ -7105,9 +7336,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s SOKOL_ASSERT(!shd->gl.prog); _SG_GL_CHECK_ERROR(); - _sg_shader_common_init(&shd->cmn, desc); - - /* copy vertex attribute names over, these are required for GLES2, and optional for GLES3 and GL3.x */ + // copy the optional vertex attribute names over for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { _sg_strcpy(&shd->gl.attrs[i].name, desc->attrs[i].name); } @@ -7133,7 +7362,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s if (log_len > 0) { GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); glGetProgramInfoLog(gl_prog, log_len, &log_len, log_buf); - SG_LOG(log_buf); + _SG_ERROR(GL_SHADER_LINKING_FAILED); + _SG_LOGMSG(GL_SHADER_LINKING_FAILED, log_buf); _sg_free(log_buf); } glDeleteProgram(gl_prog); @@ -7141,12 +7371,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s } shd->gl.prog = gl_prog; - /* resolve uniforms */ + // resolve uniforms _SG_GL_CHECK_ERROR(); for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_index]; _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; - for (int ub_index = 0; ub_index < shd->cmn.stage[stage_index].num_uniform_blocks; ub_index++) { + for (int ub_index = 0; ub_index < stage->num_uniform_blocks; ub_index++) { const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; SOKOL_ASSERT(ub_desc->size > 0); _sg_gl_uniform_block_t* ub = &gl_stage->uniform_blocks[ub_index]; @@ -7167,8 +7398,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s cur_uniform_offset += u_size; if (u_desc->name) { u->gl_loc = glGetUniformLocation(gl_prog, u_desc->name); - } - else { + } else { u->gl_loc = u_index; } ub->num_uniforms++; @@ -7181,7 +7411,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s } } - /* resolve image locations */ + // resolve combined image samplers _SG_GL_CHECK_ERROR(); GLuint cur_prog = 0; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&cur_prog); @@ -7189,25 +7419,24 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s int gl_tex_slot = 0; for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_index]; _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; - for (int img_index = 0; img_index < shd->cmn.stage[stage_index].num_images; img_index++) { - const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - SOKOL_ASSERT(img_desc->image_type != _SG_IMAGETYPE_DEFAULT); - _sg_gl_shader_image_t* gl_img = &gl_stage->images[img_index]; - GLint gl_loc = img_index; - if (img_desc->name) { - gl_loc = glGetUniformLocation(gl_prog, img_desc->name); - } + for (int img_smp_index = 0; img_smp_index < stage->num_image_samplers; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + _sg_gl_shader_image_sampler_t* gl_img_smp = &gl_stage->image_samplers[img_smp_index]; + SOKOL_ASSERT(img_smp_desc->glsl_name); + GLint gl_loc = glGetUniformLocation(gl_prog, img_smp_desc->glsl_name); if (gl_loc != -1) { - gl_img->gl_tex_slot = gl_tex_slot++; - glUniform1i(gl_loc, gl_img->gl_tex_slot); - } - else { - gl_img->gl_tex_slot = -1; + gl_img_smp->gl_tex_slot = gl_tex_slot++; + glUniform1i(gl_loc, gl_img_smp->gl_tex_slot); + } else { + gl_img_smp->gl_tex_slot = -1; + _SG_ERROR(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER); + _SG_LOGMSG(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER, img_smp_desc->glsl_name); } } } - /* it's legal to call glUseProgram with 0 */ + // it's legal to call glUseProgram with 0 glUseProgram(cur_prog); _SG_GL_CHECK_ERROR(); return SG_RESOURCESTATE_VALID; @@ -7225,11 +7454,10 @@ _SOKOL_PRIVATE void _sg_gl_discard_shader(_sg_shader_t* shd) { _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && shd && desc); - SOKOL_ASSERT(!pip->shader && pip->cmn.shader_id.id == SG_INVALID_ID); + SOKOL_ASSERT((pip->shader == 0) && (pip->cmn.shader_id.id != SG_INVALID_ID)); SOKOL_ASSERT(desc->shader.id == shd->slot.id); SOKOL_ASSERT(shd->gl.prog); pip->shader = shd; - _sg_pipeline_common_init(&pip->cmn, desc); pip->gl.primitive_type = desc->primitive_type; pip->gl.depth = desc->depth; pip->gl.stencil = desc->stencil; @@ -7243,19 +7471,19 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg pip->gl.sample_count = desc->sample_count; pip->gl.alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; - /* resolve vertex attributes */ + // resolve vertex attributes for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { pip->gl.attrs[attr_index].vb_index = -1; } for (int attr_index = 0; attr_index < _sg.limits.max_vertex_attrs; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; - const sg_vertex_step step_func = l_desc->step_func; - const int step_rate = l_desc->step_rate; + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index]; + const sg_vertex_step step_func = l_state->step_func; + const int step_rate = l_state->step_rate; GLint attr_loc = attr_index; if (!_sg_strempty(&shd->gl.attrs[attr_index].name)) { attr_loc = glGetAttribLocation(pip->shader->gl.prog, _sg_strptr(&shd->gl.attrs[attr_index].name)); @@ -7264,25 +7492,23 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg if (attr_loc != -1) { _sg_gl_attr_t* gl_attr = &pip->gl.attrs[attr_loc]; SOKOL_ASSERT(gl_attr->vb_index == -1); - gl_attr->vb_index = (int8_t) a_desc->buffer_index; + gl_attr->vb_index = (int8_t) a_state->buffer_index; if (step_func == SG_VERTEXSTEP_PER_VERTEX) { gl_attr->divisor = 0; - } - else { + } else { gl_attr->divisor = (int8_t) step_rate; pip->cmn.use_instanced_draw = true; } - SOKOL_ASSERT(l_desc->stride > 0); - gl_attr->stride = (uint8_t) l_desc->stride; - gl_attr->offset = a_desc->offset; - gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_desc->format); - gl_attr->type = _sg_gl_vertexformat_type(a_desc->format); - gl_attr->normalized = _sg_gl_vertexformat_normalized(a_desc->format); - pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; - } - else { - SG_LOG("Vertex attribute not found in shader: "); - SG_LOG(_sg_strptr(&shd->gl.attrs[attr_index].name)); + SOKOL_ASSERT(l_state->stride > 0); + gl_attr->stride = (uint8_t) l_state->stride; + gl_attr->offset = a_state->offset; + gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_state->format); + gl_attr->type = _sg_gl_vertexformat_type(a_state->format); + gl_attr->normalized = _sg_gl_vertexformat_normalized(a_state->format); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; + } else { + _SG_ERROR(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER); + _SG_LOGMSG(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, _sg_strptr(&shd->gl.attrs[attr_index].name)); } } return SG_RESOURCESTATE_VALID; @@ -7293,162 +7519,135 @@ _SOKOL_PRIVATE void _sg_gl_discard_pipeline(_sg_pipeline_t* pip) { _sg_gl_cache_invalidate_pipeline(pip); } -/* - _sg_create_pass +_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) { + const _sg_image_t* img = gl_att->image; + SOKOL_ASSERT(img); + const GLuint gl_tex = img->gl.tex[0]; + SOKOL_ASSERT(gl_tex); + const int mip_level = cmn_att->mip_level; + const int slice = cmn_att->slice; + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, GL_TEXTURE_2D, gl_tex, mip_level); + break; + case SG_IMAGETYPE_CUBE: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, _sg_gl_cubeface_target(slice), gl_tex, mip_level); + break; + default: + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att_type, gl_tex, mip_level, slice); + break; + } +} - att_imgs must point to a _sg_image* att_imgs[SG_MAX_COLOR_ATTACHMENTS+1] array, - first entries are the color attachment images (or nullptr), last entry - is the depth-stencil image (or nullptr). -*/ -_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { - SOKOL_ASSERT(pass && att_images && desc); - SOKOL_ASSERT(att_images && att_images[0]); - _SG_GL_CHECK_ERROR(); +_SOKOL_PRIVATE GLenum _sg_gl_depth_stencil_attachment_type(const _sg_gl_attachment_t* ds_att) { + const _sg_image_t* img = ds_att->image; + SOKOL_ASSERT(img); + if (_sg_is_depth_stencil_format(img->cmn.pixel_format)) { + return GL_DEPTH_STENCIL_ATTACHMENT; + } else { + return GL_DEPTH_ATTACHMENT; + } +} - _sg_pass_common_init(&pass->cmn, desc); +_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_ASSERT(color_images && resolve_images); + _SG_GL_CHECK_ERROR(); - /* copy image pointers */ - const sg_pass_attachment_desc* att_desc; + // copy image pointers for (int i = 0; i < pass->cmn.num_color_atts; i++) { - att_desc = &desc->color_attachments[i]; - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + const sg_pass_attachment_desc* color_desc = &desc->color_attachments[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(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); - pass->gl.color_atts[i].image = att_images[i]; + 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]; + + const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == pass->gl.resolve_atts[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]; + } } SOKOL_ASSERT(0 == pass->gl.ds_att.image); - att_desc = &desc->depth_stencil_attachment; - if (att_desc->image.id != SG_INVALID_ID) { - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; - SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); - pass->gl.ds_att.image = att_images[ds_img_index]; + const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + 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; } - /* store current framebuffer binding (restored at end of function) */ + // store current framebuffer binding (restored at end of function) GLuint gl_orig_fb; glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); - /* create a framebuffer object */ + // create a framebuffer object glGenFramebuffers(1, &pass->gl.fb); glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); - /* attach msaa render buffer or textures */ - const bool is_msaa = (0 != att_images[0]->gl.msaa_render_buffer); - if (is_msaa) { - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - const _sg_image_t* att_img = pass->gl.color_atts[i].image; - if (att_img) { - const GLuint gl_render_buffer = att_img->gl.msaa_render_buffer; - SOKOL_ASSERT(gl_render_buffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, (GLenum)(GL_COLOR_ATTACHMENT0+i), GL_RENDERBUFFER, gl_render_buffer); - } - } - } - else { - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - const _sg_image_t* att_img = pass->gl.color_atts[i].image; - const int mip_level = pass->cmn.color_atts[i].mip_level; - const int slice = pass->cmn.color_atts[i].slice; - if (att_img) { - const GLuint gl_tex = att_img->gl.tex[0]; - SOKOL_ASSERT(gl_tex); - const GLenum gl_att = (GLenum)(GL_COLOR_ATTACHMENT0 + i); - switch (att_img->cmn.type) { - case SG_IMAGETYPE_2D: - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, GL_TEXTURE_2D, gl_tex, mip_level); - break; - case SG_IMAGETYPE_CUBE: - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, _sg_gl_cubeface_target(slice), gl_tex, mip_level); - break; - default: - /* 3D- or array-texture */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att, gl_tex, mip_level, slice); - } - #endif - break; - } - } + // 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; + 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); } } - - /* attach depth-stencil buffer to framebuffer */ + // attach depth-stencil attachement if (pass->gl.ds_att.image) { - const GLuint gl_render_buffer = pass->gl.ds_att.image->gl.depth_render_buffer; - SOKOL_ASSERT(gl_render_buffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); - if (_sg_is_depth_stencil_format(pass->gl.ds_att.image->cmn.pixel_format)) { - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); + 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; + 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); } } - /* check if framebuffer is complete */ + // check if framebuffer is complete if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - SG_LOG("Framebuffer completeness check failed!\n"); + _SG_ERROR(GL_FRAMEBUFFER_INCOMPLETE); return SG_RESOURCESTATE_FAILED; } - /* setup color attachments for the framebuffer */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - GLenum att[SG_MAX_COLOR_ATTACHMENTS] = { - GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1, - GL_COLOR_ATTACHMENT2, - GL_COLOR_ATTACHMENT3 - }; - glDrawBuffers(pass->cmn.num_color_atts, att); - } - #endif + // setup color attachments for the framebuffer + static const GLenum gl_draw_bufs[SG_MAX_COLOR_ATTACHMENTS] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3 + }; + glDrawBuffers(pass->cmn.num_color_atts, gl_draw_bufs); - /* create MSAA resolve framebuffers if necessary */ - if (is_msaa) { - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg_gl_attachment_t* gl_att = &pass->gl.color_atts[i]; - _sg_pass_attachment_t* cmn_att = &pass->cmn.color_atts[i]; - if (gl_att->image) { - SOKOL_ASSERT(0 == gl_att->gl_msaa_resolve_buffer); - glGenFramebuffers(1, &gl_att->gl_msaa_resolve_buffer); - glBindFramebuffer(GL_FRAMEBUFFER, gl_att->gl_msaa_resolve_buffer); - const GLuint gl_tex = gl_att->image->gl.tex[0]; - SOKOL_ASSERT(gl_tex); - switch (gl_att->image->cmn.type) { - case SG_IMAGETYPE_2D: - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, gl_tex, cmn_att->mip_level); - break; - case SG_IMAGETYPE_CUBE: - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - _sg_gl_cubeface_target(cmn_att->slice), gl_tex, cmn_att->mip_level); - break; - default: - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gl_tex, cmn_att->mip_level, cmn_att->slice); - } - #endif - break; - } - /* check if framebuffer is complete */ - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - SG_LOG("Framebuffer completeness check failed (msaa resolve buffer)!\n"); - return SG_RESOURCESTATE_FAILED; - } - /* setup color attachments for the framebuffer */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - const GLenum gl_draw_bufs = GL_COLOR_ATTACHMENT0; - glDrawBuffers(1, &gl_draw_bufs); - } - #endif + // 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]; + 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_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); + return SG_RESOURCESTATE_FAILED; } + // setup color attachments for the framebuffer + glDrawBuffers(1, &gl_draw_bufs[0]); } } - /* restore original framebuffer binding */ + // restore original framebuffer binding glBindFramebuffer(GL_FRAMEBUFFER, gl_orig_fb); _SG_GL_CHECK_ERROR(); return SG_RESOURCESTATE_VALID; @@ -7462,48 +7661,44 @@ _SOKOL_PRIVATE void _sg_gl_discard_pass(_sg_pass_t* pass) { glDeleteFramebuffers(1, &pass->gl.fb); } for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->gl.color_atts[i].gl_msaa_resolve_buffer) { - glDeleteFramebuffers(1, &pass->gl.color_atts[i].gl_msaa_resolve_buffer); + if (pass->gl.msaa_resolve_framebuffer[i]) { + glDeleteFramebuffers(1, &pass->gl.msaa_resolve_framebuffer[i]); } } - if (pass->gl.ds_att.gl_msaa_resolve_buffer) { - glDeleteFramebuffers(1, &pass->gl.ds_att.gl_msaa_resolve_buffer); - } _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)); - /* NOTE: may return null */ return pass->gl.color_atts[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_pass_ds_image(const _sg_pass_t* pass) { - /* NOTE: may return null */ SOKOL_ASSERT(pass); return pass->gl.ds_att.image; } _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { - /* FIXME: what if a texture used as render target is still bound, should we - unbind all currently bound textures in begin 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); _SG_GL_CHECK_ERROR(); _sg.gl.in_pass = true; - _sg.gl.cur_pass = pass; /* can be 0 */ + _sg.gl.cur_pass = pass; // can be 0 if (pass) { _sg.gl.cur_pass_id.id = pass->slot.id; - } - else { + } else { _sg.gl.cur_pass_id.id = SG_INVALID_ID; } _sg.gl.cur_pass_width = w; _sg.gl.cur_pass_height = h; - /* number of color attachments */ - const int num_color_atts = pass ? pass->cmn.num_color_atts : 1; - // bind the render pass framebuffer // // FIXME: Disabling SRGB conversion for the default framebuffer is @@ -7519,9 +7714,7 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac glEnable(GL_FRAMEBUFFER_SRGB); #endif glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); - - } - else { + } else { // default pass SOKOL_ASSERT(_sg.gl.cur_context); #if defined(SOKOL_GLCORE33) @@ -7532,19 +7725,22 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac glViewport(0, 0, w, h); glScissor(0, 0, w, h); - /* clear color and depth-stencil attachments if needed */ - bool clear_color = false; + // number of color attachments + const int num_color_atts = pass ? pass->cmn.num_color_atts : 1; + + // clear color and depth-stencil attachments if needed + bool clear_any_color = false; for (int i = 0; i < num_color_atts; i++) { - if (SG_ACTION_CLEAR == action->colors[i].action) { - clear_color = true; + if (SG_LOADACTION_CLEAR == action->colors[i].load_action) { + clear_any_color = true; break; } } - const bool clear_depth = (action->depth.action == SG_ACTION_CLEAR); - const bool clear_stencil = (action->stencil.action == SG_ACTION_CLEAR); + const bool clear_depth = (action->depth.load_action == SG_LOADACTION_CLEAR); + const bool clear_stencil = (action->stencil.load_action == SG_LOADACTION_CLEAR); bool need_pip_cache_flush = false; - if (clear_color) { + if (clear_any_color) { bool need_color_mask_flush = false; // NOTE: not a bug to iterate over all possible color attachments for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { @@ -7578,64 +7774,33 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac } } if (need_pip_cache_flush) { - /* we messed with the state cache directly, need to clear cached - pipeline to force re-evaluation in next sg_apply_pipeline() */ + // we messed with the state cache directly, need to clear cached + // pipeline to force re-evaluation in next sg_apply_pipeline() _sg.gl.cache.cur_pipeline = 0; _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; } - bool use_mrt_clear = (0 != pass); - #if defined(SOKOL_GLES2) - use_mrt_clear = false; - #else - if (_sg.gl.gles2) { - use_mrt_clear = false; - } - #endif - if (!use_mrt_clear) { - GLbitfield clear_mask = 0; - if (clear_color) { - clear_mask |= GL_COLOR_BUFFER_BIT; - const sg_color c = action->colors[0].value; - glClearColor(c.r, c.g, c.b, c.a); - } - if (clear_depth) { - clear_mask |= GL_DEPTH_BUFFER_BIT; - #ifdef SOKOL_GLCORE33 - glClearDepth(action->depth.value); - #else - glClearDepthf(action->depth.value); - #endif - } - if (clear_stencil) { - clear_mask |= GL_STENCIL_BUFFER_BIT; - glClearStencil(action->stencil.value); - } - if (0 != clear_mask) { - glClear(clear_mask); + for (int i = 0; i < num_color_atts; i++) { + if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { + glClearBufferfv(GL_COLOR, i, &action->colors[i].clear_value.r); } } - #if !defined SOKOL_GLES2 - else { - SOKOL_ASSERT(pass); - for (int i = 0; i < num_color_atts; i++) { - if (action->colors[i].action == SG_ACTION_CLEAR) { - glClearBufferfv(GL_COLOR, i, &action->colors[i].value.r); - } - } - if (pass->gl.ds_att.image) { - if (clear_depth && clear_stencil) { - glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.value, action->stencil.value); - } - else if (clear_depth) { - glClearBufferfv(GL_DEPTH, 0, &action->depth.value); - } - else if (clear_stencil) { - GLint val = (GLint) action->stencil.value; - glClearBufferiv(GL_STENCIL, 0, &val); - } + if ((pass == 0) || (pass->gl.ds_att.image)) { + if (clear_depth && clear_stencil) { + glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.clear_value, action->stencil.clear_value); + } else if (clear_depth) { + glClearBufferfv(GL_DEPTH, 0, &action->depth.clear_value); + } else if (clear_stencil) { + GLint val = (GLint) action->stencil.clear_value; + glClearBufferiv(GL_STENCIL, 0, &val); } } - #endif + // keep store actions for end-pass + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.gl.color_store_actions[i] = action->colors[i].store_action; + } + _sg.gl.depth_store_action = action->depth.store_action; + _sg.gl.stencil_store_action = action->stencil.store_action; + _SG_GL_CHECK_ERROR(); } @@ -7643,35 +7808,55 @@ _SOKOL_PRIVATE void _sg_gl_end_pass(void) { SOKOL_ASSERT(_sg.gl.in_pass); _SG_GL_CHECK_ERROR(); - /* if this was an offscreen pass, and MSAA rendering was used, need - to resolve into the pass images */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && _sg.gl.cur_pass) { - /* check if the pass object is still valid */ + 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); - bool is_msaa = (0 != _sg.gl.cur_pass->gl.color_atts[0].gl_msaa_resolve_buffer); - if (is_msaa) { - SOKOL_ASSERT(pass->gl.fb); - glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->gl.fb); - SOKOL_ASSERT(pass->gl.color_atts[0].image); - const int w = pass->gl.color_atts[0].image->cmn.width; - const int h = pass->gl.color_atts[0].image->cmn.height; - for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { - const _sg_gl_attachment_t* gl_att = &pass->gl.color_atts[att_index]; - if (gl_att->image) { - SOKOL_ASSERT(gl_att->gl_msaa_resolve_buffer); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gl_att->gl_msaa_resolve_buffer); - glReadBuffer((GLenum)(GL_COLOR_ATTACHMENT0 + att_index)); - glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - else { - break; + bool fb_read_bound = false; + bool fb_draw_bound = true; + const int num_atts = pass->cmn.num_color_atts; + for (int i = 0; i < num_atts; i++) { + // perform MSAA resolve if needed + if (pass->gl.msaa_resolve_framebuffer[i] != 0) { + if (!fb_read_bound) { + SOKOL_ASSERT(pass->gl.fb); + glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->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]); + 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; + } + } + + // invalidate framebuffers + _SOKOL_UNUSED(fb_draw_bound); + #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); + } + GLenum invalidate_atts[SG_MAX_COLOR_ATTACHMENTS + 2] = { 0 }; + int att_index = 0; + for (int i = 0; i < num_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) { + invalidate_atts[att_index++] = GL_DEPTH_ATTACHMENT; + } + if (_sg.gl.stencil_store_action == SG_STOREACTION_DONTCARE) { + invalidate_atts[att_index++] = GL_STENCIL_ATTACHMENT; + } + if (att_index > 0) { + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, att_index, invalidate_atts); + } + #endif } - #endif + _sg.gl.cur_pass = 0; _sg.gl.cur_pass_id.id = SG_INVALID_ID; _sg.gl.cur_pass_width = 0; @@ -7705,7 +7890,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->gl.primitive_type); _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->cmn.index_type); - /* update depth state */ + // update depth state { const sg_depth_state* state_ds = &pip->gl.depth; sg_depth_state* cache_ds = &_sg.gl.cache.depth; @@ -7738,15 +7923,14 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _sg.gl.cache.polygon_offset_enabled = po_enabled; if (po_enabled) { glEnable(GL_POLYGON_OFFSET_FILL); - } - else { + } else { glDisable(GL_POLYGON_OFFSET_FILL); } } } } - /* update stencil state */ + // update stencil state { const sg_stencil_state* state_ss = &pip->gl.stencil; sg_stencil_state* cache_ss = &_sg.gl.cache.stencil; @@ -7754,8 +7938,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { cache_ss->enabled = state_ss->enabled; if (state_ss->enabled) { glEnable(GL_STENCIL_TEST); - } - else { + } else { glDisable(GL_STENCIL_TEST); } } @@ -7794,18 +7977,16 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { cache_ss->ref = state_ss->ref; } - /* update blend state - FIXME: separate blend state per color attachment not support, needs GL4 - */ - { + if (pip->cmn.color_count > 0) { + // update blend state + // FIXME: separate blend state per color attachment not support, needs GL4 const sg_blend_state* state_bs = &pip->gl.blend; sg_blend_state* cache_bs = &_sg.gl.cache.blend; if (state_bs->enabled != cache_bs->enabled) { cache_bs->enabled = state_bs->enabled; if (state_bs->enabled) { glEnable(GL_BLEND); - } - else { + } else { glDisable(GL_BLEND); } } @@ -7828,45 +8009,45 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { cache_bs->op_alpha = state_bs->op_alpha; glBlendEquationSeparate(_sg_gl_blend_op(state_bs->op_rgb), _sg_gl_blend_op(state_bs->op_alpha)); } - } - /* standalone state */ - for (GLuint i = 0; i < (GLuint)pip->cmn.color_attachment_count; i++) { - 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 - glColorMaski(i, - (cm & SG_COLORMASK_R) != 0, - (cm & SG_COLORMASK_G) != 0, - (cm & SG_COLORMASK_B) != 0, - (cm & SG_COLORMASK_A) != 0); - #else - if (0 == i) { - glColorMask((cm & SG_COLORMASK_R) != 0, + // standalone color target state + for (GLuint i = 0; i < (GLuint)pip->cmn.color_count; i++) { + 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 + glColorMaski(i, + (cm & SG_COLORMASK_R) != 0, (cm & SG_COLORMASK_G) != 0, (cm & SG_COLORMASK_B) != 0, (cm & SG_COLORMASK_A) != 0); - } - #endif + #else + if (0 == i) { + glColorMask((cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); + } + #endif + } } - } - if (!_sg_fequal(pip->cmn.blend_color.r, _sg.gl.cache.blend_color.r, 0.0001f) || - !_sg_fequal(pip->cmn.blend_color.g, _sg.gl.cache.blend_color.g, 0.0001f) || - !_sg_fequal(pip->cmn.blend_color.b, _sg.gl.cache.blend_color.b, 0.0001f) || - !_sg_fequal(pip->cmn.blend_color.a, _sg.gl.cache.blend_color.a, 0.0001f)) - { - sg_color c = pip->cmn.blend_color; - _sg.gl.cache.blend_color = c; - glBlendColor(c.r, c.g, c.b, c.a); - } + if (!_sg_fequal(pip->cmn.blend_color.r, _sg.gl.cache.blend_color.r, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.g, _sg.gl.cache.blend_color.g, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.b, _sg.gl.cache.blend_color.b, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.a, _sg.gl.cache.blend_color.a, 0.0001f)) + { + sg_color c = pip->cmn.blend_color; + _sg.gl.cache.blend_color = c; + glBlendColor(c.r, c.g, c.b, c.a); + } + } // pip->cmn.color_count > 0 + if (pip->gl.cull_mode != _sg.gl.cache.cull_mode) { _sg.gl.cache.cull_mode = pip->gl.cull_mode; if (SG_CULLMODE_NONE == pip->gl.cull_mode) { glDisable(GL_CULL_FACE); - } - else { + } else { glEnable(GL_CULL_FACE); GLenum gl_mode = (SG_CULLMODE_FRONT == pip->gl.cull_mode) ? GL_FRONT : GL_BACK; glCullFace(gl_mode); @@ -7881,8 +8062,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _sg.gl.cache.alpha_to_coverage_enabled = pip->gl.alpha_to_coverage_enabled; if (pip->gl.alpha_to_coverage_enabled) { glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); - } - else { + } else { glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); } } @@ -7891,14 +8071,13 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _sg.gl.cache.sample_count = pip->gl.sample_count; if (pip->gl.sample_count > 1) { glEnable(GL_MULTISAMPLE); - } - else { + } else { glDisable(GL_MULTISAMPLE); } } #endif - /* bind shader program */ + // bind shader program if (pip->shader->gl.prog != _sg.gl.cache.prog) { _sg.gl.cache.prog = pip->shader->gl.prog; glUseProgram(pip->shader->gl.prog); @@ -7912,40 +8091,49 @@ _SOKOL_PRIVATE void _sg_gl_apply_bindings( _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) + _sg_image_t** fs_imgs, int num_fs_imgs, + _sg_sampler_t** vs_smps, int num_vs_smps, + _sg_sampler_t** fs_smps, int num_fs_smps) { SOKOL_ASSERT(pip); - _SOKOL_UNUSED(num_fs_imgs); - _SOKOL_UNUSED(num_vs_imgs); _SOKOL_UNUSED(num_vbs); _SG_GL_CHECK_ERROR(); - /* bind textures */ + // bind combined image-samplers _SG_GL_CHECK_ERROR(); for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; const _sg_gl_shader_stage_t* gl_stage = &pip->shader->gl.stage[stage_index]; - _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS)? vs_imgs : fs_imgs; - SOKOL_ASSERT(((stage_index == SG_SHADERSTAGE_VS)? num_vs_imgs : num_fs_imgs) == stage->num_images); - for (int img_index = 0; img_index < stage->num_images; img_index++) { - const _sg_gl_shader_image_t* gl_shd_img = &gl_stage->images[img_index]; - if (gl_shd_img->gl_tex_slot != -1) { + _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS) ? vs_imgs : fs_imgs; + _sg_sampler_t** smps = (stage_index == SG_SHADERSTAGE_VS) ? vs_smps : fs_smps; + const int num_imgs = (stage_index == SG_SHADERSTAGE_VS) ? num_vs_imgs : num_fs_imgs; + const int num_smps = (stage_index == SG_SHADERSTAGE_VS) ? num_vs_smps : num_fs_smps; + SOKOL_ASSERT(num_imgs == stage->num_images); _SOKOL_UNUSED(num_imgs); + SOKOL_ASSERT(num_smps == stage->num_samplers); _SOKOL_UNUSED(num_smps); + for (int img_smp_index = 0; img_smp_index < stage->num_image_samplers; img_smp_index++) { + const int gl_tex_slot = gl_stage->image_samplers[img_smp_index].gl_tex_slot; + if (gl_tex_slot != -1) { + 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 < num_imgs); + SOKOL_ASSERT(smp_index < num_smps); _sg_image_t* img = imgs[img_index]; + _sg_sampler_t* smp = smps[smp_index]; + const GLenum gl_tgt = img->gl.target; const GLuint gl_tex = img->gl.tex[img->cmn.active_slot]; - SOKOL_ASSERT(img && img->gl.target); - SOKOL_ASSERT((gl_shd_img->gl_tex_slot != -1) && gl_tex); - _sg_gl_cache_bind_texture(gl_shd_img->gl_tex_slot, img->gl.target, gl_tex); + const GLuint gl_smp = smp->gl.smp; + _sg_gl_cache_bind_texture_sampler(gl_tex_slot, gl_tgt, gl_tex, gl_smp); } } } _SG_GL_CHECK_ERROR(); - /* index buffer (can be 0) */ + // index buffer (can be 0) const GLuint gl_ib = ib ? ib->gl.buf[ib->cmn.active_slot] : 0; _sg_gl_cache_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); _sg.gl.cache.cur_ib_offset = ib_offset; - /* vertex attributes */ + // vertex attributes for (GLuint attr_index = 0; attr_index < (GLuint)_sg.limits.max_vertex_attrs; attr_index++) { _sg_gl_attr_t* attr = &pip->gl.attrs[attr_index]; _sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index]; @@ -7953,7 +8141,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_bindings( int vb_offset = 0; GLuint gl_vb = 0; if (attr->vb_index >= 0) { - /* attribute is enabled */ + // attribute is enabled SOKOL_ASSERT(attr->vb_index < num_vbs); _sg_buffer_t* vb = vbs[attr->vb_index]; SOKOL_ASSERT(vb); @@ -7971,20 +8159,15 @@ _SOKOL_PRIVATE void _sg_gl_apply_bindings( glVertexAttribPointer(attr_index, attr->size, attr->type, attr->normalized, attr->stride, (const GLvoid*)(GLintptr)vb_offset); - #if defined(_SOKOL_GL_INSTANCING_ENABLED) - if (_sg.features.instancing) { - glVertexAttribDivisor(attr_index, (GLuint)attr->divisor); - } - #endif + glVertexAttribDivisor(attr_index, (GLuint)attr->divisor); cache_attr_dirty = true; } if (cache_attr->gl_attr.vb_index == -1) { glEnableVertexAttribArray(attr_index); cache_attr_dirty = true; } - } - else { - /* attribute is disabled */ + } else { + // attribute is disabled if (cache_attr->gl_attr.vb_index != -1) { glDisableVertexAttribArray(attr_index); cache_attr_dirty = true; @@ -8057,27 +8240,20 @@ _SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_inst const GLenum i_type = _sg.gl.cache.cur_index_type; const GLenum p_type = _sg.gl.cache.cur_primitive_type; if (0 != i_type) { - /* indexed rendering */ + // 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 (_sg.features.instancing) { - glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); - } - } - else { + glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); + } else { glDrawElements(p_type, num_elements, i_type, indices); } - } - else { - /* non-indexed rendering */ + } else { + // non-indexed rendering if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { - if (_sg.features.instancing) { - glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); - } - } - else { + glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); + } else { glDrawArrays(p_type, base_element, num_elements); } } @@ -8085,14 +8261,14 @@ _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) */ + // "soft" clear bindings (only those that are actually bound) _sg_gl_cache_clear_buffer_bindings(false); - _sg_gl_cache_clear_texture_bindings(false); + _sg_gl_cache_clear_texture_sampler_bindings(false); } _SOKOL_PRIVATE void _sg_gl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - /* only one update per buffer per frame allowed */ + // only one update per buffer per frame allowed if (++buf->cmn.active_slot >= buf->cmn.num_slots) { buf->cmn.active_slot = 0; } @@ -8125,20 +8301,20 @@ _SOKOL_PRIVATE int _sg_gl_append_buffer(_sg_buffer_t* buf, const sg_range* data, glBufferSubData(gl_tgt, buf->cmn.append_pos, (GLsizeiptr)data->size, data->ptr); _sg_gl_cache_restore_buffer_binding(gl_tgt); _SG_GL_CHECK_ERROR(); - /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + // NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend return _sg_roundup((int)data->size, 4); } _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* data) { SOKOL_ASSERT(img && data); - /* only one update per image per frame allowed */ + // only one update per image per frame allowed if (++img->cmn.active_slot >= img->cmn.num_slots) { img->cmn.active_slot = 0; } SOKOL_ASSERT(img->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); SOKOL_ASSERT(0 != img->gl.tex[img->cmn.active_slot]); - _sg_gl_cache_store_texture_binding(0); - _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[img->cmn.active_slot]); + _sg_gl_cache_store_texture_sampler_binding(0); + _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[img->cmn.active_slot], 0); const GLenum gl_img_format = _sg_gl_teximage_format(img->cmn.pixel_format); const GLenum gl_img_type = _sg_gl_teximage_type(img->cmn.pixel_format); const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; @@ -8150,26 +8326,18 @@ _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* d gl_img_target = _sg_gl_cubeface_target(face_index); } const GLvoid* data_ptr = data->subimage[face_index][mip_index].ptr; - int mip_width = img->cmn.width >> mip_index; - if (mip_width == 0) { - mip_width = 1; - } - int mip_height = img->cmn.height >> mip_index; - if (mip_height == 0) { - mip_height = 1; - } + int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { glTexSubImage2D(gl_img_target, mip_index, 0, 0, mip_width, mip_height, gl_img_format, gl_img_type, data_ptr); - } - #if !defined(SOKOL_GLES2) - else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type))) { - int mip_depth = img->cmn.num_slices >> mip_index; - if (mip_depth == 0) { - mip_depth = 1; + } 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(img->cmn.num_slices, mip_index); } glTexSubImage3D(gl_img_target, mip_index, 0, 0, 0, @@ -8178,13 +8346,18 @@ _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* d data_ptr); } - #endif } } - _sg_gl_cache_restore_texture_binding(0); + _sg_gl_cache_restore_texture_sampler_binding(0); } -/*== D3D11 BACKEND IMPLEMENTATION ============================================*/ +// ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ███ ███ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ █████ ██ ██ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██████ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>d3d11 backend #elif defined(SOKOL_D3D11) #if defined(__cplusplus) @@ -8199,7 +8372,7 @@ _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 -/*-- D3D11 C/C++ wrappers ----------------------------------------------------*/ +//-- D3D11 C/C++ wrappers ------------------------------------------------------ static inline HRESULT _sg_d3d11_CheckFormatSupport(ID3D11Device* self, DXGI_FORMAT Format, UINT* pFormatSupport) { #if defined(__cplusplus) return self->CheckFormatSupport(Format, pFormatSupport); @@ -8568,7 +8741,7 @@ static inline void _sg_d3d11_ClearState(ID3D11DeviceContext* self) { #endif } -/*-- enum translation functions ----------------------------------------------*/ +//-- enum translation functions ------------------------------------------------ _SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_usage(sg_usage usg) { switch (usg) { case SG_USAGE_IMMUTABLE: @@ -8595,7 +8768,7 @@ _SOKOL_PRIVATE UINT _sg_d3d11_cpu_access_flags(sg_usage usg) { } } -_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_pixel_format(sg_pixel_format fmt) { +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_R8: return DXGI_FORMAT_R8_UNORM; case SG_PIXELFORMAT_R8SN: return DXGI_FORMAT_R8_SNORM; @@ -8638,7 +8811,7 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_RGBA32UI: return DXGI_FORMAT_R32G32B32A32_UINT; 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_D32_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_BC1_RGBA: return DXGI_FORMAT_BC1_UNORM; case SG_PIXELFORMAT_BC2_RGBA: return DXGI_FORMAT_BC2_UNORM; @@ -8654,6 +8827,30 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_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 { + return _sg_d3d11_texture_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 { + return _sg_d3d11_texture_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 { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + _SOKOL_PRIVATE D3D11_PRIMITIVE_TOPOLOGY _sg_d3d11_primitive_topology(sg_primitive_type prim_type) { switch (prim_type) { case SG_PRIMITIVETYPE_POINTS: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; @@ -8674,45 +8871,43 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_index_format(sg_index_type index_type) { } } -_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, uint32_t max_anisotropy) { +_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, sg_filter mipmap_f, bool comparison, uint32_t max_anisotropy) { + uint32_t d3d11_filter = 0; if (max_anisotropy > 1) { - return D3D11_FILTER_ANISOTROPIC; - } - else if (mag_f == SG_FILTER_NEAREST) { - switch (min_f) { - case SG_FILTER_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_MAG_MIP_POINT; - case SG_FILTER_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; - case SG_FILTER_LINEAR_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; - default: - SOKOL_UNREACHABLE; break; - } - } - else if (mag_f == SG_FILTER_LINEAR) { - switch (min_f) { - case SG_FILTER_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; - case SG_FILTER_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; - case SG_FILTER_LINEAR_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_MAG_MIP_LINEAR; - default: - SOKOL_UNREACHABLE; break; - } - } - /* invalid value for mag filter */ - SOKOL_UNREACHABLE; - return D3D11_FILTER_MIN_MAG_MIP_POINT; + // D3D11_FILTER_ANISOTROPIC = 0x55, + d3d11_filter |= 0x55; + } else { + // D3D11_FILTER_MIN_MAG_MIP_POINT = 0, + // D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR = 0x1, + // D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x4, + // D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR = 0x5, + // D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT = 0x10, + // D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x11, + // D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14, + // D3D11_FILTER_MIN_MAG_MIP_LINEAR = 0x15, + if (mipmap_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x01; + } + if (mag_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x04; + } + if (min_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x10; + } + } + // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT = 0x80, + // D3D11_FILTER_COMPARISON_MIN_MAG_POINT_MIP_LINEAR = 0x81, + // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x84, + // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_MIP_LINEAR = 0x85, + // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT = 0x90, + // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x91, + // D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT = 0x94, + // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR = 0x95, + // D3D11_FILTER_COMPARISON_ANISOTROPIC = 0xd5, + if (comparison) { + d3d11_filter |= 0x80; + } + return (D3D11_FILTER)d3d11_filter; } _SOKOL_PRIVATE D3D11_TEXTURE_ADDRESS_MODE _sg_d3d11_address_mode(sg_wrap m) { @@ -8840,16 +9035,23 @@ _SOKOL_PRIVATE UINT8 _sg_d3d11_color_write_mask(sg_color_mask m) { return res; } -/* see: https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits#resource-limits-for-feature-level-11-hardware */ +_SOKOL_PRIVATE UINT _sg_d3d11_dxgi_fmt_caps(DXGI_FORMAT dxgi_fmt) { + UINT dxgi_fmt_caps = 0; + if (dxgi_fmt != DXGI_FORMAT_UNKNOWN) { + HRESULT hr = _sg_d3d11_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); + SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr)); + if (!SUCCEEDED(hr)) { + dxgi_fmt_caps = 0; + } + } + return dxgi_fmt_caps; +} + +// see: https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits#resource-limits-for-feature-level-11-hardware _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { _sg.backend = SG_BACKEND_D3D11; - _sg.features.instancing = true; _sg.features.origin_top_left = true; - _sg.features.multiple_render_targets = true; - _sg.features.msaa_render_targets = true; - _sg.features.imagetype_3d = true; - _sg.features.imagetype_array = true; _sg.features.image_clamp_to_border = true; _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; @@ -8861,32 +9063,23 @@ _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { _sg.limits.max_image_array_layers = 2 * 1024; _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; - /* see: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_format_support */ + // see: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_format_support for (int fmt = (SG_PIXELFORMAT_NONE+1); fmt < _SG_PIXELFORMAT_NUM; fmt++) { - UINT dxgi_fmt_caps = 0; - const DXGI_FORMAT dxgi_fmt = _sg_d3d11_pixel_format((sg_pixel_format)fmt); - if (dxgi_fmt != DXGI_FORMAT_UNKNOWN) { - HRESULT hr = _sg_d3d11_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); - SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr)); - if (!SUCCEEDED(hr)) { - dxgi_fmt_caps = 0; - } - } + 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]; - info->sample = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D); - info->filter = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE); - info->render = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); - info->blend = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); - info->msaa = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); - info->depth = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); - if (info->depth) { - info->render = true; - } + 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->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); } } _SOKOL_PRIVATE void _sg_d3d11_setup_backend(const sg_desc* desc) { - /* assume _sg.d3d11 already is zero-initialized */ + // assume _sg.d3d11 already is zero-initialized SOKOL_ASSERT(desc); SOKOL_ASSERT(desc->context.d3d11.device); SOKOL_ASSERT(desc->context.d3d11.device_context); @@ -8909,12 +9102,12 @@ _SOKOL_PRIVATE void _sg_d3d11_discard_backend(void) { } _SOKOL_PRIVATE void _sg_d3d11_clear_state(void) { - /* clear all the device context state, so that resource refs don't keep stuck in the d3d device context */ + // clear all the device context state, so that resource refs don't keep stuck in the d3d device context _sg_d3d11_ClearState(_sg.d3d11.ctx); } _SOKOL_PRIVATE void _sg_d3d11_reset_state_cache(void) { - /* just clear the d3d11 device context state */ + // just clear the d3d11 device context state _sg_d3d11_clear_state(); } @@ -8932,19 +9125,17 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_context(_sg_context_t* ctx) { _SOKOL_PRIVATE void _sg_d3d11_discard_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); - /* empty */ + // 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); - _sg_buffer_common_init(&buf->cmn, desc); const bool injected = (0 != desc->d3d11_buffer); if (injected) { buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer; _sg_d3d11_AddRef(buf->d3d11.buf); - } - else { + } else { D3D11_BUFFER_DESC d3d11_desc; _sg_clear(&d3d11_desc, sizeof(d3d11_desc)); d3d11_desc.ByteWidth = (UINT)buf->cmn.size; @@ -8961,7 +9152,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, cons } HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11.buf); if (!(SUCCEEDED(hr) && buf->d3d11.buf)) { - SG_LOG("failed to create D3D11 buffer\n"); + _SG_ERROR(D3D11_CREATE_BUFFER_FAILED); return SG_RESOURCESTATE_FAILED; } } @@ -8984,8 +9175,8 @@ _SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_ for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index]; - const int mip_width = ((img->cmn.width>>mip_index)>0) ? img->cmn.width>>mip_index : 1; - const int mip_height = ((img->cmn.height>>mip_index)>0) ? img->cmn.height>>mip_index : 1; + 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 sg_range* subimg_data = &(data->subimage[face_index][mip_index]); const size_t slice_size = subimg_data->size / (size_t)num_slices; const size_t slice_offset = slice_size * (size_t)slice_index; @@ -8993,10 +9184,9 @@ _SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_ subres_data->pSysMem = ptr + slice_offset; subres_data->SysMemPitch = (UINT)_sg_row_pitch(img->cmn.pixel_format, mip_width, 1); if (img->cmn.type == SG_IMAGETYPE_3D) { - /* FIXME? const int mip_depth = ((img->depth>>mip_index)>0) ? img->depth>>mip_index : 1; */ + // FIXME? const int mip_depth = _sg_miplevel_dim(img->depth, mip_index); subres_data->SysMemSlicePitch = (UINT)_sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); - } - else { + } else { subres_data->SysMemSlicePitch = 0; } } @@ -9006,238 +9196,213 @@ _SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); - SOKOL_ASSERT(!img->d3d11.tex2d && !img->d3d11.tex3d && !img->d3d11.texds && !img->d3d11.texmsaa); - SOKOL_ASSERT(!img->d3d11.srv && !img->d3d11.smp); + SOKOL_ASSERT((0 == img->d3d11.tex2d) && (0 == img->d3d11.tex3d) && (0 == img->d3d11.res) && (0 == img->d3d11.srv)); HRESULT hr; - _sg_image_common_init(&img->cmn, desc); const bool injected = (0 != desc->d3d11_texture) || (0 != desc->d3d11_shader_resource_view); const bool msaa = (img->cmn.sample_count > 1); - img->d3d11.format = _sg_d3d11_pixel_format(img->cmn.pixel_format); - - /* special case depth-stencil buffer? */ - if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { - /* create only a depth-texture */ - SOKOL_ASSERT(!injected); - if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { - SG_LOG("trying to create a D3D11 depth-texture with unsupported pixel format\n"); - return SG_RESOURCESTATE_FAILED; - } - D3D11_TEXTURE2D_DESC d3d11_desc; - _sg_clear(&d3d11_desc, sizeof(d3d11_desc)); - d3d11_desc.Width = (UINT)img->cmn.width; - d3d11_desc.Height = (UINT)img->cmn.height; - d3d11_desc.MipLevels = 1; - d3d11_desc.ArraySize = 1; - d3d11_desc.Format = img->d3d11.format; - d3d11_desc.Usage = D3D11_USAGE_DEFAULT; - d3d11_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; - d3d11_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; - d3d11_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); - hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_desc, NULL, &img->d3d11.texds); - if (!(SUCCEEDED(hr) && img->d3d11.texds)) { - SG_LOG("failed to create D3D11 texture 2D\n"); - return SG_RESOURCESTATE_FAILED; - } + img->d3d11.format = _sg_d3d11_texture_pixel_format(img->cmn.pixel_format); + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); + return SG_RESOURCESTATE_FAILED; } - else { - /* create (or inject) color texture and shader-resource-view */ - /* prepare initial content pointers */ - D3D11_SUBRESOURCE_DATA* init_data = 0; - if (!injected && (img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { - _sg_d3d11_fill_subres_data(img, &desc->data); - init_data = _sg.d3d11.subres_data; + // prepare initial content pointers + D3D11_SUBRESOURCE_DATA* init_data = 0; + if (!injected && (img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_d3d11_fill_subres_data(img, &desc->data); + init_data = _sg.d3d11.subres_data; + } + if (img->cmn.type != SG_IMAGETYPE_3D) { + // 2D-, cube- or array-texture + // first check for injected texture and/or resource view + if (injected) { + img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; + 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 (img->cmn.type != SG_IMAGETYPE_3D) { - /* 2D-, cube- or array-texture */ - /* if this is an MSAA render target, the following texture will be the 'resolve-texture' */ - /* first check for injected texture and/or resource view */ - if (injected) { - img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; - 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 + D3D11_TEXTURE2D_DESC d3d11_tex_desc; + _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; + switch (img->cmn.type) { + case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = (UINT)img->cmn.num_slices; break; + case SG_IMAGETYPE_CUBE: d3d11_tex_desc.ArraySize = 6; break; + default: d3d11_tex_desc.ArraySize = 1; break; } - - /* if not injected, create texture */ - if (0 == img->d3d11.tex2d) { - D3D11_TEXTURE2D_DESC d3d11_tex_desc; - _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); - d3d11_tex_desc.Width = (UINT)img->cmn.width; - d3d11_tex_desc.Height = (UINT)img->cmn.height; - d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; - switch (img->cmn.type) { - case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = (UINT)img->cmn.num_slices; break; - case SG_IMAGETYPE_CUBE: d3d11_tex_desc.ArraySize = 6; break; - default: d3d11_tex_desc.ArraySize = 1; break; - } - d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - d3d11_tex_desc.Format = img->d3d11.format; - if (img->cmn.render_target) { - d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; - if (!msaa) { - d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; - } - d3d11_tex_desc.CPUAccessFlags = 0; - } - else { - d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); - d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); - } - if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { - /* trying to create a texture format that's not supported by D3D */ - SG_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); - return SG_RESOURCESTATE_FAILED; + d3d11_tex_desc.Format = img->d3d11.format; + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { + d3d11_tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + } else { + d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; } - d3d11_tex_desc.SampleDesc.Count = 1; - d3d11_tex_desc.SampleDesc.Quality = 0; - d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; - - hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d); - if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) { - SG_LOG("failed to create D3D11 texture 2D\n"); - return SG_RESOURCESTATE_FAILED; + if (!msaa) { + d3d11_tex_desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; } + d3d11_tex_desc.CPUAccessFlags = 0; + } else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); } + d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; + d3d11_tex_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); + d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; - /* ...and similar, if not injected, create shader-resource-view */ - if (0 == img->d3d11.srv) { - D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); - d3d11_srv_desc.Format = img->d3d11.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_LOG("failed to create D3D11 resource view\n"); - return SG_RESOURCESTATE_FAILED; - } + hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d); + if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) { + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_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; - 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); - } + 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; } - - if (0 == img->d3d11.tex3d) { - D3D11_TEXTURE3D_DESC d3d11_tex_desc; - _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); - d3d11_tex_desc.Width = (UINT)img->cmn.width; - d3d11_tex_desc.Height = (UINT)img->cmn.height; - d3d11_tex_desc.Depth = (UINT)img->cmn.num_slices; - d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; - d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - d3d11_tex_desc.Format = img->d3d11.format; - if (img->cmn.render_target) { - d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; - if (!msaa) { - d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; - } - d3d11_tex_desc.CPUAccessFlags = 0; - } - else { - d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); - d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); - } - if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { - /* trying to create a texture format that's not supported by D3D */ - SG_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); - return SG_RESOURCESTATE_FAILED; - } - hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d); - if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) { - SG_LOG("failed to create D3D11 texture 3D\n"); - return SG_RESOURCESTATE_FAILED; - } + 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; } - - if (0 == img->d3d11.srv) { - D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); - d3d11_srv_desc.Format = img->d3d11.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_LOG("failed to create D3D11 resource view\n"); - 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; + 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); } } - /* also need to create a separate MSAA render target texture? */ - if (msaa) { - D3D11_TEXTURE2D_DESC d3d11_tex_desc; + if (0 == img->d3d11.tex3d) { + D3D11_TEXTURE3D_DESC d3d11_tex_desc; _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = (UINT)img->cmn.width; d3d11_tex_desc.Height = (UINT)img->cmn.height; - d3d11_tex_desc.MipLevels = 1; - d3d11_tex_desc.ArraySize = 1; + d3d11_tex_desc.Depth = (UINT)img->cmn.num_slices; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; d3d11_tex_desc.Format = img->d3d11.format; - d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; - d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; - d3d11_tex_desc.CPUAccessFlags = 0; - d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; - d3d11_tex_desc.SampleDesc.Quality = (UINT)D3D11_STANDARD_MULTISAMPLE_PATTERN; - hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, NULL, &img->d3d11.texmsaa); - if (!(SUCCEEDED(hr) && img->d3d11.texmsaa)) { - SG_LOG("failed to create D3D11 texture 2D\n"); + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; + d3d11_tex_desc.CPUAccessFlags = 0; + } else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); + } + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + _SG_ERROR(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); + return SG_RESOURCESTATE_FAILED; + } + hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d); + if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) { + _SG_ERROR(D3D11_CREATE_3D_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + 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; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->d3d11.tex2d) { + _sg_d3d11_Release(img->d3d11.tex2d); + } + if (img->d3d11.tex3d) { + _sg_d3d11_Release(img->d3d11.tex3d); + } + if (img->d3d11.res) { + _sg_d3d11_Release(img->d3d11.res); + } + if (img->d3d11.srv) { + _sg_d3d11_Release(img->d3d11.srv); + } +} - /* sampler state object, note D3D11 implements an internal shared-pool for sampler objects */ +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + SOKOL_ASSERT(0 == smp->d3d11.smp); + const bool injected = (0 != desc->d3d11_sampler); + if (injected) { + smp->d3d11.smp = (ID3D11SamplerState*)desc->d3d11_sampler; + _sg_d3d11_AddRef(smp->d3d11.smp); + } else { D3D11_SAMPLER_DESC d3d11_smp_desc; _sg_clear(&d3d11_smp_desc, sizeof(d3d11_smp_desc)); - d3d11_smp_desc.Filter = _sg_d3d11_filter(img->cmn.min_filter, img->cmn.mag_filter, img->cmn.max_anisotropy); - d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(img->cmn.wrap_u); - d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(img->cmn.wrap_v); - d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(img->cmn.wrap_w); - switch (img->cmn.border_color) { + d3d11_smp_desc.Filter = _sg_d3d11_filter(desc->min_filter, desc->mag_filter, desc->mipmap_filter, desc->compare != SG_COMPAREFUNC_NEVER, desc->max_anisotropy); + d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(desc->wrap_u); + d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(desc->wrap_v); + d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(desc->wrap_w); + d3d11_smp_desc.MipLODBias = 0.0f; // FIXME? + switch (desc->border_color) { case SG_BORDERCOLOR_TRANSPARENT_BLACK: - /* all 0.0f */ + // all 0.0f break; case SG_BORDERCOLOR_OPAQUE_WHITE: for (int i = 0; i < 4; i++) { @@ -9245,73 +9410,46 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const } break; default: - /* opaque black */ + // opaque black d3d11_smp_desc.BorderColor[3] = 1.0f; break; } - d3d11_smp_desc.MaxAnisotropy = img->cmn.max_anisotropy; - d3d11_smp_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + d3d11_smp_desc.MaxAnisotropy = desc->max_anisotropy; + d3d11_smp_desc.ComparisonFunc = _sg_d3d11_compare_func(desc->compare); d3d11_smp_desc.MinLOD = desc->min_lod; d3d11_smp_desc.MaxLOD = desc->max_lod; - hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11.smp); - if (!(SUCCEEDED(hr) && img->d3d11.smp)) { - SG_LOG("failed to create D3D11 sampler state\n"); + HRESULT hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &smp->d3d11.smp); + if (!(SUCCEEDED(hr) && smp->d3d11.smp)) { + _SG_ERROR(D3D11_CREATE_SAMPLER_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_d3d11_discard_image(_sg_image_t* img) { - SOKOL_ASSERT(img); - if (img->d3d11.tex2d) { - _sg_d3d11_Release(img->d3d11.tex2d); - } - if (img->d3d11.tex3d) { - _sg_d3d11_Release(img->d3d11.tex3d); - } - if (img->d3d11.texds) { - _sg_d3d11_Release(img->d3d11.texds); - } - if (img->d3d11.texmsaa) { - _sg_d3d11_Release(img->d3d11.texmsaa); - } - if (img->d3d11.srv) { - _sg_d3d11_Release(img->d3d11.srv); - } - if (img->d3d11.smp) { - _sg_d3d11_Release(img->d3d11.smp); +_SOKOL_PRIVATE void _sg_d3d11_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + if (smp->d3d11.smp) { + _sg_d3d11_Release(smp->d3d11.smp); } } _SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) { - /* on UWP, don't do anything (not tested) */ - #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - return true; - #else - /* load DLL on demand */ - if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) { - _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); - if (0 == _sg.d3d11.d3dcompiler_dll) { - /* don't attempt to load missing DLL in the future */ - SG_LOG("failed to load d3dcompiler_47.dll!\n"); - _sg.d3d11.d3dcompiler_dll_load_failed = true; - return false; - } - /* look up function pointers */ - _sg.d3d11.D3DCompile_func = (pD3DCompile)(void*) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile"); - SOKOL_ASSERT(_sg.d3d11.D3DCompile_func); + if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) { + _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); + if (0 == _sg.d3d11.d3dcompiler_dll) { + // don't attempt to load missing DLL in the future + _SG_ERROR(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED); + _sg.d3d11.d3dcompiler_dll_load_failed = true; + return false; } - return 0 != _sg.d3d11.d3dcompiler_dll; - #endif + // look up function pointers + _sg.d3d11.D3DCompile_func = (pD3DCompile)(void*) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile"); + SOKOL_ASSERT(_sg.d3d11.D3DCompile_func); + } + return 0 != _sg.d3d11.d3dcompiler_dll; } -#if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) -#define _sg_d3d11_D3DCompile D3DCompile -#else -#define _sg_d3d11_D3DCompile _sg.d3d11.D3DCompile_func -#endif - _SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* stage_desc) { if (!_sg_d3d11_load_d3dcompiler_dll()) { return NULL; @@ -9319,24 +9457,28 @@ _SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* st SOKOL_ASSERT(stage_desc->d3d11_target); ID3DBlob* output = NULL; ID3DBlob* errors_or_warnings = NULL; - HRESULT hr = _sg_d3d11_D3DCompile( - stage_desc->source, /* pSrcData */ - strlen(stage_desc->source), /* SrcDataSize */ - NULL, /* pSourceName */ - NULL, /* pDefines */ - NULL, /* pInclude */ - stage_desc->entry ? stage_desc->entry : "main", /* pEntryPoint */ - stage_desc->d3d11_target, /* pTarget (vs_5_0 or ps_5_0) */ - D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, /* Flags1 */ - 0, /* Flags2 */ - &output, /* ppCode */ - &errors_or_warnings); /* ppErrorMsgs */ + HRESULT hr = _sg.d3d11.D3DCompile_func( + stage_desc->source, // pSrcData + strlen(stage_desc->source), // SrcDataSize + NULL, // pSourceName + NULL, // pDefines + NULL, // pInclude + stage_desc->entry ? stage_desc->entry : "main", // pEntryPoint + stage_desc->d3d11_target, // pTarget + D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, // Flags1 + 0, // Flags2 + &output, // ppCode + &errors_or_warnings); // ppErrorMsgs + if (FAILED(hr)) { + _SG_ERROR(D3D11_SHADER_COMPILATION_FAILED); + } if (errors_or_warnings) { - SG_LOG((LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings)); + _SG_WARN(D3D11_SHADER_COMPILATION_OUTPUT); + _SG_LOGMSG(D3D11_SHADER_COMPILATION_OUTPUT, (LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings)); _sg_d3d11_Release(errors_or_warnings); errors_or_warnings = NULL; } if (FAILED(hr)) { - /* just in case, usually output is NULL here */ + // just in case, usually output is NULL here if (output) { _sg_d3d11_Release(output); output = NULL; @@ -9350,22 +9492,20 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.vs_blob); HRESULT hr; - _sg_shader_common_init(&shd->cmn, desc); - - /* copy vertex attribute semantic names and indices */ + // copy vertex attribute semantic names and indices for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { _sg_strcpy(&shd->d3d11.attrs[i].sem_name, desc->attrs[i].sem_name); shd->d3d11.attrs[i].sem_index = desc->attrs[i].sem_index; } - /* shader stage uniform blocks and image slots */ + // shader stage uniform blocks and image slots for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { - const _sg_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; + const _sg_shader_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; - /* create a D3D constant buffer for each uniform block */ + // create a D3D constant buffer for each uniform block SOKOL_ASSERT(0 == d3d11_stage->cbufs[ub_index]); D3D11_BUFFER_DESC cb_desc; _sg_clear(&cb_desc, sizeof(cb_desc)); @@ -9374,7 +9514,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &d3d11_stage->cbufs[ub_index]); if (!(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index])) { - SG_LOG("failed to create D3D11 buffer\n"); + _SG_ERROR(D3D11_CREATE_CONSTANT_BUFFER_FAILED); return SG_RESOURCESTATE_FAILED; } } @@ -9384,14 +9524,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons SIZE_T vs_length = 0, fs_length = 0; ID3DBlob* vs_blob = 0, *fs_blob = 0; if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { - /* create from shader byte code */ + // create from shader byte code vs_ptr = desc->vs.bytecode.ptr; fs_ptr = desc->fs.bytecode.ptr; vs_length = desc->vs.bytecode.size; fs_length = desc->fs.bytecode.size; - } - else { - /* compile from shader source code */ + } else { + // compile from shader source code vs_blob = _sg_d3d11_compile_shader(&desc->vs); fs_blob = _sg_d3d11_compile_shader(&desc->fs); if (vs_blob && fs_blob) { @@ -9403,13 +9542,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons } sg_resource_state result = SG_RESOURCESTATE_FAILED; if (vs_ptr && fs_ptr && (vs_length > 0) && (fs_length > 0)) { - /* create the D3D vertex- and pixel-shader objects */ + // create the D3D vertex- and pixel-shader objects hr = _sg_d3d11_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11.vs); bool vs_succeeded = SUCCEEDED(hr) && shd->d3d11.vs; hr = _sg_d3d11_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11.fs); bool fs_succeeded = SUCCEEDED(hr) && shd->d3d11.fs; - /* need to store the vertex shader byte code, this is needed later in sg_create_pipeline */ + // need to store the vertex shader byte code, this is needed later in sg_create_pipeline if (vs_succeeded && fs_succeeded) { shd->d3d11.vs_blob_length = vs_length; shd->d3d11.vs_blob = _sg_malloc((size_t)vs_length); @@ -9457,79 +9596,77 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, SOKOL_ASSERT(!pip->d3d11.il && !pip->d3d11.rs && !pip->d3d11.dss && !pip->d3d11.bs); pip->shader = shd; - _sg_pipeline_common_init(&pip->cmn, desc); pip->d3d11.index_format = _sg_d3d11_index_format(pip->cmn.index_type); pip->d3d11.topology = _sg_d3d11_primitive_topology(desc->primitive_type); pip->d3d11.stencil_ref = desc->stencil.ref; - /* create input layout object */ + // create input layout object HRESULT hr; D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]; _sg_clear(d3d11_comps, sizeof(d3d11_comps)); int attr_index = 0; for (; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; - const sg_vertex_step step_func = l_desc->step_func; - const int step_rate = l_desc->step_rate; + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index]; + const sg_vertex_step step_func = l_state->step_func; + const int step_rate = l_state->step_rate; D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index]; d3d11_comp->SemanticName = _sg_strptr(&shd->d3d11.attrs[attr_index].sem_name); d3d11_comp->SemanticIndex = (UINT)shd->d3d11.attrs[attr_index].sem_index; - d3d11_comp->Format = _sg_d3d11_vertex_format(a_desc->format); - d3d11_comp->InputSlot = (UINT)a_desc->buffer_index; - d3d11_comp->AlignedByteOffset = (UINT)a_desc->offset; + d3d11_comp->Format = _sg_d3d11_vertex_format(a_state->format); + d3d11_comp->InputSlot = (UINT)a_state->buffer_index; + d3d11_comp->AlignedByteOffset = (UINT)a_state->offset; d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { d3d11_comp->InstanceDataStepRate = (UINT)step_rate; pip->cmn.use_instanced_draw = true; } - pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; } - for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { - if (pip->cmn.vertex_layout_valid[layout_index]) { - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; - SOKOL_ASSERT(l_desc->stride > 0); - pip->d3d11.vb_strides[layout_index] = (UINT)l_desc->stride; - } - else { + for (int layout_index = 0; layout_index < SG_MAX_VERTEX_BUFFERS; layout_index++) { + if (pip->cmn.vertex_buffer_layout_active[layout_index]) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index]; + SOKOL_ASSERT(l_state->stride > 0); + pip->d3d11.vb_strides[layout_index] = (UINT)l_state->stride; + } else { 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 */ + 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_LOG("failed to create D3D11 input layout\n"); + _SG_ERROR(D3D11_CREATE_INPUT_LAYOUT_FAILED); return SG_RESOURCESTATE_FAILED; } - /* create rasterizer state */ + // create rasterizer state D3D11_RASTERIZER_DESC rs_desc; _sg_clear(&rs_desc, sizeof(rs_desc)); rs_desc.FillMode = D3D11_FILL_SOLID; rs_desc.CullMode = _sg_d3d11_cull_mode(desc->cull_mode); rs_desc.FrontCounterClockwise = desc->face_winding == SG_FACEWINDING_CCW; - rs_desc.DepthBias = (INT) pip->cmn.depth_bias; - rs_desc.DepthBiasClamp = pip->cmn.depth_bias_clamp; - rs_desc.SlopeScaledDepthBias = pip->cmn.depth_bias_slope_scale; + rs_desc.DepthBias = (INT) pip->cmn.depth.bias; + rs_desc.DepthBiasClamp = pip->cmn.depth.bias_clamp; + rs_desc.SlopeScaledDepthBias = pip->cmn.depth.bias_slope_scale; rs_desc.DepthClipEnable = TRUE; rs_desc.ScissorEnable = TRUE; rs_desc.MultisampleEnable = desc->sample_count > 1; rs_desc.AntialiasedLineEnable = FALSE; hr = _sg_d3d11_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11.rs); if (!(SUCCEEDED(hr) && pip->d3d11.rs)) { - SG_LOG("failed to create D3D11 rasterizer state\n"); + _SG_ERROR(D3D11_CREATE_RASTERIZER_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } - /* create depth-stencil state */ + // create depth-stencil state D3D11_DEPTH_STENCIL_DESC dss_desc; _sg_clear(&dss_desc, sizeof(dss_desc)); dss_desc.DepthEnable = TRUE; @@ -9550,11 +9687,11 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare); hr = _sg_d3d11_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11.dss); if (!(SUCCEEDED(hr) && pip->d3d11.dss)) { - SG_LOG("failed to create D3D11 depth stencil state\n"); + _SG_ERROR(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } - /* create blend state */ + // create blend state D3D11_BLEND_DESC bs_desc; _sg_clear(&bs_desc, sizeof(bs_desc)); bs_desc.AlphaToCoverageEnable = desc->alpha_to_coverage_enabled; @@ -9584,10 +9721,9 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, } hr = _sg_d3d11_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs); if (!(SUCCEEDED(hr) && pip->d3d11.bs)) { - SG_LOG("failed to create D3D11 blend state\n"); + _SG_ERROR(D3D11_CREATE_BLEND_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } - return SG_RESOURCESTATE_VALID; } @@ -9611,94 +9747,110 @@ _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** att_images, const sg_pass_desc* desc) { +_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_ASSERT(att_images && att_images[0]); + SOKOL_ASSERT(color_images && resolve_images); SOKOL_ASSERT(_sg.d3d11.dev); - _sg_pass_common_init(&pass->cmn, desc); - + // copy image pointers for (int i = 0; i < pass->cmn.num_color_atts; i++) { - const sg_pass_attachment_desc* att_desc = &desc->color_attachments[i]; - _SOKOL_UNUSED(att_desc); - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); - _sg_image_t* att_img = att_images[i]; - SOKOL_ASSERT(att_img && (att_img->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_img->cmn.pixel_format)); + const sg_pass_attachment_desc* color_desc = &desc->color_attachments[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].image); - pass->d3d11.color_atts[i].image = att_img; + 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]; + + const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == pass->d3d11.resolve_atts[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]; + } + } + SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); + const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + 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; + } - /* create D3D11 render-target-view */ - const _sg_pass_attachment_t* cmn_att = &pass->cmn.color_atts[i]; - SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].rtv); - ID3D11Resource* d3d11_res = 0; - const bool is_msaa = att_img->cmn.sample_count > 1; + // 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]; + const _sg_image_t* color_img = color_images[i]; + SOKOL_ASSERT(0 == pass->d3d11.color_atts[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)); - d3d11_rtv_desc.Format = att_img->d3d11.format; - if ((att_img->cmn.type == SG_IMAGETYPE_2D) || is_msaa) { - if (is_msaa) { - d3d11_res = (ID3D11Resource*) att_img->d3d11.texmsaa; + d3d11_rtv_desc.Format = _sg_d3d11_rtv_pixel_format(color_img->cmn.pixel_format); + if (color_img->cmn.type == SG_IMAGETYPE_2D) { + if (msaa) { d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; - } - else { - d3d11_res = (ID3D11Resource*) att_img->d3d11.tex2d; + } else { d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - d3d11_rtv_desc.Texture2D.MipSlice = (UINT)cmn_att->mip_level; + d3d11_rtv_desc.Texture2D.MipSlice = (UINT)cmn_color_att->mip_level; } - } - else if ((att_img->cmn.type == SG_IMAGETYPE_CUBE) || (att_img->cmn.type == SG_IMAGETYPE_ARRAY)) { - d3d11_res = (ID3D11Resource*) att_img->d3d11.tex2d; - d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; - d3d11_rtv_desc.Texture2DArray.MipSlice = (UINT)cmn_att->mip_level; - d3d11_rtv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_att->slice; - d3d11_rtv_desc.Texture2DArray.ArraySize = 1; - } - else { - SOKOL_ASSERT(att_img->cmn.type == SG_IMAGETYPE_3D); - d3d11_res = (ID3D11Resource*) att_img->d3d11.tex3d; + } else if ((color_img->cmn.type == SG_IMAGETYPE_CUBE) || (color_img->cmn.type == SG_IMAGETYPE_ARRAY)) { + if (msaa) { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_rtv_desc.Texture2DMSArray.FirstArraySlice = (UINT)cmn_color_att->slice; + d3d11_rtv_desc.Texture2DMSArray.ArraySize = 1; + } else { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + d3d11_rtv_desc.Texture2DArray.MipSlice = (UINT)cmn_color_att->mip_level; + d3d11_rtv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_color_att->slice; + d3d11_rtv_desc.Texture2DArray.ArraySize = 1; + } + } else { + SOKOL_ASSERT(color_img->cmn.type == SG_IMAGETYPE_3D); + SOKOL_ASSERT(!msaa); d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; - d3d11_rtv_desc.Texture3D.MipSlice = (UINT)cmn_att->mip_level; - d3d11_rtv_desc.Texture3D.FirstWSlice = (UINT)cmn_att->slice; + d3d11_rtv_desc.Texture3D.MipSlice = (UINT)cmn_color_att->mip_level; + d3d11_rtv_desc.Texture3D.FirstWSlice = (UINT)cmn_color_att->slice; d3d11_rtv_desc.Texture3D.WSize = 1; } - SOKOL_ASSERT(d3d11_res); - HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, d3d11_res, &d3d11_rtv_desc, &pass->d3d11.color_atts[i].rtv); - if (!(SUCCEEDED(hr) && pass->d3d11.color_atts[i].rtv)) { - SG_LOG("failed to create D3D11 render target view\n"); + 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)) { + _SG_ERROR(D3D11_CREATE_RTV_FAILED); return SG_RESOURCESTATE_FAILED; } } - - /* optional depth-stencil image */ - SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); - SOKOL_ASSERT(0 == pass->d3d11.ds_att.dsv); - if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; - const sg_pass_attachment_desc* att_desc = &desc->depth_stencil_attachment; - _SOKOL_UNUSED(att_desc); - _sg_image_t* att_img = att_images[ds_img_index]; - SOKOL_ASSERT(att_img && (att_img->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_img->cmn.pixel_format)); - SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); - pass->d3d11.ds_att.image = att_img; - - /* create D3D11 depth-stencil-view */ + SOKOL_ASSERT(0 == pass->d3d11.ds_att.view.dsv); + if (ds_desc->image.id != SG_INVALID_ID) { + const _sg_pass_attachment_t* cmn_ds_att = &pass->cmn.ds_att; + 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)); - d3d11_dsv_desc.Format = att_img->d3d11.format; - const bool is_msaa = att_img->cmn.sample_count > 1; - if (is_msaa) { - d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; - } - else { - d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; - } - ID3D11Resource* d3d11_res = (ID3D11Resource*) att_img->d3d11.texds; - SOKOL_ASSERT(d3d11_res); - HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, d3d11_res, &d3d11_dsv_desc, &pass->d3d11.ds_att.dsv); - if (!(SUCCEEDED(hr) && pass->d3d11.ds_att.dsv)) { - SG_LOG("failed to create D3D11 depth stencil view\n"); + d3d11_dsv_desc.Format = _sg_d3d11_dsv_pixel_format(ds_img->cmn.pixel_format); + SOKOL_ASSERT(ds_img && ds_img->cmn.type != SG_IMAGETYPE_3D); + if (ds_img->cmn.type == SG_IMAGETYPE_2D) { + if (msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + } else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + d3d11_dsv_desc.Texture2D.MipSlice = (UINT)cmn_ds_att->mip_level; + } + } else if ((ds_img->cmn.type == SG_IMAGETYPE_CUBE) || (ds_img->cmn.type == SG_IMAGETYPE_ARRAY)) { + if (msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_dsv_desc.Texture2DMSArray.FirstArraySlice = (UINT)cmn_ds_att->slice; + d3d11_dsv_desc.Texture2DMSArray.ArraySize = 1; + } else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + d3d11_dsv_desc.Texture2DArray.MipSlice = (UINT)cmn_ds_att->mip_level; + d3d11_dsv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_ds_att->slice; + d3d11_dsv_desc.Texture2DArray.ArraySize = 1; + } + } + 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)) { + _SG_ERROR(D3D11_CREATE_DSV_FAILED); return SG_RESOURCESTATE_FAILED; } } @@ -9709,23 +9861,29 @@ _SOKOL_PRIVATE void _sg_d3d11_discard_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); SOKOL_ASSERT(pass != _sg.d3d11.cur_pass); for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->d3d11.color_atts[i].rtv) { - _sg_d3d11_Release(pass->d3d11.color_atts[i].rtv); + if (pass->d3d11.color_atts[i].view.rtv) { + _sg_d3d11_Release(pass->d3d11.color_atts[i].view.rtv); + } + if (pass->d3d11.resolve_atts[i].view.rtv) { + _sg_d3d11_Release(pass->d3d11.resolve_atts[i].view.rtv); } } - if (pass->d3d11.ds_att.dsv) { - _sg_d3d11_Release(pass->d3d11.ds_att.dsv); + if (pass->d3d11.ds_att.view.dsv) { + _sg_d3d11_Release(pass->d3d11.ds_att.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)); - /* NOTE: may return null */ return pass->d3d11.color_atts[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_pass_ds_image(const _sg_pass_t* pass) { - /* NOTE: may return null */ SOKOL_ASSERT(pass); return pass->d3d11.ds_att.image; } @@ -9743,22 +9901,20 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* _sg.d3d11.cur_pass_id.id = pass->slot.id; _sg.d3d11.num_rtvs = 0; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg.d3d11.cur_rtvs[i] = pass->d3d11.color_atts[i].rtv; + _sg.d3d11.cur_rtvs[i] = pass->d3d11.color_atts[i].view.rtv; if (_sg.d3d11.cur_rtvs[i]) { _sg.d3d11.num_rtvs++; } } - _sg.d3d11.cur_dsv = pass->d3d11.ds_att.dsv; - } - else { - /* render to default frame buffer */ + _sg.d3d11.cur_dsv = pass->d3d11.ds_att.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 { + } 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++) { @@ -9766,16 +9922,15 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* } if (_sg.d3d11.dsv_cb) { _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_cb(); - } - else { + } 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); } - /* apply the render-target- and depth-stencil-views */ + // 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); - /* set viewport and scissor rect to cover whole screen */ + // set viewport and scissor rect to cover whole screen D3D11_VIEWPORT vp; _sg_clear(&vp, sizeof(vp)); vp.Width = (FLOAT) w; @@ -9789,25 +9944,25 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* rect.bottom = h; _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); - /* perform clear action */ + // perform clear action for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { - if (action->colors[i].action == SG_ACTION_CLEAR) { - _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, _sg.d3d11.cur_rtvs[i], &action->colors[i].value.r); + 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); } } UINT ds_flags = 0; - if (action->depth.action == SG_ACTION_CLEAR) { + if (action->depth.load_action == SG_LOADACTION_CLEAR) { ds_flags |= D3D11_CLEAR_DEPTH; } - if (action->stencil.action == SG_ACTION_CLEAR) { + 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.value, action->stencil.value); + _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, _sg.d3d11.cur_dsv, ds_flags, action->depth.clear_value, action->stencil.clear_value); } } -/* D3D11CalcSubresource only exists for C++ */ +// D3D11CalcSubresource only exists for C++ _SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { return mip_slice + array_slice * mip_levels; } @@ -9816,27 +9971,37 @@ _SOKOL_PRIVATE void _sg_d3d11_end_pass(void) { SOKOL_ASSERT(_sg.d3d11.in_pass && _sg.d3d11.ctx); _sg.d3d11.in_pass = false; - /* need to resolve MSAA render target into texture? */ + // 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++) { - _sg_pass_attachment_t* cmn_att = &_sg.d3d11.cur_pass->cmn.color_atts[i]; - _sg_image_t* att_img = _sg.d3d11.cur_pass->d3d11.color_atts[i].image; - SOKOL_ASSERT(att_img && (att_img->slot.id == cmn_att->image_id.id)); - if (att_img->cmn.sample_count > 1) { - /* FIXME: support MSAA resolve into 3D texture */ - SOKOL_ASSERT(att_img->d3d11.tex2d && att_img->d3d11.texmsaa && !att_img->d3d11.tex3d); - SOKOL_ASSERT(DXGI_FORMAT_UNKNOWN != att_img->d3d11.format); - UINT dst_subres = _sg_d3d11_calcsubresource((UINT)cmn_att->mip_level, (UINT)cmn_att->slice, (UINT)att_img->cmn.num_mipmaps); + const _sg_image_t* resolve_img = _sg.d3d11.cur_pass->d3d11.resolve_atts[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]; + 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); + SOKOL_ASSERT(resolve_img->cmn.sample_count == 1); + const UINT src_subres = _sg_d3d11_calcsubresource( + (UINT)cmn_color_att->mip_level, + (UINT)cmn_color_att->slice, + (UINT)color_img->cmn.num_mipmaps); + const UINT dst_subres = _sg_d3d11_calcsubresource( + (UINT)cmn_resolve_att->mip_level, + (UINT)cmn_resolve_att->slice, + (UINT)resolve_img->cmn.num_mipmaps); _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, - (ID3D11Resource*) att_img->d3d11.tex2d, /* pDstResource */ - dst_subres, /* DstSubresource */ - (ID3D11Resource*) att_img->d3d11.texmsaa, /* pSrcResource */ - 0, /* SrcSubresource */ - att_img->d3d11.format); + resolve_img->d3d11.res, + dst_subres, + color_img->d3d11.res, + src_subres, + color_img->d3d11.format); } } } + _sg.d3d11.cur_pass = 0; _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; _sg.d3d11.cur_pipeline = 0; @@ -9900,57 +10065,49 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_bindings( _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) + _sg_image_t** fs_imgs, int num_fs_imgs, + _sg_sampler_t** vs_smps, int num_vs_smps, + _sg_sampler_t** fs_smps, int num_fs_smps) { SOKOL_ASSERT(pip); SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(_sg.d3d11.in_pass); - /* gather all the D3D11 resources into arrays */ + // gather all the D3D11 resources into arrays ID3D11Buffer* d3d11_ib = ib ? ib->d3d11.buf : 0; - ID3D11Buffer* d3d11_vbs[SG_MAX_SHADERSTAGE_BUFFERS]; - UINT d3d11_vb_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; - ID3D11ShaderResourceView* d3d11_vs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; - ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_IMAGES]; - ID3D11ShaderResourceView* d3d11_fs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; - ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_IMAGES]; - int i; - for (i = 0; i < num_vbs; i++) { + 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}; + ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; + ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; + for (int i = 0; i < num_vbs; i++) { SOKOL_ASSERT(vbs[i]->d3d11.buf); d3d11_vbs[i] = vbs[i]->d3d11.buf; d3d11_vb_offsets[i] = (UINT)vb_offsets[i]; } - for (; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { - d3d11_vbs[i] = 0; - d3d11_vb_offsets[i] = 0; - } - for (i = 0; i < num_vs_imgs; i++) { + for (int i = 0; i < num_vs_imgs; i++) { SOKOL_ASSERT(vs_imgs[i]->d3d11.srv); - SOKOL_ASSERT(vs_imgs[i]->d3d11.smp); d3d11_vs_srvs[i] = vs_imgs[i]->d3d11.srv; - d3d11_vs_smps[i] = vs_imgs[i]->d3d11.smp; } - for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - d3d11_vs_srvs[i] = 0; - d3d11_vs_smps[i] = 0; - } - for (i = 0; i < num_fs_imgs; i++) { + for (int i = 0; i < num_fs_imgs; i++) { SOKOL_ASSERT(fs_imgs[i]->d3d11.srv); - SOKOL_ASSERT(fs_imgs[i]->d3d11.smp); d3d11_fs_srvs[i] = fs_imgs[i]->d3d11.srv; - d3d11_fs_smps[i] = fs_imgs[i]->d3d11.smp; } - for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - d3d11_fs_srvs[i] = 0; - d3d11_fs_smps[i] = 0; + for (int i = 0; i < num_vs_smps; i++) { + SOKOL_ASSERT(vs_smps[i]->d3d11.smp); + d3d11_vs_smps[i] = vs_smps[i]->d3d11.smp; } - - _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, d3d11_vbs, pip->d3d11.vb_strides, d3d11_vb_offsets); + for (int i = 0; i < num_fs_smps; i++) { + SOKOL_ASSERT(fs_smps[i]->d3d11.smp); + d3d11_fs_smps[i] = fs_smps[i]->d3d11.smp; + } + _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_VERTEX_BUFFERS, d3d11_vbs, pip->d3d11.vb_strides, d3d11_vb_offsets); _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, pip->d3d11.index_format, (UINT)ib_offset); _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_srvs); - _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_smps); _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_srvs); - _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_smps); + _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); } _SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { @@ -9969,16 +10126,13 @@ _SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_i if (_sg.d3d11.use_indexed_draw) { if (_sg.d3d11.use_instanced_draw) { _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); - } - else { + } else { _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0); } - } - else { + } else { if (_sg.d3d11.use_instanced_draw) { _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); - } - else { + } else { _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element); } } @@ -9998,7 +10152,7 @@ _SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* d memcpy(d3d11_msr.pData, data->ptr, data->size); _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); } else { - SG_LOG("failed to map buffer while updating!\n"); + _SG_ERROR(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED); } } @@ -10014,24 +10168,16 @@ _SOKOL_PRIVATE int _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* da memcpy(dst_ptr, data->ptr, data->size); _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); } else { - SG_LOG("failed to map buffer while appending!\n"); + _SG_ERROR(D3D11_MAP_FOR_APPEND_BUFFER_FAILED); } - /* NOTE: this alignment is a requirement from WebGPU, but we want identical behaviour across all backend */ + // NOTE: this alignment is a requirement from WebGPU, but we want identical behaviour across all backend return _sg_roundup((int)data->size, 4); } _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.tex2d || img->d3d11.tex3d); - ID3D11Resource* d3d11_res = 0; - if (img->d3d11.tex3d) { - d3d11_res = (ID3D11Resource*) img->d3d11.tex3d; - } - else { - d3d11_res = (ID3D11Resource*) img->d3d11.tex2d; - } - SOKOL_ASSERT(d3d11_res); + 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; UINT subres_index = 0; @@ -10041,20 +10187,19 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data for (int slice_index = 0; slice_index < num_slices; slice_index++) { for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); - const int mip_width = ((img->cmn.width>>mip_index)>0) ? img->cmn.width>>mip_index : 1; - const int mip_height = ((img->cmn.height>>mip_index)>0) ? img->cmn.height>>mip_index : 1; + 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 sg_range* subimg_data = &(data->subimage[face_index][mip_index]); const size_t slice_size = subimg_data->size / (size_t)num_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, d3d11_res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + hr = _sg_d3d11_Map(_sg.d3d11.ctx, img->d3d11.res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); if (SUCCEEDED(hr)) { - /* FIXME: need to handle difference in depth-pitch for 3D textures as well! */ + // 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 { + } 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; @@ -10064,16 +10209,22 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data dst_ptr += d3d11_msr.RowPitch; } } - _sg_d3d11_Unmap(_sg.d3d11.ctx, d3d11_res, subres_index); + _sg_d3d11_Unmap(_sg.d3d11.ctx, img->d3d11.res, subres_index); } else { - SG_LOG("failed to map texture!\n"); + _SG_ERROR(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED); } } } } } -/*== METAL BACKEND IMPLEMENTATION ============================================*/ +// ███ ███ ███████ ████████ █████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ████ ██ █████ ██ ███████ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>metal backend #elif defined(SOKOL_METAL) #if __has_feature(objc_arc) @@ -10084,31 +10235,55 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data #define _SG_OBJC_RELEASE(obj) { [obj release]; obj = nil; } #endif -/*-- enum translation functions ----------------------------------------------*/ -_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_action a) { +//-- enum translation functions ------------------------------------------------ +_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_load_action a) { + switch (a) { + case SG_LOADACTION_CLEAR: return MTLLoadActionClear; + case SG_LOADACTION_LOAD: return MTLLoadActionLoad; + case SG_LOADACTION_DONTCARE: return MTLLoadActionDontCare; + default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; + } +} + +_SOKOL_PRIVATE MTLStoreAction _sg_mtl_store_action(sg_store_action a, bool resolve) { switch (a) { - case SG_ACTION_CLEAR: return MTLLoadActionClear; - case SG_ACTION_LOAD: return MTLLoadActionLoad; - case SG_ACTION_DONTCARE: return MTLLoadActionDontCare; - default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; + case SG_STOREACTION_STORE: + if (resolve) { + return MTLStoreActionStoreAndMultisampleResolve; + } else { + return MTLStoreActionStore; + } + break; + case SG_STOREACTION_DONTCARE: + if (resolve) { + return MTLStoreActionMultisampleResolve; + } else { + return MTLStoreActionDontCare; + } + break; + default: SOKOL_UNREACHABLE; return (MTLStoreAction)0; + } +} + +_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_resource_options_storage_mode_managed_or_shared(void) { + #if defined(_SG_TARGET_MACOS) + if (_sg.mtl.force_managed_storage_mode || !_sg.mtl.has_unified_memory) { + return MTLResourceStorageModeManaged; + } else { + return MTLResourceStorageModeShared; } + #else + return MTLResourceStorageModeShared; + #endif } _SOKOL_PRIVATE MTLResourceOptions _sg_mtl_buffer_resource_options(sg_usage usg) { switch (usg) { case SG_USAGE_IMMUTABLE: - #if defined(_SG_TARGET_MACOS) - return MTLResourceStorageModeManaged; - #else - return MTLResourceStorageModeShared; - #endif + return _sg_mtl_resource_options_storage_mode_managed_or_shared(); case SG_USAGE_DYNAMIC: case SG_USAGE_STREAM: - #if defined(_SG_TARGET_MACOS) - return MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; - #else - return MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared; - #endif + return MTLResourceCPUCacheModeWriteCombined | _sg_mtl_resource_options_storage_mode_managed_or_shared(); default: SOKOL_UNREACHABLE; return 0; @@ -10360,22 +10535,29 @@ _SOKOL_PRIVATE bool _sg_mtl_is_pvrtc(sg_pixel_format fmt) { } _SOKOL_PRIVATE MTLSamplerAddressMode _sg_mtl_address_mode(sg_wrap w) { + if (_sg.features.image_clamp_to_border) { + if (@available(macOS 12.0, iOS 14.0, *)) { + // border color feature available + switch (w) { + case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; + case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToBorderColor; + case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; + default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; + } + } + } + // fallthrough: clamp to border no supported switch (w) { case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; - #if defined(_SG_TARGET_MACOS) - case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToBorderColor; - #else - /* clamp-to-border not supported on iOS, fall back to clamp-to-edge */ case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToEdge; - #endif case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; } } -#if defined(_SG_TARGET_MACOS) -_SOKOL_PRIVATE MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { +_SOKOL_PRIVATE API_AVAILABLE(ios(14.0), macos(12.0)) MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { switch (c) { case SG_BORDERCOLOR_TRANSPARENT_BLACK: return MTLSamplerBorderColorTransparentBlack; case SG_BORDERCOLOR_OPAQUE_BLACK: return MTLSamplerBorderColorOpaqueBlack; @@ -10383,46 +10565,38 @@ _SOKOL_PRIVATE MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { default: SOKOL_UNREACHABLE; return (MTLSamplerBorderColor)0; } } -#endif _SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) { switch (f) { case SG_FILTER_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_LINEAR: return MTLSamplerMinMagFilterNearest; case SG_FILTER_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - case SG_FILTER_LINEAR_MIPMAP_LINEAR: return MTLSamplerMinMagFilterLinear; default: SOKOL_UNREACHABLE; return (MTLSamplerMinMagFilter)0; } } -_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mip_filter(sg_filter f) { +_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mipmap_filter(sg_filter f) { switch (f) { - case SG_FILTER_NEAREST: - case SG_FILTER_LINEAR: + case SG_FILTER_NONE: return MTLSamplerMipFilterNotMipmapped; - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: + case SG_FILTER_NEAREST: return MTLSamplerMipFilterNearest; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_LINEAR: + case SG_FILTER_LINEAR: return MTLSamplerMipFilterLinear; default: SOKOL_UNREACHABLE; return (MTLSamplerMipFilter)0; } } -/*-- a pool for all Metal resource objects, with deferred release queue -------*/ - +//-- a pool for all Metal resource objects, with deferred release queue --------- _SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { _sg.mtl.idpool.num_slots = 2 * ( 2 * desc->buffer_pool_size + - 5 * desc->image_pool_size + + 4 * desc->image_pool_size + + 1 * desc->sampler_pool_size + 4 * desc->shader_pool_size + 2 * desc->pipeline_pool_size + desc->pass_pool_size @@ -10434,17 +10608,14 @@ _SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { [_sg.mtl.idpool.pool addObject:null]; } SOKOL_ASSERT([_sg.mtl.idpool.pool count] == (NSUInteger)_sg.mtl.idpool.num_slots); - /* a queue of currently free slot indices */ + // a queue of currently free slot indices _sg.mtl.idpool.free_queue_top = 0; _sg.mtl.idpool.free_queue = (int*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(int)); - /* pool slot 0 is reserved! */ + // pool slot 0 is reserved! for (int i = _sg.mtl.idpool.num_slots-1; i >= 1; i--) { _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = i; } - /* a circular queue which holds release items (frame index - when a resource is to be released, and the resource's - pool index - */ + // a circular queue which holds release items (frame index when a resource is to be released, and the resource's pool index _sg.mtl.idpool.release_queue_front = 0; _sg.mtl.idpool.release_queue_back = 0; _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t)); @@ -10460,7 +10631,7 @@ _SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { _SG_OBJC_RELEASE(_sg.mtl.idpool.pool); } -/* get a new free resource pool slot */ +// get a new free resource pool slot _SOKOL_PRIVATE int _sg_mtl_alloc_pool_slot(void) { SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top > 0); const int slot_index = _sg.mtl.idpool.free_queue[--_sg.mtl.idpool.free_queue_top]; @@ -10468,14 +10639,14 @@ _SOKOL_PRIVATE int _sg_mtl_alloc_pool_slot(void) { return slot_index; } -/* put a free resource pool slot back into the free-queue */ +// put a free resource pool slot back into the free-queue _SOKOL_PRIVATE void _sg_mtl_free_pool_slot(int slot_index) { SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top < _sg.mtl.idpool.num_slots); SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = slot_index; } -/* add an MTLResource to the pool, return pool index or 0 if input was 'nil' */ +// add an MTLResource to the pool, return pool index or 0 if input was 'nil' _SOKOL_PRIVATE int _sg_mtl_add_resource(id res) { if (nil == res) { return _SG_MTL_INVALID_SLOT_INDEX; @@ -10500,10 +10671,10 @@ _SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_inde SOKOL_ASSERT([NSNull null] != _sg.mtl.idpool.pool[(NSUInteger)slot_index]); int release_index = _sg.mtl.idpool.release_queue_front++; if (_sg.mtl.idpool.release_queue_front >= _sg.mtl.idpool.num_slots) { - /* wrap-around */ + // wrap-around _sg.mtl.idpool.release_queue_front = 0; } - /* release queue full? */ + // release queue full? SOKOL_ASSERT(_sg.mtl.idpool.release_queue_front != _sg.mtl.idpool.release_queue_back); SOKOL_ASSERT(0 == _sg.mtl.idpool.release_queue[release_index].frame_index); const uint32_t safe_to_release_frame_index = frame_index + SG_NUM_INFLIGHT_FRAMES + 1; @@ -10511,96 +10682,42 @@ _SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_inde _sg.mtl.idpool.release_queue[release_index].slot_index = slot_index; } -/* run garbage-collection pass on all resources in the release-queue */ +// run garbage-collection pass on all resources in the release-queue _SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { while (_sg.mtl.idpool.release_queue_back != _sg.mtl.idpool.release_queue_front) { if (frame_index < _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index) { - /* don't need to check further, release-items past this are too young */ + // don't need to check further, release-items past this are too young break; } - /* safe to release this resource */ + // safe to release this resource const int slot_index = _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index; SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); - /* note: the NSMutableArray takes ownership of its items, assigning an NSNull object will - release the object, no matter if using ARC or not - */ + // note: the NSMutableArray takes ownership of its items, assigning an NSNull object will + // release the object, no matter if using ARC or not SOKOL_ASSERT(_sg.mtl.idpool.pool[(NSUInteger)slot_index] != [NSNull null]); _sg.mtl.idpool.pool[(NSUInteger)slot_index] = [NSNull null]; - /* put the now free pool index back on the free queue */ + // put the now free pool index back on the free queue _sg_mtl_free_pool_slot(slot_index); - /* reset the release queue slot and advance the back index */ + // reset the release queue slot and advance the back index _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index = 0; _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index = _SG_MTL_INVALID_SLOT_INDEX; _sg.mtl.idpool.release_queue_back++; - if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) { - /* wrap-around */ - _sg.mtl.idpool.release_queue_back = 0; - } - } -} - -_SOKOL_PRIVATE id _sg_mtl_id(int slot_index) { - return _sg.mtl.idpool.pool[(NSUInteger)slot_index]; -} - -_SOKOL_PRIVATE void _sg_mtl_init_sampler_cache(const sg_desc* desc) { - SOKOL_ASSERT(desc->sampler_cache_size > 0); - _sg_smpcache_init(&_sg.mtl.sampler_cache, desc->sampler_cache_size); -} - -/* destroy the sampler cache, and release all sampler objects */ -_SOKOL_PRIVATE void _sg_mtl_destroy_sampler_cache(uint32_t frame_index) { - SOKOL_ASSERT(_sg.mtl.sampler_cache.items); - SOKOL_ASSERT(_sg.mtl.sampler_cache.num_items <= _sg.mtl.sampler_cache.capacity); - for (int i = 0; i < _sg.mtl.sampler_cache.num_items; i++) { - _sg_mtl_release_resource(frame_index, (int)_sg_smpcache_sampler(&_sg.mtl.sampler_cache, i)); + if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) { + // wrap-around + _sg.mtl.idpool.release_queue_back = 0; + } } - _sg_smpcache_discard(&_sg.mtl.sampler_cache); } -/* - create and add an MTLSamplerStateObject and return its resource pool index, - reuse identical sampler state if one exists -*/ -_SOKOL_PRIVATE int _sg_mtl_create_sampler(id mtl_device, const sg_image_desc* img_desc) { - SOKOL_ASSERT(img_desc); - int index = _sg_smpcache_find_item(&_sg.mtl.sampler_cache, img_desc); - if (index >= 0) { - /* reuse existing sampler */ - return (int)_sg_smpcache_sampler(&_sg.mtl.sampler_cache, index); - } - else { - /* create a new Metal sampler state object and add to sampler cache */ - MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; - mtl_desc.sAddressMode = _sg_mtl_address_mode(img_desc->wrap_u); - mtl_desc.tAddressMode = _sg_mtl_address_mode(img_desc->wrap_v); - if (SG_IMAGETYPE_3D == img_desc->type) { - mtl_desc.rAddressMode = _sg_mtl_address_mode(img_desc->wrap_w); - } - #if defined(_SG_TARGET_MACOS) - mtl_desc.borderColor = _sg_mtl_border_color(img_desc->border_color); - #endif - mtl_desc.minFilter = _sg_mtl_minmag_filter(img_desc->min_filter); - mtl_desc.magFilter = _sg_mtl_minmag_filter(img_desc->mag_filter); - mtl_desc.mipFilter = _sg_mtl_mip_filter(img_desc->min_filter); - mtl_desc.lodMinClamp = img_desc->min_lod; - mtl_desc.lodMaxClamp = img_desc->max_lod; - mtl_desc.maxAnisotropy = img_desc->max_anisotropy; - mtl_desc.normalizedCoordinates = YES; - id mtl_sampler = [mtl_device newSamplerStateWithDescriptor:mtl_desc]; - _SG_OBJC_RELEASE(mtl_desc); - int sampler_handle = _sg_mtl_add_resource(mtl_sampler); - _SG_OBJC_RELEASE(mtl_sampler); - _sg_smpcache_add_item(&_sg.mtl.sampler_cache, img_desc, (uintptr_t)sampler_handle); - return sampler_handle; - } +_SOKOL_PRIVATE id _sg_mtl_id(int slot_index) { + return _sg.mtl.idpool.pool[(NSUInteger)slot_index]; } _SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { _sg_clear(&_sg.mtl.state_cache, sizeof(_sg.mtl.state_cache)); } -/* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */ +// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { #if defined(_SG_TARGET_MACOS) _sg.backend = SG_BACKEND_METAL_MACOS; @@ -10611,20 +10728,22 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg.backend = SG_BACKEND_METAL_IOS; #endif #endif - _sg.features.instancing = true; _sg.features.origin_top_left = true; - _sg.features.multiple_render_targets = true; - _sg.features.msaa_render_targets = true; - _sg.features.imagetype_3d = true; - _sg.features.imagetype_array = true; - #if defined(_SG_TARGET_MACOS) - _sg.features.image_clamp_to_border = true; - #else - _sg.features.image_clamp_to_border = false; - #endif _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; + _sg.features.image_clamp_to_border = false; + if (@available(macOS 12.0, iOS 14.0, *)) { + _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple7] + || [_sg.mtl.device supportsFamily:MTLGPUFamilyApple8] + || [_sg.mtl.device supportsFamily:MTLGPUFamilyMac2]; + if (!_sg.features.image_clamp_to_border) { + if (@available(macOS 13.0, iOS 16.0, *)) { + _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyMetal3]; + } + } + } + #if defined(_SG_TARGET_MACOS) _sg.limits.max_image_size_2d = 16 * 1024; _sg.limits.max_image_size_cube = 16 * 1024; @@ -10632,7 +10751,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg.limits.max_image_size_array = 16 * 1024; _sg.limits.max_image_array_layers = 2 * 1024; #else - /* newer iOS devices support 16k textures */ + // FIXME: newer iOS devices support 16k textures _sg.limits.max_image_size_2d = 8 * 1024; _sg.limits.max_image_size_cube = 8 * 1024; _sg.limits.max_image_size_3d = 2 * 1024; @@ -10743,16 +10862,15 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { #endif } -/*-- main Metal backend state and functions ----------------------------------*/ +//-- main Metal backend state and functions ------------------------------------ _SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { - /* assume already zero-initialized */ + // 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->uniform_buffer_size > 0); _sg_mtl_init_pool(desc); - _sg_mtl_init_sampler_cache(desc); _sg_mtl_clear_state_cache(); _sg.mtl.valid = true; _sg.mtl.renderpass_descriptor_cb = desc->context.metal.renderpass_descriptor_cb; @@ -10771,20 +10889,29 @@ _SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared ]; } + if (@available(macOS 10.15, iOS 13.0, *)) { + _sg.mtl.has_unified_memory = _sg.mtl.device.hasUnifiedMemory; + } else { + #if defined(_SG_TARGET_MACOS) + _sg.mtl.has_unified_memory = false; + #else + _sg.mtl.has_unified_memory = true; + #endif + } + _sg.mtl.force_managed_storage_mode = desc->mtl_force_managed_storage_mode; _sg_mtl_init_caps(); } _SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { SOKOL_ASSERT(_sg.mtl.valid); - /* wait for the last frame to finish */ + // wait for the last frame to finish for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); } - /* semaphore must be "relinquished" before destruction */ + // semaphore must be "relinquished" before destruction for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { dispatch_semaphore_signal(_sg.mtl.sem); } - _sg_mtl_destroy_sampler_cache(_sg.mtl.frame_index); _sg_mtl_garbage_collect(_sg.mtl.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); _sg_mtl_destroy_pool(); _sg.mtl.valid = false; @@ -10795,7 +10922,7 @@ _SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { _SG_OBJC_RELEASE(_sg.mtl.uniform_buffers[i]); } - /* NOTE: MTLCommandBuffer and MTLRenderCommandEncoder are auto-released */ + // NOTE: MTLCommandBuffer and MTLRenderCommandEncoder are auto-released _sg.mtl.cmd_buffer = nil; _sg.mtl.present_cmd_buffer = nil; _sg.mtl.cmd_encoder = nil; @@ -10817,10 +10944,7 @@ _SOKOL_PRIVATE void _sg_mtl_bind_uniform_buffers(void) { _SOKOL_PRIVATE void _sg_mtl_reset_state_cache(void) { _sg_mtl_clear_state_cache(); - - /* need to restore the uniform buffer binding (normally happens in - _sg_mtl_begin_pass() - */ + // need to restore the uniform buffer binding (normally happens in _sg_mtl_begin_pass() if (nil != _sg.mtl.cmd_encoder) { _sg_mtl_bind_uniform_buffers(); } @@ -10835,7 +10959,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_context(_sg_context_t* ctx) { _SOKOL_PRIVATE void _sg_mtl_discard_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); - /* empty */ + // empty } _SOKOL_PRIVATE void _sg_mtl_activate_context(_sg_context_t* ctx) { @@ -10845,7 +10969,6 @@ _SOKOL_PRIVATE void _sg_mtl_activate_context(_sg_context_t* ctx) { _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); - _sg_buffer_common_init(&buf->cmn, desc); const bool injected = (0 != desc->mtl_buffers[0]); MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(buf->cmn.usage); for (int slot = 0; slot < buf->cmn.num_slots; slot++) { @@ -10853,13 +10976,11 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const if (injected) { SOKOL_ASSERT(desc->mtl_buffers[slot]); mtl_buf = (__bridge id) desc->mtl_buffers[slot]; - } - else { + } else { if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { SOKOL_ASSERT(desc->data.ptr); mtl_buf = [_sg.mtl.device newBufferWithBytes:desc->data.ptr length:(NSUInteger)buf->cmn.size options:mtl_options]; - } - else { + } else { mtl_buf = [_sg.mtl.device newBufferWithLength:(NSUInteger)buf->cmn.size options:mtl_options]; } } @@ -10872,7 +10993,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const _SOKOL_PRIVATE void _sg_mtl_discard_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); for (int slot = 0; slot < buf->cmn.num_slots; slot++) { - /* it's valid to call release resource with '0' */ + // it's valid to call release resource with '0' _sg_mtl_release_resource(_sg.mtl.frame_index, buf->mtl.buf[slot]); } } @@ -10885,9 +11006,9 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unr SOKOL_ASSERT(data->subimage[face_index][mip_index].ptr); SOKOL_ASSERT(data->subimage[face_index][mip_index].size > 0); const uint8_t* data_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr; - const int mip_width = _sg_max(img->cmn.width >> mip_index, 1); - const int mip_height = _sg_max(img->cmn.height >> mip_index, 1); - /* special case PVRTC formats: bytePerRow and bytesPerImage must be 0 */ + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + // special case PVRTC formats: bytePerRow and bytesPerImage must be 0 int bytes_per_row = 0; int bytes_per_slice = 0; if (!_sg_mtl_is_pvrtc(img->cmn.pixel_format)) { @@ -10901,13 +11022,11 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unr MTLRegion region; int bytes_per_image; if (img->cmn.type == SG_IMAGETYPE_3D) { - const int mip_depth = _sg_max(img->cmn.num_slices >> mip_index, 1); + const int mip_depth = _sg_miplevel_dim(img->cmn.num_slices, mip_index); region = MTLRegionMake3D(0, 0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height, (NSUInteger)mip_depth); bytes_per_image = bytes_per_slice; - /* FIXME: apparently the minimal bytes_per_image size for 3D texture - is 4 KByte... somehow need to handle this */ - } - else { + // FIXME: apparently the minimal bytes_per_image size for 3D texture is 4 KByte... somehow need to handle this + } else { region = MTLRegionMake2D(0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height); bytes_per_image = 0; } @@ -10927,164 +11046,89 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unr } } -/* - FIXME: METAL RESOURCE STORAGE MODE FOR macOS AND iOS - - For immutable textures on macOS, the recommended procedure is to create - a MTLStorageModeManaged texture with the immutable content first, - and then use the GPU to blit the content into a MTLStorageModePrivate - texture before the first use. - - On iOS use the same one-time-blit procedure, but from a - MTLStorageModeShared to a MTLStorageModePrivate texture. - - It probably makes sense to handle this in a separate 'resource manager' - with a recycable pool of blit-source-textures? -*/ - -/* initialize MTLTextureDescritor with common attributes */ +// initialize MTLTextureDescritor 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); if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { - SG_LOG("Unsupported texture pixel format!\n"); + _SG_ERROR(METAL_TEXTURE_FORMAT_NOT_SUPPORTED); return false; } mtl_desc.width = (NSUInteger)img->cmn.width; mtl_desc.height = (NSUInteger)img->cmn.height; if (SG_IMAGETYPE_3D == img->cmn.type) { mtl_desc.depth = (NSUInteger)img->cmn.num_slices; - } - else { + } else { mtl_desc.depth = 1; } mtl_desc.mipmapLevelCount = (NSUInteger)img->cmn.num_mipmaps; if (SG_IMAGETYPE_ARRAY == img->cmn.type) { mtl_desc.arrayLength = (NSUInteger)img->cmn.num_slices; - } - else { + } else { mtl_desc.arrayLength = 1; } mtl_desc.usage = MTLTextureUsageShaderRead; - if (img->cmn.render_target) { - mtl_desc.usage |= MTLTextureUsageRenderTarget; - } MTLResourceOptions res_options = 0; if (img->cmn.usage != SG_USAGE_IMMUTABLE) { res_options |= MTLResourceCPUCacheModeWriteCombined; } - #if defined(_SG_TARGET_MACOS) - /* macOS: use managed textures */ - res_options |= MTLResourceStorageModeManaged; - #else - /* iOS: use CPU/GPU shared memory */ - res_options |= MTLResourceStorageModeShared; - #endif + res_options |= _sg_mtl_resource_options_storage_mode_managed_or_shared(); mtl_desc.resourceOptions = res_options; return true; } -/* initialize MTLTextureDescritor with rendertarget attributes */ +// initialize MTLTextureDescritor 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); - /* render targets are only visible to the GPU */ - mtl_desc.resourceOptions = MTLResourceStorageModePrivate; - /* non-MSAA render targets are shader-readable */ mtl_desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; } -/* initialize MTLTextureDescritor with MSAA attributes */ +// initialize MTLTextureDescritor 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); - /* render targets are only visible to the GPU */ - mtl_desc.resourceOptions = MTLResourceStorageModePrivate; - /* MSAA render targets are not shader-readable (instead they are resolved) */ mtl_desc.usage = MTLTextureUsageRenderTarget; + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; mtl_desc.textureType = MTLTextureType2DMultisample; - mtl_desc.depth = 1; - mtl_desc.arrayLength = 1; - mtl_desc.mipmapLevelCount = 1; mtl_desc.sampleCount = (NSUInteger)img->cmn.sample_count; } _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); - _sg_image_common_init(&img->cmn, desc); const bool injected = (0 != desc->mtl_textures[0]); - const bool msaa = (img->cmn.sample_count > 1); - /* first initialize all Metal resource pool slots to 'empty' */ + // first initialize all Metal resource pool slots to 'empty' for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { img->mtl.tex[i] = _sg_mtl_add_resource(nil); } - img->mtl.sampler_state = _sg_mtl_add_resource(nil); - img->mtl.depth_tex = _sg_mtl_add_resource(nil); - img->mtl.msaa_tex = _sg_mtl_add_resource(nil); - /* initialize a Metal texture descriptor with common attributes */ + // initialize a Metal texture descriptor MTLTextureDescriptor* mtl_desc = [[MTLTextureDescriptor alloc] init]; if (!_sg_mtl_init_texdesc_common(mtl_desc, img)) { _SG_OBJC_RELEASE(mtl_desc); return SG_RESOURCESTATE_FAILED; } - - /* special case depth-stencil-buffer? */ - if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { - /* depth-stencil buffer texture must always be a render target */ - SOKOL_ASSERT(img->cmn.render_target); - SOKOL_ASSERT(img->cmn.type == SG_IMAGETYPE_2D); - SOKOL_ASSERT(img->cmn.num_mipmaps == 1); - SOKOL_ASSERT(!injected); - if (msaa) { + if (img->cmn.render_target) { + if (img->cmn.sample_count > 1) { _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); - } - else { - _sg_mtl_init_texdesc_rt(mtl_desc, img); - } - id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; - SOKOL_ASSERT(nil != tex); - img->mtl.depth_tex = _sg_mtl_add_resource(tex); - _SG_OBJC_RELEASE(tex); - } - else { - /* create the color texture - In case this is a render target without MSAA, add the relevant - render-target descriptor attributes. - In case this is a render target *with* MSAA, the color texture - will serve as MSAA-resolve target (not as render target), and rendering - will go into a separate render target texture of type - MTLTextureType2DMultisample. - */ - if (img->cmn.render_target && !msaa) { + } else { _sg_mtl_init_texdesc_rt(mtl_desc, img); } - for (int slot = 0; slot < img->cmn.num_slots; slot++) { - id tex; - if (injected) { - SOKOL_ASSERT(desc->mtl_textures[slot]); - tex = (__bridge id) desc->mtl_textures[slot]; - } - else { - tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; - if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { - _sg_mtl_copy_image_data(img, tex, &desc->data); - } + } + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + id mtl_tex; + if (injected) { + SOKOL_ASSERT(desc->mtl_textures[slot]); + mtl_tex = (__bridge id) desc->mtl_textures[slot]; + } else { + mtl_tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_mtl_copy_image_data(img, mtl_tex, &desc->data); } - img->mtl.tex[slot] = _sg_mtl_add_resource(tex); - _SG_OBJC_RELEASE(tex); - } - - /* if MSAA color render target, create an additional MSAA render-surface texture */ - if (img->cmn.render_target && msaa) { - _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); - id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; - img->mtl.msaa_tex = _sg_mtl_add_resource(tex); - _SG_OBJC_RELEASE(tex); } - - /* create (possibly shared) sampler state */ - img->mtl.sampler_state = _sg_mtl_create_sampler(_sg.mtl.device, desc); + img->mtl.tex[slot] = _sg_mtl_add_resource(mtl_tex); + _SG_OBJC_RELEASE(mtl_tex); } _SG_OBJC_RELEASE(mtl_desc); return SG_RESOURCESTATE_VALID; @@ -11092,13 +11136,50 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg _SOKOL_PRIVATE void _sg_mtl_discard_image(_sg_image_t* img) { SOKOL_ASSERT(img); - /* it's valid to call release resource with a 'null resource' */ + // it's valid to call release resource with a 'null resource' for (int slot = 0; slot < img->cmn.num_slots; slot++) { _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.tex[slot]); } - _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.depth_tex); - _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.msaa_tex); - /* NOTE: sampler state objects are shared and not released until shutdown */ +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + id mtl_smp; + const bool injected = (0 != desc->mtl_sampler); + if (injected) { + SOKOL_ASSERT(desc->mtl_sampler); + mtl_smp = (__bridge id) desc->mtl_sampler; + } else { + MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; + mtl_desc.sAddressMode = _sg_mtl_address_mode(desc->wrap_u); + mtl_desc.tAddressMode = _sg_mtl_address_mode(desc->wrap_v); + mtl_desc.rAddressMode = _sg_mtl_address_mode(desc->wrap_w); + if (_sg.features.image_clamp_to_border) { + if (@available(macOS 12.0, iOS 14.0, *)) { + mtl_desc.borderColor = _sg_mtl_border_color(desc->border_color); + } + } + mtl_desc.minFilter = _sg_mtl_minmag_filter(desc->min_filter); + mtl_desc.magFilter = _sg_mtl_minmag_filter(desc->mag_filter); + mtl_desc.mipFilter = _sg_mtl_mipmap_filter(desc->mipmap_filter); + mtl_desc.lodMinClamp = desc->min_lod; + mtl_desc.lodMaxClamp = desc->max_lod; + // FIXME: lodAverage? + mtl_desc.maxAnisotropy = desc->max_anisotropy; + mtl_desc.normalizedCoordinates = YES; + mtl_desc.compareFunction = _sg_mtl_compare_func(desc->compare); + mtl_smp = [_sg.mtl.device newSamplerStateWithDescriptor:mtl_desc]; + _SG_OBJC_RELEASE(mtl_desc); + } + smp->mtl.sampler_state = _sg_mtl_add_resource(mtl_smp); + _SG_OBJC_RELEASE(mtl_smp); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + // it's valid to call release resource with a 'null resource' + _sg_mtl_release_resource(_sg.mtl.frame_index, smp->mtl.sampler_state); } _SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { @@ -11109,7 +11190,8 @@ _SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { error:&err ]; if (err) { - SG_LOG([err.localizedDescription UTF8String]); + _SG_ERROR(METAL_SHADER_COMPILATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); } return lib; } @@ -11119,7 +11201,8 @@ _SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const void* ptr, siz dispatch_data_t lib_data = dispatch_data_create(ptr, num_bytes, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); id lib = [_sg.mtl.device newLibraryWithData:lib_data error:&err]; if (err) { - SG_LOG([err.localizedDescription UTF8String]); + _SG_ERROR(METAL_SHADER_CREATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); } _SG_OBJC_RELEASE(lib_data); return lib; @@ -11128,9 +11211,7 @@ _SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const void* ptr, siz _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); - _sg_shader_common_init(&shd->cmn, desc); - - /* create metal libray objects and lookup entry functions */ + // create metal library objects and lookup entry functions id vs_lib = nil; id fs_lib = nil; id vs_func = nil; @@ -11138,7 +11219,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const const char* vs_entry = desc->vs.entry; const char* fs_entry = desc->fs.entry; if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { - /* separate byte code provided */ + // separate byte code provided vs_lib = _sg_mtl_library_from_bytecode(desc->vs.bytecode.ptr, desc->vs.bytecode.size); fs_lib = _sg_mtl_library_from_bytecode(desc->fs.bytecode.ptr, desc->fs.bytecode.size); if ((nil == vs_lib) || (nil == fs_lib)) { @@ -11146,9 +11227,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const } vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; - } - else if (desc->vs.source && desc->fs.source) { - /* separate sources provided */ + } else if (desc->vs.source && desc->fs.source) { + // separate sources provided vs_lib = _sg_mtl_compile_library(desc->vs.source); fs_lib = _sg_mtl_compile_library(desc->fs.source); if ((nil == vs_lib) || (nil == fs_lib)) { @@ -11156,19 +11236,18 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const } vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; - } - else { + } else { goto failed; } if (nil == vs_func) { - SG_LOG("vertex shader entry function not found\n"); + _SG_ERROR(METAL_VERTEX_SHADER_ENTRY_NOT_FOUND); goto failed; } if (nil == fs_func) { - SG_LOG("fragment shader entry function not found\n"); + _SG_ERROR(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND); goto failed; } - /* it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index */ + // it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib = _sg_mtl_add_resource(vs_lib); _SG_OBJC_RELEASE(vs_lib); shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); @@ -11196,7 +11275,7 @@ failed: _SOKOL_PRIVATE void _sg_mtl_discard_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); - /* it is valid to call _sg_mtl_release_resource with a 'null resource' */ + // it is valid to call _sg_mtl_release_resource with a 'null resource' _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib); _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); @@ -11208,7 +11287,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s SOKOL_ASSERT(desc->shader.id == shd->slot.id); pip->shader = shd; - _sg_pipeline_common_init(&pip->cmn, desc); sg_primitive_type prim_type = desc->primitive_type; pip->mtl.prim_type = _sg_mtl_primitive_type(prim_type); @@ -11220,35 +11298,35 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s pip->mtl.winding = _sg_mtl_winding(desc->face_winding); pip->mtl.stencil_ref = desc->stencil.ref; - /* create vertex-descriptor */ + // create vertex-descriptor MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor]; for (NSUInteger attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_desc->format); - vtx_desc.attributes[attr_index].offset = (NSUInteger)a_desc->offset; - vtx_desc.attributes[attr_index].bufferIndex = (NSUInteger)(a_desc->buffer_index + SG_MAX_SHADERSTAGE_UBS); - pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_state->format); + vtx_desc.attributes[attr_index].offset = (NSUInteger)a_state->offset; + vtx_desc.attributes[attr_index].bufferIndex = (NSUInteger)(a_state->buffer_index + SG_MAX_SHADERSTAGE_UBS); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; } - for (NSUInteger layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { - if (pip->cmn.vertex_layout_valid[layout_index]) { - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; + for (NSUInteger layout_index = 0; layout_index < SG_MAX_VERTEX_BUFFERS; layout_index++) { + if (pip->cmn.vertex_buffer_layout_active[layout_index]) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index]; const NSUInteger mtl_vb_slot = layout_index + SG_MAX_SHADERSTAGE_UBS; - SOKOL_ASSERT(l_desc->stride > 0); - vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_desc->stride; - vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_desc->step_func); - vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_desc->step_rate; - if (SG_VERTEXSTEP_PER_INSTANCE == l_desc->step_func) { + SOKOL_ASSERT(l_state->stride > 0); + vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_state->stride; + vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_state->step_func); + vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_state->step_rate; + if (SG_VERTEXSTEP_PER_INSTANCE == l_state->step_func) { // NOTE: not actually used in _sg_mtl_draw() pip->cmn.use_instanced_draw = true; } } } - /* render-pipeline descriptor */ + // render-pipeline descriptor MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init]; rp_desc.vertexDescriptor = vtx_desc; SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); @@ -11263,17 +11341,17 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s if (desc->depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL) { rp_desc.stencilAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); } - /* FIXME: this only works on macOS 10.13! - for (int i = 0; i < (SG_MAX_SHADERSTAGE_UBS+SG_MAX_SHADERSTAGE_BUFFERS); i++) { - rp_desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; - } - for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { - rp_desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; + if (@available(macOS 10.13, iOS 11.0, *)) { + for (NSUInteger i = 0; i < (SG_MAX_SHADERSTAGE_UBS+SG_MAX_VERTEX_BUFFERS); i++) { + rp_desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; + } + for (NSUInteger i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + rp_desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; + } } - */ for (NSUInteger i = 0; i < (NSUInteger)desc->color_count; i++) { SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); - const sg_color_state* cs = &desc->colors[i]; + const sg_color_target_state* cs = &desc->colors[i]; rp_desc.colorAttachments[i].pixelFormat = _sg_mtl_pixel_format(cs->pixel_format); rp_desc.colorAttachments[i].writeMask = _sg_mtl_color_write_mask(cs->write_mask); rp_desc.colorAttachments[i].blendingEnabled = cs->blend.enabled; @@ -11289,11 +11367,12 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s _SG_OBJC_RELEASE(rp_desc); if (nil == mtl_rps) { SOKOL_ASSERT(err); - SG_LOG([err.localizedDescription UTF8String]); + _SG_ERROR(METAL_CREATE_RPS_FAILED); + _SG_LOGMSG(METAL_CREATE_RPS_OUTPUT, [err.localizedDescription UTF8String]); return SG_RESOURCESTATE_FAILED; } - /* depth-stencil-state */ + // depth-stencil-state MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init]; ds_desc.depthCompareFunction = _sg_mtl_compare_func(desc->depth.compare); ds_desc.depthWriteEnabled = desc->depth.write_enabled; @@ -11315,6 +11394,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s ds_desc.frontFaceStencil.readMask = desc->stencil.read_mask; ds_desc.frontFaceStencil.writeMask = desc->stencil.write_mask; } + // FIXME: can this actually fail? id mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc]; _SG_OBJC_RELEASE(ds_desc); pip->mtl.rps = _sg_mtl_add_resource(mtl_rps); @@ -11326,36 +11406,39 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s _SOKOL_PRIVATE void _sg_mtl_discard_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); - /* it's valid to call release resource with a 'null resource' */ + // it's valid to call release resource with a 'null resource' _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.rps); _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.dss); } -_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { +_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_ASSERT(att_images && att_images[0]); - - _sg_pass_common_init(&pass->cmn, desc); + SOKOL_ASSERT(color_images && resolve_images); - /* copy image pointers */ - const sg_pass_attachment_desc* att_desc; + // copy image pointers for (int i = 0; i < pass->cmn.num_color_atts; i++) { - att_desc = &desc->color_attachments[i]; - if (att_desc->image.id != SG_INVALID_ID) { - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); - SOKOL_ASSERT(0 == pass->mtl.color_atts[i].image); - SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); - pass->mtl.color_atts[i].image = att_images[i]; + const sg_pass_attachment_desc* color_desc = &desc->color_attachments[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(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]; + + const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == pass->mtl.resolve_atts[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]; } } SOKOL_ASSERT(0 == pass->mtl.ds_att.image); - att_desc = &desc->depth_stencil_attachment; - if (att_desc->image.id != SG_INVALID_ID) { - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; - SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); - pass->mtl.ds_att.image = att_images[ds_img_index]; + const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + 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; } return SG_RESOURCESTATE_VALID; } @@ -11366,13 +11449,19 @@ _SOKOL_PRIVATE void _sg_mtl_discard_pass(_sg_pass_t* pass) { } _SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_color_image(const _sg_pass_t* pass, int index) { + // NOTE: may return null SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); - /* NOTE: may return null */ return pass->mtl.color_atts[index].image; } +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_resolve_image(const _sg_pass_t* pass, int index) { + // NOTE: may return null + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return pass->mtl.resolve_atts[index].image; +} + _SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_ds_image(const _sg_pass_t* pass) { - /* NOTE: may return null */ + // NOTE: may return null SOKOL_ASSERT(pass); return pass->mtl.ds_att.image; } @@ -11401,7 +11490,7 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a */ if (nil == _sg.mtl.cmd_buffer) { SOKOL_ASSERT(nil == _sg.mtl.present_cmd_buffer); - /* block until the oldest frame in flight has finished */ + // 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]; _sg.mtl.present_cmd_buffer = [_sg.mtl.cmd_queue commandBuffer]; @@ -11414,78 +11503,73 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a }]; } - /* if this is first pass in frame, get uniform buffer base pointer */ + // if this is first pass in frame, get uniform buffer base pointer if (0 == _sg.mtl.cur_ub_base_ptr) { _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; } - /* initialize a render pass descriptor */ + // initialize a render pass descriptor MTLRenderPassDescriptor* pass_desc = nil; if (pass) { - /* offscreen render pass */ + // offscreen render pass pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; - } - else { - /* default render pass, call user-provided callback to provide render pass descriptor */ + } 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 { + } else { pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_userdata_cb(_sg.mtl.user_data); } - } 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 */ + } 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) { - /* setup pass descriptor for offscreen rendering */ + // 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_att = &pass->cmn.color_atts[i]; - const _sg_mtl_attachment_t* mtl_att = &pass->mtl.color_atts[i]; - const _sg_image_t* att_img = mtl_att->image; - SOKOL_ASSERT(att_img->slot.state == SG_RESOURCESTATE_VALID); - SOKOL_ASSERT(att_img->slot.id == cmn_att->image_id.id); - const bool is_msaa = (att_img->cmn.sample_count > 1); - pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].action); - pass_desc.colorAttachments[i].storeAction = is_msaa ? MTLStoreActionMultisampleResolve : MTLStoreActionStore; - sg_color c = action->colors[i].value; + 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]; + 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_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); + SOKOL_ASSERT(color_att_img->mtl.tex[color_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].load_action); + pass_desc.colorAttachments[i].storeAction = _sg_mtl_store_action(action->colors[i].store_action, resolve_att_img != 0); + sg_color c = action->colors[i].clear_value; pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); - if (is_msaa) { - SOKOL_ASSERT(att_img->mtl.msaa_tex != _SG_MTL_INVALID_SLOT_INDEX); - SOKOL_ASSERT(att_img->mtl.tex[mtl_att->image->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - pass_desc.colorAttachments[i].texture = _sg_mtl_id(att_img->mtl.msaa_tex); - pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_id(att_img->mtl.tex[att_img->cmn.active_slot]); - pass_desc.colorAttachments[i].resolveLevel = (NSUInteger)cmn_att->mip_level; - switch (att_img->cmn.type) { - case SG_IMAGETYPE_CUBE: - case SG_IMAGETYPE_ARRAY: - pass_desc.colorAttachments[i].resolveSlice = (NSUInteger)cmn_att->slice; - break; - case SG_IMAGETYPE_3D: - pass_desc.colorAttachments[i].resolveDepthPlane = (NSUInteger)cmn_att->slice; - break; - default: break; - } + pass_desc.colorAttachments[i].texture = _sg_mtl_id(color_att_img->mtl.tex[color_att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].level = (NSUInteger)cmn_color_att->mip_level; + switch (color_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].slice = (NSUInteger)cmn_color_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].depthPlane = (NSUInteger)cmn_color_att->slice; + break; + default: break; } - else { - SOKOL_ASSERT(att_img->mtl.tex[att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - pass_desc.colorAttachments[i].texture = _sg_mtl_id(att_img->mtl.tex[att_img->cmn.active_slot]); - pass_desc.colorAttachments[i].level = (NSUInteger)cmn_att->mip_level; - switch (att_img->cmn.type) { + if (resolve_att_img) { + SOKOL_ASSERT(resolve_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(resolve_att_img->slot.id == cmn_resolve_att->image_id.id); + SOKOL_ASSERT(resolve_att_img->mtl.tex[resolve_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_id(resolve_att_img->mtl.tex[resolve_att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].resolveLevel = (NSUInteger)cmn_resolve_att->mip_level; + switch (resolve_att_img->cmn.type) { case SG_IMAGETYPE_CUBE: case SG_IMAGETYPE_ARRAY: - pass_desc.colorAttachments[i].slice = (NSUInteger)cmn_att->slice; + pass_desc.colorAttachments[i].resolveSlice = (NSUInteger)cmn_resolve_att->slice; break; case SG_IMAGETYPE_3D: - pass_desc.colorAttachments[i].depthPlane = (NSUInteger)cmn_att->slice; + pass_desc.colorAttachments[i].resolveDepthPlane = (NSUInteger)cmn_resolve_att->slice; break; default: break; } @@ -11495,36 +11579,58 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a 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->mtl.depth_tex != _SG_MTL_INVALID_SLOT_INDEX); - pass_desc.depthAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); - pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); - pass_desc.depthAttachment.clearDepth = action->depth.value; + 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; + switch (ds_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.depthAttachment.slice = (NSUInteger)cmn_ds_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.depthAttachment.resolveDepthPlane = (NSUInteger)cmn_ds_att->slice; + break; + default: break; + } if (_sg_is_depth_stencil_format(ds_att_img->cmn.pixel_format)) { - pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); - pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); - pass_desc.stencilAttachment.clearStencil = action->stencil.value; + pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot]); + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action); + pass_desc.stencilAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false); + pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value; + switch (ds_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.stencilAttachment.slice = (NSUInteger)cmn_ds_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.stencilAttachment.resolveDepthPlane = (NSUInteger)cmn_ds_att->slice; + break; + default: break; + } } } - } - else { - /* setup pass descriptor for default rendering */ - pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].action); - sg_color c = action->colors[0].value; + } else { + // setup pass descriptor for default rendering + pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].load_action); + 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.action); - pass_desc.depthAttachment.clearDepth = action->depth.value; - pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); - pass_desc.stencilAttachment.clearStencil = action->stencil.value; + 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; } - /* create a render command encoder, this might return nil if window is minimized */ + // 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; return; } - /* bind the global uniform buffer, this only happens once per pass */ + // bind the global uniform buffer, this only happens once per pass _sg_mtl_bind_uniform_buffers(); } @@ -11534,7 +11640,7 @@ _SOKOL_PRIVATE void _sg_mtl_end_pass(void) { _sg.mtl.pass_valid = false; if (nil != _sg.mtl.cmd_encoder) { [_sg.mtl.cmd_encoder endEncoding]; - /* NOTE: MTLRenderCommandEncoder is autoreleased */ + // NOTE: MTLRenderCommandEncoder is autoreleased _sg.mtl.cmd_encoder = nil; } } @@ -11547,12 +11653,11 @@ _SOKOL_PRIVATE void _sg_mtl_commit(void) { SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); SOKOL_ASSERT(nil != _sg.mtl.present_cmd_buffer); - /* present, commit and signal semaphore when done */ + // 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 { + } else { cur_drawable = (__bridge id) _sg.mtl.drawable_userdata_cb(_sg.mtl.user_data); } if (nil != cur_drawable) { @@ -11561,17 +11666,17 @@ _SOKOL_PRIVATE void _sg_mtl_commit(void) { [_sg.mtl.cmd_buffer commit]; [_sg.mtl.present_cmd_buffer commit]; - /* garbage-collect resources pending for release */ + // garbage-collect resources pending for release _sg_mtl_garbage_collect(_sg.mtl.frame_index); - /* rotate uniform buffer slot */ + // rotate uniform buffer slot if (++_sg.mtl.cur_frame_rotate_index >= SG_NUM_INFLIGHT_FRAMES) { _sg.mtl.cur_frame_rotate_index = 0; } _sg.mtl.frame_index++; _sg.mtl.cur_ub_offset = 0; _sg.mtl.cur_ub_base_ptr = 0; - /* NOTE: MTLCommandBuffer is autoreleased */ + // NOTE: MTLCommandBuffer is autoreleased _sg.mtl.cmd_buffer = nil; _sg.mtl.present_cmd_buffer = nil; } @@ -11598,7 +11703,7 @@ _SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool return; } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - /* clip against framebuffer rect */ + // clip against framebuffer rect x = _sg_min(_sg_max(0, x), _sg.mtl.cur_width-1); y = _sg_min(_sg_max(0, y), _sg.mtl.cur_height-1); if ((x + w) > _sg.mtl.cur_width) { @@ -11627,7 +11732,7 @@ _SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - if ((_sg.mtl.state_cache.cur_pipeline != pip) || (_sg.mtl.state_cache.cur_pipeline_id.id != pip->slot.id)) { + if (_sg.mtl.state_cache.cur_pipeline_id.id != pip->slot.id) { _sg.mtl.state_cache.cur_pipeline = pip; _sg.mtl.state_cache.cur_pipeline_id.id = pip->slot.id; sg_color c = pip->cmn.blend_color; @@ -11635,7 +11740,7 @@ _SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { [_sg.mtl.cmd_encoder setCullMode:pip->mtl.cull_mode]; [_sg.mtl.cmd_encoder setFrontFacingWinding:pip->mtl.winding]; [_sg.mtl.cmd_encoder setStencilReferenceValue:pip->mtl.stencil_ref]; - [_sg.mtl.cmd_encoder setDepthBias:pip->cmn.depth_bias slopeScale:pip->cmn.depth_bias_slope_scale clamp:pip->cmn.depth_bias_clamp]; + [_sg.mtl.cmd_encoder setDepthBias:pip->cmn.depth.bias slopeScale:pip->cmn.depth.bias_slope_scale clamp:pip->cmn.depth.bias_clamp]; SOKOL_ASSERT(pip->mtl.rps != _SG_MTL_INVALID_SLOT_INDEX); [_sg.mtl.cmd_encoder setRenderPipelineState:_sg_mtl_id(pip->mtl.rps)]; SOKOL_ASSERT(pip->mtl.dss != _SG_MTL_INVALID_SLOT_INDEX); @@ -11648,7 +11753,9 @@ _SOKOL_PRIVATE void _sg_mtl_apply_bindings( _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) + _sg_image_t** fs_imgs, int num_fs_imgs, + _sg_sampler_t** vs_smps, int num_vs_smps, + _sg_sampler_t** fs_smps, int num_fs_smps) { _SOKOL_UNUSED(pip); SOKOL_ASSERT(_sg.mtl.in_pass); @@ -11657,25 +11764,22 @@ _SOKOL_PRIVATE void _sg_mtl_apply_bindings( } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - /* store index buffer binding, this will be needed later in sg_draw() */ + // store index buffer binding, this will be needed later in sg_draw() _sg.mtl.state_cache.cur_indexbuffer = ib; _sg.mtl.state_cache.cur_indexbuffer_offset = ib_offset; if (ib) { SOKOL_ASSERT(pip->cmn.index_type != SG_INDEXTYPE_NONE); _sg.mtl.state_cache.cur_indexbuffer_id.id = ib->slot.id; - } - else { + } else { SOKOL_ASSERT(pip->cmn.index_type == SG_INDEXTYPE_NONE); _sg.mtl.state_cache.cur_indexbuffer_id.id = SG_INVALID_ID; } - /* apply vertex buffers */ - NSUInteger slot; - for (slot = 0; slot < (NSUInteger)num_vbs; slot++) { + // apply vertex buffers + for (NSUInteger slot = 0; slot < (NSUInteger)num_vbs; slot++) { const _sg_buffer_t* vb = vbs[slot]; - if ((_sg.mtl.state_cache.cur_vertexbuffers[slot] != vb) || - (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offsets[slot]) || - (_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id)) + if ((_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id) || + (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offsets[slot])) { _sg.mtl.state_cache.cur_vertexbuffers[slot] = vb; _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offsets[slot]; @@ -11688,29 +11792,47 @@ _SOKOL_PRIVATE void _sg_mtl_apply_bindings( } } - /* apply vertex shader images */ - for (slot = 0; slot < (NSUInteger)num_vs_imgs; slot++) { + // apply vertex shader images + for (NSUInteger slot = 0; slot < (NSUInteger)num_vs_imgs; slot++) { const _sg_image_t* img = vs_imgs[slot]; - if ((_sg.mtl.state_cache.cur_vs_images[slot] != img) || (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id)) { + 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]; - SOKOL_ASSERT(img->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); - [_sg.mtl.cmd_encoder setVertexSamplerState:_sg_mtl_id(img->mtl.sampler_state) atIndex:slot]; } } - /* apply fragment shader images */ - for (slot = 0; slot < (NSUInteger)num_fs_imgs; slot++) { + // apply fragment shader images + for (NSUInteger slot = 0; slot < (NSUInteger)num_fs_imgs; slot++) { const _sg_image_t* img = fs_imgs[slot]; - if ((_sg.mtl.state_cache.cur_fs_images[slot] != img) || (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id)) { + 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]; - SOKOL_ASSERT(img->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); - [_sg.mtl.cmd_encoder setFragmentSamplerState:_sg_mtl_id(img->mtl.sampler_state) atIndex:slot]; + } + } + + // apply vertex shader samplers + for (NSUInteger slot = 0; slot < (NSUInteger)num_vs_smps; slot++) { + const _sg_sampler_t* smp = 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]; + } + } + + // apply fragment shader samplers + for (NSUInteger slot = 0; slot < (NSUInteger)num_fs_smps; slot++) { + const _sg_sampler_t* smp = 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]; } } } @@ -11729,13 +11851,12 @@ _SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_i SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); SOKOL_ASSERT(data->size == _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); - /* copy to global uniform buffer, record offset into cmd encoder, and advance offset */ + // copy to global uniform buffer, record offset into cmd encoder, and advance offset uint8_t* dst = &_sg.mtl.cur_ub_base_ptr[_sg.mtl.cur_ub_offset]; memcpy(dst, data->ptr, data->size); if (stage_index == SG_SHADERSTAGE_VS) { [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; - } - else { + } else { [_sg.mtl.cmd_encoder setFragmentBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; } _sg.mtl.cur_ub_offset = _sg_roundup(_sg.mtl.cur_ub_offset + (int)data->size, _SG_MTL_UB_ALIGN); @@ -11749,7 +11870,7 @@ _SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_ins 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) { - /* indexed rendering */ + // indexed rendering SOKOL_ASSERT(_sg.mtl.state_cache.cur_indexbuffer && (_sg.mtl.state_cache.cur_indexbuffer->slot.id == _sg.mtl.state_cache.cur_indexbuffer_id.id)); const _sg_buffer_t* ib = _sg.mtl.state_cache.cur_indexbuffer; SOKOL_ASSERT(ib->mtl.buf[ib->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); @@ -11760,9 +11881,8 @@ _SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_ins indexBuffer:_sg_mtl_id(ib->mtl.buf[ib->cmn.active_slot]) indexBufferOffset:index_buffer_offset instanceCount:(NSUInteger)num_instances]; - } - else { - /* non-indexed rendering */ + } else { + // non-indexed rendering [_sg.mtl.cmd_encoder drawPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type vertexStart:(NSUInteger)base_element vertexCount:(NSUInteger)num_elements @@ -11779,7 +11899,9 @@ _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) - [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLStorageModeManaged) { + [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; + } #endif } @@ -11795,9 +11917,11 @@ _SOKOL_PRIVATE int _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* data dst_ptr += buf->cmn.append_pos; memcpy(dst_ptr, data->ptr, data->size); #if defined(_SG_TARGET_MACOS) - [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLStorageModeManaged) { + [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; + } #endif - /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backends */ + // NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backends return _sg_roundup((int)data->size, 4); } @@ -11810,15 +11934,20 @@ _SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data* _sg_mtl_copy_image_data(img, mtl_tex, data); } -/*== WEBGPU BACKEND IMPLEMENTATION ===========================================*/ +// ██ ██ ███████ ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ █ ██ █████ ██████ ██ ███ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ███████ ██████ ██████ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>webgpu backend #elif defined(SOKOL_WGPU) _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; - } - else { + } else { res |= WGPUBufferUsage_Index; } if (SG_USAGE_IMMUTABLE != u) { @@ -11850,11 +11979,12 @@ _SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_tex_viewdim(sg_image_type t) { } } -_SOKOL_PRIVATE WGPUTextureComponentType _sg_wgpu_tex_comptype(sg_sampler_type t) { +_SOKOL_PRIVATE WGPUTextureComponentType _sg_wgpu_tex_comptype(sg_image_sample_type t) { + // FIXME switch (t) { - case SG_SAMPLERTYPE_FLOAT: return WGPUTextureComponentType_Float; - case SG_SAMPLERTYPE_SINT: return WGPUTextureComponentType_Sint; - case SG_SAMPLERTYPE_UINT: return WGPUTextureComponentType_Uint; + case SG_IMAGESAMPLETYPE_FLOAT: return WGPUTextureComponentType_Float; + case SG_IMAGESAMPLETYPE_SINT: return WGPUTextureComponentType_Sint; + case SG_IMAGESAMPLETYPE_UINT: return WGPUTextureComponentType_Uint; default: SOKOL_UNREACHABLE; return WGPUTextureComponentType_Force32; } } @@ -11862,8 +11992,7 @@ _SOKOL_PRIVATE WGPUTextureComponentType _sg_wgpu_tex_comptype(sg_sampler_type t) _SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_tex_dim(sg_image_type t) { if (SG_IMAGETYPE_3D == t) { return WGPUTextureDimension_3D; - } - else { + } else { return WGPUTextureDimension_2D; } } @@ -11916,7 +12045,7 @@ _SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_mipfilter(sg_filter f) { } _SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_indexformat(sg_index_type t) { - /* NOTE: there's no WGPUIndexFormat_None */ + // NOTE: there's no WGPUIndexFormat_None return (t == SG_INDEXTYPE_UINT16) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32; } @@ -11942,7 +12071,7 @@ _SOKOL_PRIVATE WGPUVertexFormat _sg_wgpu_vertexformat(sg_vertex_format f) { case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_UShort4Norm; case SG_VERTEXFORMAT_HALF2: return WGPUVertexFormat_Half2; case SG_VERTEXFORMAT_HALF3: return WGPUVertexFormat_Half4; - /* FIXME! UINT10_N2 */ + // FIXME! UINT10_N2 case SG_VERTEXFORMAT_UINT10_N2: default: SOKOL_UNREACHABLE; @@ -12010,7 +12139,7 @@ _SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { case SG_PIXELFORMAT_RGBA32UI: return WGPUTextureFormat_RGBA32Uint; case SG_PIXELFORMAT_RGBA32SI: return WGPUTextureFormat_RGBA32Sint; case SG_PIXELFORMAT_RGBA32F: return WGPUTextureFormat_RGBA32Float; - case SG_PIXELFORMAT_DEPTH: return WGPUTextureFormat_Depth24Plus; + case SG_PIXELFORMAT_DEPTH: return WGPUTextureFormat_Depth32Float; case SG_PIXELFORMAT_DEPTH_STENCIL: return WGPUTextureFormat_Depth24PlusStencil8; case SG_PIXELFORMAT_BC1_RGBA: return WGPUTextureFormat_BC1RGBAUnorm; case SG_PIXELFORMAT_BC2_RGBA: return WGPUTextureFormat_BC2RGBAUnorm; @@ -12023,7 +12152,7 @@ _SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { case SG_PIXELFORMAT_BC6H_RGBUF: return WGPUTextureFormat_BC6HRGBUfloat; case SG_PIXELFORMAT_BC7_RGBA: return WGPUTextureFormat_BC7RGBAUnorm; - /* NOT SUPPORTED */ + // NOT SUPPORTED case SG_PIXELFORMAT_R16: case SG_PIXELFORMAT_R16SN: case SG_PIXELFORMAT_RG16: @@ -12111,7 +12240,7 @@ _SOKOL_PRIVATE WGPUBlendFactor _sg_wgpu_blendfactor(sg_blend_factor f) { case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return WGPUBlendFactor_SrcAlphaSaturated; case SG_BLENDFACTOR_BLEND_COLOR: return WGPUBlendFactor_BlendColor; case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return WGPUBlendFactor_OneMinusBlendColor; - /* FIXME: separate blend alpha value not supported? */ + // FIXME: separate blend alpha value not supported? case SG_BLENDFACTOR_BLEND_ALPHA: return WGPUBlendFactor_BlendColor; case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return WGPUBlendFactor_OneMinusBlendColor; default: @@ -12138,17 +12267,12 @@ _SOKOL_PRIVATE WGPUColorWriteMaskFlags _sg_wgpu_colorwritemask(uint8_t m) { _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg.backend = SG_BACKEND_WGPU; - _sg.features.instancing = true; _sg.features.origin_top_left = true; - _sg.features.multiple_render_targets = true; - _sg.features.msaa_render_targets = true; - _sg.features.imagetype_3d = true; - _sg.features.imagetype_array = true; _sg.features.image_clamp_to_border = false; _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; - /* FIXME: max images size??? */ + // FIXME: max images size??? _sg.limits.max_image_size_2d = 8 * 1024; _sg.limits.max_image_size_cube = 8 * 1024; _sg.limits.max_image_size_3d = 2 * 1024; @@ -12179,7 +12303,7 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); - /* FIXME: missing SG_PIXELFORMAT_RG11B10F */ + // FIXME: missing SG_PIXELFORMAT_RG11B10F _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); @@ -12303,9 +12427,9 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_mapped_callback(WGPUBufferMapAsyncStatus sta if (!_sg.wgpu.valid) { return; } - /* FIXME: better handling for this */ + // FIXME: better handling for this if (WGPUBufferMapAsyncStatus_Success != status) { - SG_LOG("Mapping uniform buffer failed!\n"); + _SG_ERROR(WGPU_MAP_UNIFORM_BUFFER_FAILED); SOKOL_ASSERT(false); } SOKOL_ASSERT(data && (data_len == _sg.wgpu.ub.num_bytes)); @@ -12317,17 +12441,17 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_mapped_callback(WGPUBufferMapAsyncStatus sta _SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { - /* immediately request a new mapping for the last frame's current staging buffer */ + // immediately request a new mapping for the last frame's current staging buffer if (!first_frame) { WGPUBuffer ub_src = _sg.wgpu.ub.stage.buf[_sg.wgpu.ub.stage.cur]; wgpuBufferMapWriteAsync(ub_src, _sg_wgpu_ubpool_mapped_callback, (void*)(intptr_t)_sg.wgpu.ub.stage.cur); } - /* rewind per-frame offsets */ + // rewind per-frame offsets _sg.wgpu.ub.offset = 0; _sg_clear(&_sg.wgpu.ub.bind_offsets, sizeof(_sg.wgpu.ub.bind_offsets)); - /* check if a mapped staging buffer is available, otherwise create one */ + // check if a mapped staging buffer is available, otherwise create one for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { if (_sg.wgpu.ub.stage.ptr[i]) { _sg.wgpu.ub.stage.cur = i; @@ -12335,7 +12459,7 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { } } - /* no mapped uniform buffer available, create one */ + // no mapped uniform buffer available, create one SOKOL_ASSERT(_sg.wgpu.ub.stage.num < _SG_WGPU_STAGING_PIPELINE_SIZE); _sg.wgpu.ub.stage.cur = _sg.wgpu.ub.stage.num++; const int cur = _sg.wgpu.ub.stage.cur; @@ -12353,7 +12477,7 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { } _SOKOL_PRIVATE void _sg_wgpu_ubpool_flush(void) { - /* unmap staging buffer and copy to uniform buffer */ + // unmap staging buffer and copy to uniform buffer const int cur = _sg.wgpu.ub.stage.cur; SOKOL_ASSERT(_sg.wgpu.ub.stage.ptr[cur]); _sg.wgpu.ub.stage.ptr[cur] = 0; @@ -12365,15 +12489,15 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_flush(void) { } } -/* helper function to compute number of bytes needed in staging buffer to copy image data */ +// helper function to compute number of bytes needed in staging buffer to copy image data _SOKOL_PRIVATE uint32_t _sg_wgpu_image_data_buffer_size(const _sg_image_t* img) { uint32_t num_bytes = 0; const uint32_t num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { - const uint32_t mip_width = _sg_max(img->cmn.width >> mip_index, 1); - const uint32_t mip_height = _sg_max(img->cmn.height >> mip_index, 1); - /* row-pitch must be 256-aligend */ + const uint32_t mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const uint32_t mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + // row-pitch must be 256-aligend const uint32_t bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, _SG_WGPU_ROWPITCH_ALIGN); num_bytes += bytes_per_slice * num_slices * num_faces; } @@ -12409,9 +12533,9 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* st SOKOL_ASSERT(src_base_ptr); uint8_t* dst_base_ptr = stg_base_ptr + stg_offset; - const uint32_t mip_width = _sg_max(img->cmn.width >> mip_index, 1); - const uint32_t mip_height = _sg_max(img->cmn.height >> mip_index, 1); - const uint32_t mip_depth = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_max(img->cmn.num_slices >> mip_index, 1) : 1; + const uint32_t mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const uint32_t mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + const uint32_t mip_depth = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : 1; const uint32_t num_rows = _sg_num_rows(fmt, mip_height); const uint32_t src_bytes_per_row = _sg_row_pitch(fmt, mip_width, 1); const uint32_t dst_bytes_per_row = _sg_row_pitch(fmt, mip_width, _SG_WGPU_ROWPITCH_ALIGN); @@ -12423,14 +12547,13 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* st SOKOL_ASSERT(dst_bytes_per_slice == (dst_bytes_per_row * num_rows)); _SOKOL_UNUSED(src_bytes_per_slice); - /* copy data into mapped staging buffer */ + // copy data into mapped staging buffer if (src_bytes_per_row == dst_bytes_per_row) { - /* can do a single memcpy */ + // can do a single memcpy uint32_t num_bytes = data->subimage[face_index][mip_index].size; memcpy(dst_base_ptr, src_base_ptr, num_bytes); - } - else { - /* src/dst pitch doesn't match, need to copy row by row */ + } else { + // src/dst pitch doesn't match, need to copy row by row uint8_t* dst_ptr = dst_base_ptr; const uint8_t* src_ptr = src_base_ptr; for (uint32_t slice_index = 0; slice_index < num_slices; slice_index++) { @@ -12443,7 +12566,7 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* st } } - /* record the staging copy operation into command encoder */ + // record the staging copy operation into command encoder src_view.imageHeight = mip_height; src_view.rowPitch = dst_bytes_per_row; dst_view.mipLevel = mip_index; @@ -12485,7 +12608,7 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* st _SOKOL_PRIVATE void _sg_wgpu_staging_init(const sg_desc* desc) { SOKOL_ASSERT(desc && (desc->staging_buffer_size > 0)); _sg.wgpu.staging.num_bytes = desc->staging_buffer_size; - /* there's actually nothing more to do here */ + // there's actually nothing more to do here } _SOKOL_PRIVATE void _sg_wgpu_staging_discard(void) { @@ -12502,7 +12625,7 @@ _SOKOL_PRIVATE void _sg_wgpu_staging_mapped_callback(WGPUBufferMapAsyncStatus st if (!_sg.wgpu.valid) { return; } - /* FIXME: better handling for this */ + // FIXME: better handling for this if (WGPUBufferMapAsyncStatus_Success != status) { SOKOL_ASSERT("Mapping staging buffer failed!\n"); SOKOL_ASSERT(false); @@ -12516,16 +12639,16 @@ _SOKOL_PRIVATE void _sg_wgpu_staging_mapped_callback(WGPUBufferMapAsyncStatus st _SOKOL_PRIVATE void _sg_wgpu_staging_next_frame(bool first_frame) { - /* immediately request a new mapping for the last frame's current staging buffer */ + // immediately request a new mapping for the last frame's current staging buffer if (!first_frame) { WGPUBuffer cur_buf = _sg.wgpu.staging.buf[_sg.wgpu.staging.cur]; wgpuBufferMapWriteAsync(cur_buf, _sg_wgpu_staging_mapped_callback, (void*)(intptr_t)_sg.wgpu.staging.cur); } - /* rewind staging-buffer offset */ + // rewind staging-buffer offset _sg.wgpu.staging.offset = 0; - /* check if mapped staging buffer is available, otherwise create one */ + // check if mapped staging buffer is available, otherwise create one for (int i = 0; i < _sg.wgpu.staging.num; i++) { if (_sg.wgpu.staging.ptr[i]) { _sg.wgpu.staging.cur = i; @@ -12533,7 +12656,7 @@ _SOKOL_PRIVATE void _sg_wgpu_staging_next_frame(bool first_frame) { } } - /* no mapped buffer available, create one */ + // no mapped buffer available, create one SOKOL_ASSERT(_sg.wgpu.staging.num < _SG_WGPU_STAGING_PIPELINE_SIZE); _sg.wgpu.staging.cur = _sg.wgpu.staging.num++; const int cur = _sg.wgpu.staging.cur; @@ -12564,7 +12687,7 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_staging_copy_to_buffer(WGPUBuffer dst_buf, uint SOKOL_ASSERT(data_num_bytes > 0); uint32_t copy_num_bytes = _sg_roundup(data_num_bytes, 4); if ((_sg.wgpu.staging.offset + copy_num_bytes) >= _sg.wgpu.staging.num_bytes) { - SG_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_buffer())!\n"); + _SG_ERROR(WGPU_STAGING_BUFFER_FULL_COPY_TO_BUFFER); return false; } const int cur = _sg.wgpu.staging.cur; @@ -12579,11 +12702,11 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_staging_copy_to_buffer(WGPUBuffer dst_buf, uint } _SOKOL_PRIVATE bool _sg_wgpu_staging_copy_to_texture(_sg_image_t* img, const sg_image_data* data) { - /* similar to _sg_wgpu_staging_copy_to_buffer(), but with image data instead */ + // similar to _sg_wgpu_staging_copy_to_buffer(), but with image data instead SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); uint32_t num_bytes = _sg_wgpu_image_data_buffer_size(img); if ((_sg.wgpu.staging.offset + num_bytes) >= _sg.wgpu.staging.num_bytes) { - SG_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_texture)!\n"); + _SG_ERROR(WGPU_STAGING_BUFFER_FULL_COPY_TO_TEXTURE); return false; } const int cur = _sg.wgpu.staging.cur; @@ -12599,56 +12722,33 @@ _SOKOL_PRIVATE bool _sg_wgpu_staging_copy_to_texture(_sg_image_t* img, const sg_ } _SOKOL_PRIVATE void _sg_wgpu_staging_unmap(void) { - /* called at end of frame before queue-submit */ + // called at end of frame before queue-submit const int cur = _sg.wgpu.staging.cur; SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); _sg.wgpu.staging.ptr[cur] = 0; wgpuBufferUnmap(_sg.wgpu.staging.buf[cur]); } -/*--- WGPU sampler cache functions ---*/ -_SOKOL_PRIVATE void _sg_wgpu_init_sampler_cache(const sg_desc* desc) { - SOKOL_ASSERT(desc->sampler_cache_size > 0); - _sg_smpcache_init(&_sg.wgpu.sampler_cache, desc->sampler_cache_size); -} - -_SOKOL_PRIVATE void _sg_wgpu_destroy_sampler_cache(void) { - SOKOL_ASSERT(_sg.wgpu.sampler_cache.items); - SOKOL_ASSERT(_sg.wgpu.sampler_cache.num_items <= _sg.wgpu.sampler_cache.capacity); - for (int i = 0; i < _sg.wgpu.sampler_cache.num_items; i++) { - wgpuSamplerRelease((WGPUSampler)_sg_smpcache_sampler(&_sg.wgpu.sampler_cache, i)); - } - _sg_smpcache_discard(&_sg.wgpu.sampler_cache); -} - _SOKOL_PRIVATE WGPUSampler _sg_wgpu_create_sampler(const sg_image_desc* img_desc) { SOKOL_ASSERT(img_desc); - int index = _sg_smpcache_find_item(&_sg.wgpu.sampler_cache, img_desc); - if (index >= 0) { - /* reuse existing sampler */ - return (WGPUSampler) _sg_smpcache_sampler(&_sg.wgpu.sampler_cache, index); - } - else { - /* create a new WGPU sampler and add to sampler cache */ - /* FIXME: anisotropic filtering not supported? */ - WGPUSamplerDescriptor smp_desc; - _sg_clear(&smp_desc, sizeof(smp_desc)); - smp_desc.addressModeU = _sg_wgpu_sampler_addrmode(img_desc->wrap_u); - smp_desc.addressModeV = _sg_wgpu_sampler_addrmode(img_desc->wrap_v); - smp_desc.addressModeW = _sg_wgpu_sampler_addrmode(img_desc->wrap_w); - smp_desc.magFilter = _sg_wgpu_sampler_minmagfilter(img_desc->mag_filter); - smp_desc.minFilter = _sg_wgpu_sampler_minmagfilter(img_desc->min_filter); - smp_desc.mipmapFilter = _sg_wgpu_sampler_mipfilter(img_desc->min_filter); - smp_desc.lodMinClamp = img_desc->min_lod; - smp_desc.lodMaxClamp = img_desc->max_lod; - WGPUSampler smp = wgpuDeviceCreateSampler(_sg.wgpu.dev, &smp_desc); - SOKOL_ASSERT(smp); - _sg_smpcache_add_item(&_sg.wgpu.sampler_cache, img_desc, (uintptr_t)smp); - return smp; - } -} - -/*--- WGPU backend API functions ---*/ + // create a new WGPU sampler + // FIXME: anisotropic filtering not supported? + WGPUSamplerDescriptor smp_desc; + _sg_clear(&smp_desc, sizeof(smp_desc)); + smp_desc.addressModeU = _sg_wgpu_sampler_addrmode(img_desc->wrap_u); + smp_desc.addressModeV = _sg_wgpu_sampler_addrmode(img_desc->wrap_v); + smp_desc.addressModeW = _sg_wgpu_sampler_addrmode(img_desc->wrap_w); + smp_desc.magFilter = _sg_wgpu_sampler_minmagfilter(img_desc->mag_filter); + smp_desc.minFilter = _sg_wgpu_sampler_minmagfilter(img_desc->min_filter); + smp_desc.mipmapFilter = _sg_wgpu_sampler_mipfilter(img_desc->min_filter); + smp_desc.lodMinClamp = img_desc->min_lod; + smp_desc.lodMaxClamp = img_desc->max_lod; + WGPUSampler smp = wgpuDeviceCreateSampler(_sg.wgpu.dev, &smp_desc); + SOKOL_ASSERT(smp); + return smp; +} + +//--- WGPU backend API functions --- _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { SOKOL_ASSERT(desc); SOKOL_ASSERT(desc->context.wgpu.device); @@ -12670,17 +12770,16 @@ _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { _sg.wgpu.queue = wgpuDeviceCreateQueue(_sg.wgpu.dev); SOKOL_ASSERT(_sg.wgpu.queue); - /* setup WebGPU features and limits */ + // setup WebGPU features and limits _sg_wgpu_init_caps(); - /* setup the sampler cache, uniform and staging buffer pools */ - _sg_wgpu_init_sampler_cache(&_sg.desc); + // setup the uniform and staging buffer pools _sg_wgpu_ubpool_init(desc); _sg_wgpu_ubpool_next_frame(true); _sg_wgpu_staging_init(desc); _sg_wgpu_staging_next_frame(true); - /* create an empty bind group for shader stages without bound images */ + // create an empty bind group for shader stages without bound images WGPUBindGroupLayoutDescriptor bgl_desc; _sg_clear(&bgl_desc, sizeof(bgl_desc)); WGPUBindGroupLayout empty_bgl = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); @@ -12692,7 +12791,7 @@ _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { SOKOL_ASSERT(_sg.wgpu.empty_bind_group); wgpuBindGroupLayoutRelease(empty_bgl); - /* create initial per-frame command encoders */ + // create initial per-frame command encoders WGPUCommandEncoderDescriptor cmd_enc_desc; _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); @@ -12708,7 +12807,6 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { _sg.wgpu.valid = false; _sg_wgpu_ubpool_discard(); _sg_wgpu_staging_discard(); - _sg_wgpu_destroy_sampler_cache(); wgpuBindGroupRelease(_sg.wgpu.empty_bind_group); wgpuCommandEncoderRelease(_sg.wgpu.render_cmd_enc); _sg.wgpu.render_cmd_enc = 0; @@ -12721,7 +12819,7 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { } _SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) { - SG_LOG("_sg_wgpu_reset_state_cache: FIXME\n"); + _SG_WARN(WGPU_RESET_STATE_CACHE_FIXME); } _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_context(_sg_context_t* ctx) { @@ -12737,18 +12835,16 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_context(_sg_context_t* ctx) { _SOKOL_PRIVATE void _sg_wgpu_activate_context(_sg_context_t* ctx) { (void)ctx; - SG_LOG("_sg_wgpu_activate_context: FIXME\n"); + _SG_WARN(WGPU_ACTIVATE_CONTEXT_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); - _sg_buffer_common_init(&buf->cmn, desc); if (injected) { buf->wgpu.buf = (WGPUBuffer) desc->wgpu_buffer; wgpuBufferReference(buf->wgpu.buf); - } - else { + } else { WGPUBufferDescriptor wgpu_buf_desc; _sg_clear(&wgpu_buf_desc, sizeof(wgpu_buf_desc)); wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(buf->cmn.type, buf->cmn.usage); @@ -12760,8 +12856,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const SOKOL_ASSERT(res.data && (res.dataLength == buf->cmn.size)); memcpy(res.data, desc->data.ptr, buf->cmn.size); wgpuBufferUnmap(res.buffer); - } - else { + } else { buf->wgpu.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &wgpu_buf_desc); } } @@ -12784,12 +12879,10 @@ _SOKOL_PRIVATE void _sg_wgpu_init_texdesc_common(WGPUTextureDescriptor* wgpu_tex if (desc->type == SG_IMAGETYPE_3D) { wgpu_tex_desc->size.depth = desc->num_slices; wgpu_tex_desc->arrayLayerCount = 1; - } - else if (desc->type == SG_IMAGETYPE_CUBE) { + } else if (desc->type == SG_IMAGETYPE_CUBE) { wgpu_tex_desc->size.depth = 1; wgpu_tex_desc->arrayLayerCount = 6; - } - else { + } else { wgpu_tex_desc->size.depth = 1; wgpu_tex_desc->arrayLayerCount = desc->num_slices; } @@ -12803,8 +12896,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s SOKOL_ASSERT(_sg.wgpu.dev); SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - _sg_image_common_init(&img->cmn, desc); - const bool injected = (0 != desc->wgpu_texture); const bool is_msaa = desc->sample_count > 1; WGPUTextureDescriptor wgpu_tex_desc; @@ -12822,13 +12913,11 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s wgpu_tex_desc.sampleCount = desc->sample_count; img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); SOKOL_ASSERT(img->wgpu.tex); - } - else { + } else { if (injected) { img->wgpu.tex = (WGPUTexture) desc->wgpu_texture; wgpuTextureReference(img->wgpu.tex); - } - else { + } else { /* NOTE: in the MSAA-rendertarget case, both the MSAA texture *and* the resolve texture need OutputAttachment usage */ @@ -12838,7 +12927,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); SOKOL_ASSERT(img->wgpu.tex); - /* copy content into texture via a throw-away staging buffer */ + // copy content into texture via a throw-away staging buffer if (desc->usage == SG_USAGE_IMMUTABLE && !desc->render_target) { WGPUBufferDescriptor wgpu_buf_desc; _sg_clear(&wgpu_buf_desc, sizeof(wgpu_buf_desc)); @@ -12854,7 +12943,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s } } - /* create texture view object */ + // create texture view object WGPUTextureViewDescriptor wgpu_view_desc; _sg_clear(&wgpu_view_desc, sizeof(wgpu_view_desc)); wgpu_view_desc.dimension = _sg_wgpu_tex_viewdim(desc->type); @@ -12875,7 +12964,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s SOKOL_ASSERT(img->wgpu.msaa_tex); } - /* create sampler via shared-sampler-cache */ + // create sampler via shared-sampler-cache img->wgpu.sampler = _sg_wgpu_create_sampler(desc); SOKOL_ASSERT(img->wgpu.sampler); } @@ -12896,7 +12985,7 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) { wgpuTextureRelease(img->wgpu.msaa_tex); img->wgpu.msaa_tex = 0; } - /* NOTE: do *not* destroy the sampler from the shared-sampler-cache */ + // NOTE: do *not* destroy the sampler from the shared-sampler-cache img->wgpu.sampler = 0; } @@ -12930,7 +13019,6 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) { _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); SOKOL_ASSERT(desc->vs.bytecode.ptr && desc->fs.bytecode.ptr); - _sg_shader_common_init(&shd->cmn, desc); bool success = true; for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { @@ -12950,7 +13038,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const success = false; } - /* create image/sampler bind group for the shader stage */ + // create image/sampler bind group for the shader stage WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; int num_imgs = cmn_stage->num_images; if (num_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { @@ -12959,7 +13047,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const WGPUBindGroupLayoutBinding bglb_desc[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; _sg_clear(bglb_desc, sizeof(bglb_desc)); for (int img_index = 0; img_index < num_imgs; img_index++) { - /* texture- and sampler-bindings */ + // texture- and sampler-bindings WGPUBindGroupLayoutBinding* tex_desc = &bglb_desc[img_index*2 + 0]; WGPUBindGroupLayoutBinding* smp_desc = &bglb_desc[img_index*2 + 1]; @@ -13004,7 +13092,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout); SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout); pip->shader = shd; - _sg_pipeline_common_init(&pip->cmn, desc); pip->wgpu.stencil_ref = (uint32_t) desc->stencil.ref; WGPUBindGroupLayout pip_bgl[3] = { @@ -13018,12 +13105,12 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ pl_desc.bindGroupLayouts = &pip_bgl[0]; WGPUPipelineLayout pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &pl_desc); - WGPUVertexBufferLayoutDescriptor vb_desc[SG_MAX_SHADERSTAGE_BUFFERS]; + WGPUVertexBufferLayoutDescriptor vb_desc[SG_MAX_VERTEX_BUFFERS]; _sg_clear(&vb_desc, sizeof(vb_desc)); - WGPUVertexAttributeDescriptor va_desc[SG_MAX_SHADERSTAGE_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; + WGPUVertexAttributeDescriptor va_desc[SG_MAX_VERTEX_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; _sg_clear(&va_desc, sizeof(va_desc)); int vb_idx = 0; - for (; vb_idx < SG_MAX_SHADERSTAGE_BUFFERS; vb_idx++) { + for (; vb_idx < SG_MAX_VERTEX_BUFFERS; vb_idx++) { const sg_buffer_layout_desc* src_vb_desc = &desc->layout.buffers[vb_idx]; if (0 == src_vb_desc->stride) { break; @@ -13039,7 +13126,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ if (SG_VERTEXFORMAT_INVALID == src_va_desc->format) { break; } - pip->cmn.vertex_layout_valid[src_va_desc->buffer_index] = true; + pip->cmn.vertex_buffer_layout_active[src_va_desc->buffer_index] = true; if (vb_idx == src_va_desc->buffer_index) { va_desc[vb_idx][va_idx].format = _sg_wgpu_vertexformat(src_va_desc->format); va_desc[vb_idx][va_idx].offset = src_va_desc->offset; @@ -13114,7 +13201,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ } pip_desc.colorStateCount = desc->color_count; pip_desc.colorStates = cs_desc; - pip_desc.sampleMask = 0xFFFFFFFF; /* FIXME: ??? */ + pip_desc.sampleMask = 0xFFFFFFFF; // FIXME: ??? pip->wgpu.pip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &pip_desc); SOKOL_ASSERT(0 != pip->wgpu.pip); wgpuPipelineLayoutRelease(pip_layout); @@ -13137,9 +13224,8 @@ _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** att_images, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && desc); SOKOL_ASSERT(att_images && att_images[0]); - _sg_pass_common_init(&pass->cmn, desc); - /* copy image pointers and create render-texture views */ + // copy image pointers and create render-texture views const sg_pass_attachment_desc* att_desc; for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { att_desc = &desc->color_attachments[i]; @@ -13150,7 +13236,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag SOKOL_ASSERT(img && (img->slot.id == att_desc->image.id)); SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format)); pass->wgpu.color_atts[i].image = img; - /* create a render-texture-view to render into the right sub-surface */ + // create a render-texture-view to render into the right sub-surface const bool is_msaa = img->cmn.sample_count > 1; WGPUTextureViewDescriptor view_desc; _sg_clear(&view_desc, sizeof(view_desc)); @@ -13162,7 +13248,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag SOKOL_ASSERT(wgpu_tex); pass->wgpu.color_atts[i].render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); SOKOL_ASSERT(pass->wgpu.color_atts[i].render_tex_view); - /* ... and if needed a separate resolve texture view */ + // ... and if needed a separate resolve texture view if (is_msaa) { view_desc.baseMipLevel = att_desc->mip_level; view_desc.baseArrayLayer = att_desc->slice; @@ -13180,7 +13266,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); _sg_image_t* ds_img = att_images[ds_img_index]; pass->wgpu.ds_att.image = ds_img; - /* create a render-texture view */ + // create a render-texture view SOKOL_ASSERT(0 == att_desc->mip_level); SOKOL_ASSERT(0 == att_desc->slice); WGPUTextureViewDescriptor view_desc; @@ -13213,12 +13299,12 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_pass(_sg_pass_t* pass) { _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)); - /* NOTE: may return null */ + // NOTE: may return null return pass->wgpu.color_atts[index].image; } _SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_ds_image(const _sg_pass_t* pass) { - /* NOTE: may return null */ + // NOTE: may return null SOKOL_ASSERT(pass); return pass->wgpu.ds_att.image; } @@ -13270,9 +13356,8 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att_desc; _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.render_cmd_enc, &wgpu_pass_desc); } - } - else { - /* default render pass */ + } else { + // default render pass WGPUTextureView wgpu_render_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); @@ -13287,7 +13372,7 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* color_att_desc.clearColor.b = action->colors[0].value.b; color_att_desc.clearColor.a = action->colors[0].value.a; color_att_desc.attachment = wgpu_render_view; - color_att_desc.resolveTarget = wgpu_resolve_view; /* null if no MSAA rendering */ + color_att_desc.resolveTarget = wgpu_resolve_view; // null if no MSAA rendering pass_desc.colorAttachmentCount = 1; pass_desc.colorAttachments = &color_att_desc; WGPURenderPassDepthStencilAttachmentDescriptor ds_att_desc; @@ -13303,9 +13388,9 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* } SOKOL_ASSERT(_sg.wgpu.pass_enc); - /* initial uniform buffer binding (required even if no uniforms are set in the frame) */ + // initial uniform buffer binding (required even if no uniforms are set in the frame) wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, - 0, /* groupIndex 0 is reserved for uniform buffers */ + 0, // groupIndex 0 is reserved for uniform buffers _sg.wgpu.ub.bindgroup, SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, &_sg.wgpu.ub.bind_offsets[0][0]); @@ -13326,7 +13411,7 @@ _SOKOL_PRIVATE void _sg_wgpu_commit(void) { SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - /* finish and submit this frame's work */ + // finish and submit this frame's work _sg_wgpu_ubpool_flush(); _sg_wgpu_staging_unmap(); @@ -13349,13 +13434,13 @@ _SOKOL_PRIVATE void _sg_wgpu_commit(void) { wgpuCommandBufferRelease(cmd_bufs[0]); wgpuCommandBufferRelease(cmd_bufs[1]); - /* create a new render- and staging-command-encoders for next frame */ + // create a new render- and staging-command-encoders for next frame WGPUCommandEncoderDescriptor cmd_enc_desc; _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); - /* grab new staging buffers for uniform- and vertex/image-updates */ + // grab new staging buffers for uniform- and vertex/image-updates _sg_wgpu_ubpool_next_frame(false); _sg_wgpu_staging_next_frame(false); } @@ -13376,7 +13461,7 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); - /* clip against framebuffer rect */ + // clip against framebuffer rect x = _sg_min(_sg_max(0, x), _sg.wgpu.cur_width-1); y = _sg_min(_sg_max(0, y), _sg.wgpu.cur_height-1); if ((x + w) > _sg.wgpu.cur_width) { @@ -13442,17 +13527,17 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_bindings( SOKOL_ASSERT(_sg.wgpu.pass_enc); SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); - /* index buffer */ + // index buffer if (ib) { wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.pass_enc, ib->wgpu.buf, ib_offset); } - /* vertex buffers */ + // vertex buffers for (uint32_t slot = 0; slot < (uint32_t)num_vbs; slot++) { wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.pass_enc, slot, vbs[slot]->wgpu.buf, (uint64_t)vb_offsets[slot]); } - /* need to create throw-away bind groups for images */ + // need to create throw-away bind groups for images if (num_vs_imgs > 0) { if (num_vs_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { num_vs_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; @@ -13462,8 +13547,7 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_bindings( WGPUBindGroup vs_img_bg = _sg_wgpu_create_images_bindgroup(vs_bgl, vs_imgs, num_vs_imgs); wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, vs_img_bg, 0, 0); wgpuBindGroupRelease(vs_img_bg); - } - else { + } else { wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, _sg.wgpu.empty_bind_group, 0, 0); } if (num_fs_imgs > 0) { @@ -13475,8 +13559,7 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_bindings( WGPUBindGroup fs_img_bg = _sg_wgpu_create_images_bindgroup(fs_bgl, fs_imgs, num_fs_imgs); wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, fs_img_bg, 0, 0); wgpuBindGroupRelease(fs_img_bg); - } - else { + } else { wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, _sg.wgpu.empty_bind_group, 0, 0); } } @@ -13498,7 +13581,7 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(sg_shader_stage stage_index, int ub_ memcpy(dst_ptr, data->ptr, data->size); _sg.wgpu.ub.bind_offsets[stage_index][ub_index] = _sg.wgpu.ub.offset; wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, - 0, /* groupIndex 0 is reserved for uniform buffers */ + 0, // groupIndex 0 is reserved for uniform buffers _sg.wgpu.ub.bindgroup, SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, &_sg.wgpu.ub.bind_offsets[0][0]); @@ -13510,8 +13593,7 @@ _SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_in SOKOL_ASSERT(_sg.wgpu.pass_enc); if (_sg.wgpu.draw_indexed) { wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0, 0); - } - else { + } else { wgpuRenderPassEncoderDraw(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0); } } @@ -13538,7 +13620,13 @@ _SOKOL_PRIVATE void _sg_wgpu_update_image(_sg_image_t* img, const sg_image_data* } #endif -/*== BACKEND API WRAPPERS ====================================================*/ +// ██████ ███████ ███ ██ ███████ ██████ ██ ██████ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ███ █████ ██ ██ ██ █████ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ███████ ██ ████ ███████ ██ ██ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>generic backend static inline void _sg_setup_backend(const sg_desc* desc) { #if defined(_SOKOL_ANY_GL) _sg_gl_setup_backend(desc); @@ -13699,6 +13787,38 @@ static inline void _sg_discard_image(_sg_image_t* img) { #endif } +static inline sg_resource_state _sg_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_sampler(smp, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_sampler(smp, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_sampler(smp, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_sampler(smp, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_sampler(smp, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_sampler(_sg_sampler_t* smp) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_sampler(smp); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_sampler(smp); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_sampler(smp); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_sampler(smp); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_sampler(smp); + #else + #error("INVALID BACKEND"); + #endif +} + static inline sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { #if defined(_SOKOL_ANY_GL) return _sg_gl_create_shader(shd, desc); @@ -13763,17 +13883,17 @@ 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** att_images, const sg_pass_desc* desc) { +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) { #if defined(_SOKOL_ANY_GL) - return _sg_gl_create_pass(pass, att_images, desc); + return _sg_gl_create_pass(pass, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_METAL) - return _sg_mtl_create_pass(pass, att_images, desc); + return _sg_mtl_create_pass(pass, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_D3D11) - return _sg_d3d11_create_pass(pass, att_images, desc); + return _sg_d3d11_create_pass(pass, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_WGPU) - return _sg_wgpu_create_pass(pass, att_images, desc); + return _sg_wgpu_create_pass(pass, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_create_pass(pass, att_images, desc); + return _sg_dummy_create_pass(pass, color_images, resolve_images, ds_image, desc); #else #error("INVALID BACKEND"); #endif @@ -13811,6 +13931,22 @@ static inline _sg_image_t* _sg_pass_color_image(const _sg_pass_t* pass, int inde #endif } +static inline _sg_image_t* _sg_pass_resolve_image(const _sg_pass_t* pass, int index) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_pass_resolve_image(pass, index); + #elif defined(SOKOL_METAL) + return _sg_mtl_pass_resolve_image(pass, index); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_pass_resolve_image(pass, index); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_pass_resolve_image(pass, index); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_pass_resolve_image(pass, index); + #else + #error("INVALID BACKEND"); + #endif +} + static inline _sg_image_t* _sg_pass_ds_image(const _sg_pass_t* pass) { #if defined(_SOKOL_ANY_GL) return _sg_gl_pass_ds_image(pass); @@ -13912,18 +14048,20 @@ static inline void _sg_apply_bindings( _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, _sg_image_t** vs_imgs, int num_vs_imgs, - _sg_image_t** fs_imgs, int num_fs_imgs) + _sg_image_t** fs_imgs, int num_fs_imgs, + _sg_sampler_t** vs_smps, int num_vs_smps, + _sg_sampler_t** fs_smps, int num_fs_smps) { #if defined(_SOKOL_ANY_GL) - _sg_gl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + _sg_gl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); #elif defined(SOKOL_METAL) - _sg_mtl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + _sg_mtl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); #elif defined(SOKOL_D3D11) - _sg_d3d11_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + _sg_d3d11_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); #elif defined(SOKOL_WGPU) - _sg_wgpu_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + _sg_wgpu_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); #elif defined(SOKOL_DUMMY_BACKEND) - _sg_dummy_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + _sg_dummy_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); #else #error("INVALID BACKEND"); #endif @@ -14025,19 +14163,24 @@ static inline void _sg_update_image(_sg_image_t* img, const sg_image_data* data) #endif } -/*== RESOURCE POOLS ==========================================================*/ - +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool _SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { SOKOL_ASSERT(pool && (num >= 1)); - /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ + // slot 0 is reserved for the 'invalid id', so bump the pool size by 1 pool->size = num + 1; pool->queue_top = 0; - /* generation counters indexable by pool slot index, slot 0 is reserved */ + // generation counters indexable by pool slot index, slot 0 is reserved size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; pool->gen_ctrs = (uint32_t*)_sg_malloc_clear(gen_ctrs_size); - /* it's not a bug to only reserve 'num' here */ + // it's not a bug to only reserve 'num' here pool->free_queue = (int*) _sg_malloc_clear(sizeof(int) * (size_t)num); - /* never allocate the zero-th pool item since the invalid id is 0 */ + // never allocate the zero-th pool item since the invalid id is 0 for (int i = pool->size-1; i >= 1; i--) { pool->free_queue[pool->queue_top++] = i; } @@ -14062,9 +14205,8 @@ _SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool) { int slot_index = pool->free_queue[--pool->queue_top]; SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); return slot_index; - } - else { - /* pool exhausted */ + } else { + // pool exhausted return _SG_INVALID_SLOT_INDEX; } } @@ -14075,7 +14217,7 @@ _SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) { SOKOL_ASSERT(pool->free_queue); SOKOL_ASSERT(pool->queue_top < pool->size); #ifdef SOKOL_DEBUG - /* debug check against double-free */ + // debug check against double-free for (int i = 0; i < pool->queue_top; i++) { SOKOL_ASSERT(pool->free_queue[i] != slot_index); } @@ -14105,6 +14247,14 @@ _SOKOL_PRIVATE void _sg_reset_image_to_alloc_state(_sg_image_t* img) { img->slot.state = SG_RESOURCESTATE_ALLOC; } +_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)); + smp->slot = slot; + smp->slot.state = SG_RESOURCESTATE_ALLOC; +} + _SOKOL_PRIVATE void _sg_reset_shader_to_alloc_state(_sg_shader_t* shd) { SOKOL_ASSERT(shd); _sg_slot_t slot = shd->slot; @@ -14140,7 +14290,7 @@ _SOKOL_PRIVATE void _sg_reset_context_to_alloc_state(_sg_context_t* ctx) { _SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { SOKOL_ASSERT(p); SOKOL_ASSERT(desc); - /* note: the pools here will have an additional item, since slot 0 is reserved */ + // note: the pools here will have an additional item, since slot 0 is reserved SOKOL_ASSERT((desc->buffer_pool_size > 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->buffer_pool, desc->buffer_pool_size); size_t buffer_pool_byte_size = sizeof(_sg_buffer_t) * (size_t)p->buffer_pool.size; @@ -14151,6 +14301,11 @@ _SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { size_t image_pool_byte_size = sizeof(_sg_image_t) * (size_t)p->image_pool.size; p->images = (_sg_image_t*) _sg_malloc_clear(image_pool_byte_size); + SOKOL_ASSERT((desc->sampler_pool_size > 0) && (desc->sampler_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->sampler_pool, desc->sampler_pool_size); + size_t sampler_pool_byte_size = sizeof(_sg_sampler_t) * (size_t)p->sampler_pool.size; + p->samplers = (_sg_sampler_t*) _sg_malloc_clear(sampler_pool_byte_size); + SOKOL_ASSERT((desc->shader_pool_size > 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->shader_pool, desc->shader_pool_size); size_t shader_pool_byte_size = sizeof(_sg_shader_t) * (size_t)p->shader_pool.size; @@ -14178,12 +14333,14 @@ _SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { _sg_free(p->passes); p->passes = 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->pipeline_pool); _sg_discard_pool(&p->shader_pool); + _sg_discard_pool(&p->sampler_pool); _sg_discard_pool(&p->image_pool); _sg_discard_pool(&p->buffer_pool); } @@ -14209,14 +14366,14 @@ _SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int s return slot->id; } -/* extract slot index from id */ +// extract slot index from id _SOKOL_PRIVATE int _sg_slot_index(uint32_t id) { int slot_index = (int) (id & _SG_SLOT_MASK); SOKOL_ASSERT(_SG_INVALID_SLOT_INDEX != slot_index); return slot_index; } -/* returns pointer to resource by id without matching id check */ +// returns pointer to resource by id without matching id check _SOKOL_PRIVATE _sg_buffer_t* _sg_buffer_at(const _sg_pools_t* p, uint32_t buf_id) { SOKOL_ASSERT(p && (SG_INVALID_ID != buf_id)); int slot_index = _sg_slot_index(buf_id); @@ -14231,6 +14388,13 @@ _SOKOL_PRIVATE _sg_image_t* _sg_image_at(const _sg_pools_t* p, uint32_t img_id) return &p->images[slot_index]; } +_SOKOL_PRIVATE _sg_sampler_t* _sg_sampler_at(const _sg_pools_t* p, uint32_t smp_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != smp_id)); + int slot_index = _sg_slot_index(smp_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->sampler_pool.size)); + return &p->samplers[slot_index]; +} + _SOKOL_PRIVATE _sg_shader_t* _sg_shader_at(const _sg_pools_t* p, uint32_t shd_id) { SOKOL_ASSERT(p && (SG_INVALID_ID != shd_id)); int slot_index = _sg_slot_index(shd_id); @@ -14259,7 +14423,7 @@ _SOKOL_PRIVATE _sg_context_t* _sg_context_at(const _sg_pools_t* p, uint32_t cont return &p->contexts[slot_index]; } -/* returns pointer to resource with matching id check, may return 0 */ +// returns pointer to resource with matching id check, may return 0 _SOKOL_PRIVATE _sg_buffer_t* _sg_lookup_buffer(const _sg_pools_t* p, uint32_t buf_id) { if (SG_INVALID_ID != buf_id) { _sg_buffer_t* buf = _sg_buffer_at(p, buf_id); @@ -14280,6 +14444,16 @@ _SOKOL_PRIVATE _sg_image_t* _sg_lookup_image(const _sg_pools_t* p, uint32_t img_ return 0; } +_SOKOL_PRIVATE _sg_sampler_t* _sg_lookup_sampler(const _sg_pools_t* p, uint32_t smp_id) { + if (SG_INVALID_ID != smp_id) { + _sg_sampler_t* smp = _sg_sampler_at(p, smp_id); + if (smp->slot.id == smp_id) { + return smp; + } + } + return 0; +} + _SOKOL_PRIVATE _sg_shader_t* _sg_lookup_shader(const _sg_pools_t* p, uint32_t shd_id) { SOKOL_ASSERT(p); if (SG_INVALID_ID != shd_id) { @@ -14348,6 +14522,14 @@ _SOKOL_PRIVATE void _sg_discard_all_resources(_sg_pools_t* p, uint32_t ctx_id) { } } } + 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]); + } + } + } 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; @@ -14374,161 +14556,27 @@ _SOKOL_PRIVATE void _sg_discard_all_resources(_sg_pools_t* p, uint32_t ctx_id) { } } -/*== VALIDATION LAYER ========================================================*/ -#if defined(SOKOL_DEBUG) -/* return a human readable string for an _sg_validate_error */ -_SOKOL_PRIVATE const char* _sg_validate_string(_sg_validate_error_t err) { - switch (err) { - /* buffer creation validation errors */ - case _SG_VALIDATE_BUFFERDESC_CANARY: return "sg_buffer_desc not initialized"; - case _SG_VALIDATE_BUFFERDESC_SIZE: return "sg_buffer_desc.size cannot be 0"; - case _SG_VALIDATE_BUFFERDESC_DATA: return "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)"; - case _SG_VALIDATE_BUFFERDESC_DATA_SIZE: return "immutable buffer data size differs from buffer size"; - case _SG_VALIDATE_BUFFERDESC_NO_DATA: return "dynamic/stream usage buffers cannot be initialized with data"; - - /* image data (in image creation and updating) */ - case _SG_VALIDATE_IMAGEDATA_NODATA: return "sg_image_data: no data (.ptr and/or .size is zero)"; - case _SG_VALIDATE_IMAGEDATA_DATA_SIZE: return "sg_image_data: data size doesn't match expected surface size"; - - /* image creation validation errros */ - case _SG_VALIDATE_IMAGEDESC_CANARY: return "sg_image_desc not initialized"; - case _SG_VALIDATE_IMAGEDESC_WIDTH: return "sg_image_desc.width must be > 0"; - case _SG_VALIDATE_IMAGEDESC_HEIGHT: return "sg_image_desc.height must be > 0"; - case _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT: return "invalid pixel format for render-target image"; - case _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT: return "invalid pixel format for non-render-target image"; - case _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT: return "non-render-target images cannot be multisampled"; - case _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT: return "MSAA not supported for this pixel format"; - case _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE: return "render target images must be SG_USAGE_IMMUTABLE"; - case _SG_VALIDATE_IMAGEDESC_RT_NO_DATA: return "render target images cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA: return "images with injected textures cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA: return "dynamic/stream images cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE: return "compressed images must be immutable"; - - /* shader creation */ - case _SG_VALIDATE_SHADERDESC_CANARY: return "sg_shader_desc not initialized"; - case _SG_VALIDATE_SHADERDESC_SOURCE: return "shader source code required"; - case _SG_VALIDATE_SHADERDESC_BYTECODE: return "shader byte code required"; - case _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE: return "shader source or byte code required"; - case _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE: return "shader byte code length (in bytes) required"; - case _SG_VALIDATE_SHADERDESC_NO_CONT_UBS: return "shader uniform blocks must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS: return "uniform block members must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS: return "GL backend requires uniform block member declarations"; - case _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME: return "uniform block member name missing"; - case _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH: return "size of uniform block members doesn't match uniform block size"; - case _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT: return "uniform array count must be >= 1"; - case _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE: return "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout"; - - case _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS: return "shader images must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_IMG_NAME: return "GL backend requires uniform block member names"; - case _SG_VALIDATE_SHADERDESC_ATTR_NAMES: return "GLES2 backend requires vertex attribute names"; - case _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS: return "D3D11 backend requires vertex attribute semantics"; - case _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG: return "vertex attribute name/semantic string too long (max len 16)"; - - /* pipeline creation */ - case _SG_VALIDATE_PIPELINEDESC_CANARY: return "sg_pipeline_desc not initialized"; - case _SG_VALIDATE_PIPELINEDESC_SHADER: return "sg_pipeline_desc.shader missing or invalid"; - case _SG_VALIDATE_PIPELINEDESC_NO_ATTRS: return "sg_pipeline_desc.layout.attrs is empty or not continuous"; - case _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4: return "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4"; - case _SG_VALIDATE_PIPELINEDESC_ATTR_NAME: return "GLES2/WebGL missing vertex attribute name in shader"; - case _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS: return "D3D11 missing vertex attribute semantics in shader"; - - /* pass creation */ - case _SG_VALIDATE_PASSDESC_CANARY: return "sg_pass_desc not initialized"; - case _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS: return "sg_pass_desc.color_attachments[0] must be valid"; - case _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS: return "color attachments must occupy continuous slots"; - case _SG_VALIDATE_PASSDESC_IMAGE: return "pass attachment image is not valid"; - case _SG_VALIDATE_PASSDESC_MIPLEVEL: return "pass attachment mip level is bigger than image has mipmaps"; - case _SG_VALIDATE_PASSDESC_FACE: return "pass attachment image is cubemap, but face index is too big"; - case _SG_VALIDATE_PASSDESC_LAYER: return "pass attachment image is array texture, but layer index is too big"; - case _SG_VALIDATE_PASSDESC_SLICE: return "pass attachment image is 3d texture, but slice value is too big"; - case _SG_VALIDATE_PASSDESC_IMAGE_NO_RT: return "pass attachment image must be render targets"; - case _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT: return "pass color-attachment images must have a renderable pixel format"; - case _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT: return "pass depth-attachment image must have depth pixel format"; - case _SG_VALIDATE_PASSDESC_IMAGE_SIZES: return "all pass attachments must have the same size"; - case _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS: return "all pass attachments must have the same sample count"; - - /* sg_begin_pass */ - case _SG_VALIDATE_BEGINPASS_PASS: return "sg_begin_pass: pass must be valid"; - case _SG_VALIDATE_BEGINPASS_IMAGE: return "sg_begin_pass: one or more attachment images are not valid"; - - /* sg_apply_pipeline */ - case _SG_VALIDATE_APIP_PIPELINE_VALID_ID: return "sg_apply_pipeline: invalid pipeline id provided"; - case _SG_VALIDATE_APIP_PIPELINE_EXISTS: return "sg_apply_pipeline: pipeline object no longer alive"; - case _SG_VALIDATE_APIP_PIPELINE_VALID: return "sg_apply_pipeline: pipeline object not in valid state"; - case _SG_VALIDATE_APIP_SHADER_EXISTS: return "sg_apply_pipeline: shader object no longer alive"; - case _SG_VALIDATE_APIP_SHADER_VALID: return "sg_apply_pipeline: shader object not in valid state"; - case _SG_VALIDATE_APIP_ATT_COUNT: return "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments"; - case _SG_VALIDATE_APIP_COLOR_FORMAT: return "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format"; - case _SG_VALIDATE_APIP_DEPTH_FORMAT: return "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format"; - case _SG_VALIDATE_APIP_SAMPLE_COUNT: return "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count"; - - /* sg_apply_bindings */ - case _SG_VALIDATE_ABND_PIPELINE: return "sg_apply_bindings: must be called after sg_apply_pipeline"; - case _SG_VALIDATE_ABND_PIPELINE_EXISTS: return "sg_apply_bindings: currently applied pipeline object no longer alive"; - case _SG_VALIDATE_ABND_PIPELINE_VALID: return "sg_apply_bindings: currently applied pipeline object not in valid state"; - case _SG_VALIDATE_ABND_VBS: return "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts"; - case _SG_VALIDATE_ABND_VB_EXISTS: return "sg_apply_bindings: vertex buffer no longer alive"; - case _SG_VALIDATE_ABND_VB_TYPE: return "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER"; - case _SG_VALIDATE_ABND_VB_OVERFLOW: return "sg_apply_bindings: buffer in vertex buffer slot is overflown"; - case _SG_VALIDATE_ABND_NO_IB: return "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided"; - case _SG_VALIDATE_ABND_IB: return "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided"; - case _SG_VALIDATE_ABND_IB_EXISTS: return "sg_apply_bindings: index buffer no longer alive"; - case _SG_VALIDATE_ABND_IB_TYPE: return "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER"; - case _SG_VALIDATE_ABND_IB_OVERFLOW: return "sg_apply_bindings: buffer in index buffer slot is overflown"; - case _SG_VALIDATE_ABND_VS_IMGS: return "sg_apply_bindings: vertex shader image count doesn't match sg_shader_desc"; - case _SG_VALIDATE_ABND_VS_IMG_EXISTS: return "sg_apply_bindings: vertex shader image no longer alive"; - case _SG_VALIDATE_ABND_VS_IMG_TYPES: return "sg_apply_bindings: one or more vertex shader image types don't match sg_shader_desc"; - case _SG_VALIDATE_ABND_FS_IMGS: return "sg_apply_bindings: fragment shader image count doesn't match sg_shader_desc"; - case _SG_VALIDATE_ABND_FS_IMG_EXISTS: return "sg_apply_bindings: fragment shader image no longer alive"; - case _SG_VALIDATE_ABND_FS_IMG_TYPES: return "sg_apply_bindings: one or more fragment shader image types don't match sg_shader_desc"; - - /* sg_apply_uniforms */ - case _SG_VALIDATE_AUB_NO_PIPELINE: return "sg_apply_uniforms: must be called after sg_apply_pipeline()"; - case _SG_VALIDATE_AUB_NO_UB_AT_SLOT: return "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot"; - case _SG_VALIDATE_AUB_SIZE: return "sg_apply_uniforms: data size exceeds declared uniform block size"; - - /* sg_update_buffer */ - case _SG_VALIDATE_UPDATEBUF_USAGE: return "sg_update_buffer: cannot update immutable buffer"; - case _SG_VALIDATE_UPDATEBUF_SIZE: return "sg_update_buffer: update size is bigger than buffer size"; - case _SG_VALIDATE_UPDATEBUF_ONCE: return "sg_update_buffer: only one update allowed per buffer and frame"; - case _SG_VALIDATE_UPDATEBUF_APPEND: return "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame"; - - /* sg_append_buffer */ - case _SG_VALIDATE_APPENDBUF_USAGE: return "sg_append_buffer: cannot append to immutable buffer"; - case _SG_VALIDATE_APPENDBUF_SIZE: return "sg_append_buffer: overall appended size is bigger than buffer size"; - case _SG_VALIDATE_APPENDBUF_UPDATE: return "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame"; - - /* sg_update_image */ - case _SG_VALIDATE_UPDIMG_USAGE: return "sg_update_image: cannot update immutable image"; - case _SG_VALIDATE_UPDIMG_ONCE: return "sg_update_image: only one update allowed per image and frame"; - - default: return "unknown validation error"; - } -} -#endif /* defined(SOKOL_DEBUG) */ - -/*-- validation checks -------------------------------------------------------*/ +// ██ ██ █████ ██ ██ ██████ █████ ████████ ██ ██████ ███ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ███████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ████ ██ ██ ███████ ██ ██████ ██ ██ ██ ██ ██████ ██ ████ +// +// >>validation #if defined(SOKOL_DEBUG) _SOKOL_PRIVATE void _sg_validate_begin(void) { - _sg.validate_error = _SG_VALIDATE_SUCCESS; -} - -_SOKOL_PRIVATE void _sg_validate(bool cond, _sg_validate_error_t err) { - if (!cond) { - _sg.validate_error = err; - SG_LOG(_sg_validate_string(err)); - } + _sg.validate_error = SG_LOGITEM_OK; } _SOKOL_PRIVATE bool _sg_validate_end(void) { - if (_sg.validate_error != _SG_VALIDATE_SUCCESS) { + if (_sg.validate_error != SG_LOGITEM_OK) { #if !defined(SOKOL_VALIDATE_NON_FATAL) - SG_LOG("^^^^ SOKOL-GFX VALIDATION FAILED, TERMINATING ^^^^"); - SOKOL_ASSERT(false); + _SG_PANIC(VALIDATION_FAILED); + return false; + #else + return false; #endif - return false; - } - else { + } else { return true; } } @@ -14543,22 +14591,21 @@ _SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { return true; } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); - SOKOL_VALIDATE(desc->size > 0, _SG_VALIDATE_BUFFERDESC_SIZE); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_BUFFERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_BUFFERDESC_CANARY); + _SG_VALIDATE(desc->size > 0, VALIDATE_BUFFERDESC_SIZE); bool injected = (0 != desc->gl_buffers[0]) || (0 != desc->mtl_buffers[0]) || (0 != desc->d3d11_buffer) || (0 != desc->wgpu_buffer); if (!injected && (desc->usage == SG_USAGE_IMMUTABLE)) { - SOKOL_VALIDATE((0 != desc->data.ptr) && (desc->data.size > 0), _SG_VALIDATE_BUFFERDESC_DATA); - SOKOL_VALIDATE(desc->size == desc->data.size, _SG_VALIDATE_BUFFERDESC_DATA_SIZE); - } - else { - SOKOL_VALIDATE(0 == desc->data.ptr, _SG_VALIDATE_BUFFERDESC_NO_DATA); + _SG_VALIDATE((0 != desc->data.ptr) && (desc->data.size > 0), VALIDATE_BUFFERDESC_DATA); + _SG_VALIDATE(desc->size == desc->data.size, VALIDATE_BUFFERDESC_DATA_SIZE); + } else { + _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_BUFFERDESC_NO_DATA); } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14576,12 +14623,12 @@ _SOKOL_PRIVATE void _sg_validate_image_data(const sg_image_data* data, sg_pixel_ for (int mip_index = 0; mip_index < num_mips; mip_index++) { const bool has_data = data->subimage[face_index][mip_index].ptr != 0; const bool has_size = data->subimage[face_index][mip_index].size > 0; - SOKOL_VALIDATE(has_data && has_size, _SG_VALIDATE_IMAGEDATA_NODATA); - const int mip_width = _sg_max(width >> mip_index, 1); - const int mip_height = _sg_max(height >> mip_index, 1); + _SG_VALIDATE(has_data && has_size, VALIDATE_IMAGEDATA_NODATA); + const int mip_width = _sg_miplevel_dim(width, mip_index); + const int mip_height = _sg_miplevel_dim(height, mip_index); const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); const int expected_size = bytes_per_slice * num_slices; - SOKOL_VALIDATE(expected_size == (int)data->subimage[face_index][mip_index].size, _SG_VALIDATE_IMAGEDATA_DATA_SIZE); + _SG_VALIDATE(expected_size == (int)data->subimage[face_index][mip_index].size, VALIDATE_IMAGEDATA_DATA_SIZE); } } #endif @@ -14596,41 +14643,38 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { return true; } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); - SOKOL_VALIDATE(desc->width > 0, _SG_VALIDATE_IMAGEDESC_WIDTH); - SOKOL_VALIDATE(desc->height > 0, _SG_VALIDATE_IMAGEDESC_HEIGHT); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(desc->width > 0, VALIDATE_IMAGEDESC_WIDTH); + _SG_VALIDATE(desc->height > 0, VALIDATE_IMAGEDESC_HEIGHT); const sg_pixel_format fmt = desc->pixel_format; const sg_usage usage = desc->usage; const bool injected = (0 != desc->gl_textures[0]) || (0 != desc->mtl_textures[0]) || (0 != desc->d3d11_texture) || (0 != desc->wgpu_texture); + if (_sg_is_depth_or_depth_stencil_format(fmt)) { + _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE); + } if (desc->render_target) { SOKOL_ASSERT(((int)fmt >= 0) && ((int)fmt < _SG_PIXELFORMAT_NUM)); - SOKOL_VALIDATE(_sg.formats[fmt].render, _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT); - /* on GLES2, sample count for render targets is completely ignored */ - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - if (!_sg.gl.gles2) { - #endif - if (desc->sample_count > 1) { - SOKOL_VALIDATE(_sg.features.msaa_render_targets && _sg.formats[fmt].msaa, _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); - } - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + _SG_VALIDATE(_sg.formats[fmt].render, VALIDATE_IMAGEDESC_RT_PIXELFORMAT); + _SG_VALIDATE(usage == SG_USAGE_IMMUTABLE, VALIDATE_IMAGEDESC_RT_IMMUTABLE); + _SG_VALIDATE(desc->data.subimage[0][0].ptr==0, VALIDATE_IMAGEDESC_RT_NO_DATA); + if (desc->sample_count > 1) { + _SG_VALIDATE(_sg.formats[fmt].msaa, VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); + _SG_VALIDATE(desc->num_mipmaps == 1, VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS); + _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_MSAA_3D_IMAGE); } - #endif - SOKOL_VALIDATE(usage == SG_USAGE_IMMUTABLE, _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE); - SOKOL_VALIDATE(desc->data.subimage[0][0].ptr==0, _SG_VALIDATE_IMAGEDESC_RT_NO_DATA); - } - else { - SOKOL_VALIDATE(desc->sample_count <= 1, _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); + } else { + _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt); - SOKOL_VALIDATE(valid_nonrt_fmt, _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); + _SG_VALIDATE(valid_nonrt_fmt, VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); const bool is_compressed = _sg_is_compressed_pixel_format(desc->pixel_format); const bool is_immutable = (usage == SG_USAGE_IMMUTABLE); if (is_compressed) { - SOKOL_VALIDATE(is_immutable, _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); + _SG_VALIDATE(is_immutable, VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); } if (!injected && is_immutable) { // image desc must have valid data @@ -14641,24 +14685,41 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { (desc->type == SG_IMAGETYPE_CUBE) ? 6 : 1, desc->num_mipmaps, desc->num_slices); - } - else { + } else { // image desc must not have data for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { const bool no_data = 0 == desc->data.subimage[face_index][mip_index].ptr; const bool no_size = 0 == desc->data.subimage[face_index][mip_index].size; if (injected) { - SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA); + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_INJECTED_NO_DATA); } if (!is_immutable) { - SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); } } } } } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_sampler_desc(const sg_sampler_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(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); + return _sg_validate_end(); #endif } @@ -14671,43 +14732,41 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { return true; } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); - #if defined(SOKOL_GLES2) - SOKOL_VALIDATE(0 != desc->attrs[0].name, _SG_VALIDATE_SHADERDESC_ATTR_NAMES); - #elif defined(SOKOL_D3D11) - SOKOL_VALIDATE(0 != desc->attrs[0].sem_name, _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS); + _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_GLES2) || defined(SOKOL_GLES3) - /* on GL, must provide shader source code */ - SOKOL_VALIDATE(0 != desc->vs.source, _SG_VALIDATE_SHADERDESC_SOURCE); - SOKOL_VALIDATE(0 != desc->fs.source, _SG_VALIDATE_SHADERDESC_SOURCE); + #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) + // on GL, must provide shader source code + _SG_VALIDATE(0 != desc->vs.source, VALIDATE_SHADERDESC_SOURCE); + _SG_VALIDATE(0 != desc->fs.source, VALIDATE_SHADERDESC_SOURCE); #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) - /* on Metal or D3D11, must provide shader source code or byte code */ - SOKOL_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); - SOKOL_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + // on Metal or D3D11, must provide shader source code or byte code + _SG_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + _SG_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); #elif defined(SOKOL_WGPU) - /* on WGPU byte code must be provided */ - SOKOL_VALIDATE((0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); - SOKOL_VALIDATE((0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); + // on WGPU byte code must be provided + _SG_VALIDATE((0 != desc->vs.bytecode.ptr), VALIDATE_SHADERDESC_BYTECODE); + _SG_VALIDATE((0 != desc->fs.bytecode.ptr), VALIDATE_SHADERDESC_BYTECODE); #else - /* Dummy Backend, don't require source or bytecode */ + // Dummy Backend, don't require source or bytecode #endif for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { if (desc->attrs[i].name) { - SOKOL_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + _SG_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); } if (desc->attrs[i].sem_name) { - SOKOL_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + _SG_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); } } - /* if shader byte code, the size must also be provided */ + // if shader byte code, the size must also be provided if (0 != desc->vs.bytecode.ptr) { - SOKOL_VALIDATE(desc->vs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + _SG_VALIDATE(desc->vs.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); } if (0 != desc->fs.bytecode.ptr) { - SOKOL_VALIDATE(desc->fs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + _SG_VALIDATE(desc->fs.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); } for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == 0)? &desc->vs : &desc->fs; @@ -14715,7 +14774,7 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; if (ub_desc->size > 0) { - SOKOL_VALIDATE(uniform_blocks_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UBS); + _SG_VALIDATE(uniform_blocks_continuous, VALIDATE_SHADERDESC_NO_CONT_UBS); #if defined(_SOKOL_ANY_GL) bool uniforms_continuous = true; uint32_t uniform_offset = 0; @@ -14723,12 +14782,12 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; if (u_desc->type != SG_UNIFORMTYPE_INVALID) { - SOKOL_VALIDATE(uniforms_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - SOKOL_VALIDATE(0 != u_desc->name, _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME); + _SG_VALIDATE(uniforms_continuous, VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); + #if defined(SOKOL_GLES3) + _SG_VALIDATE(0 != u_desc->name, VALIDATE_SHADERDESC_UB_MEMBER_NAME); #endif const int array_count = u_desc->array_count; - SOKOL_VALIDATE(array_count > 0, _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT); + _SG_VALIDATE(array_count > 0, VALIDATE_SHADERDESC_UB_ARRAY_COUNT); const uint32_t u_align = _sg_uniform_alignment(u_desc->type, array_count, ub_desc->layout); const uint32_t u_size = _sg_uniform_size(u_desc->type, array_count, ub_desc->layout); uniform_offset = _sg_align_u32(uniform_offset, u_align); @@ -14737,40 +14796,80 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { // with std140, arrays are only allowed for FLOAT4, INT4, MAT4 if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { if (array_count > 1) { - SOKOL_VALIDATE((u_desc->type == SG_UNIFORMTYPE_FLOAT4) || (u_desc->type == SG_UNIFORMTYPE_INT4) || (u_desc->type == SG_UNIFORMTYPE_MAT4), _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE); + _SG_VALIDATE((u_desc->type == SG_UNIFORMTYPE_FLOAT4) || (u_desc->type == SG_UNIFORMTYPE_INT4) || (u_desc->type == SG_UNIFORMTYPE_MAT4), VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE); } } - } - else { + } else { uniforms_continuous = false; } } if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { uniform_offset = _sg_align_u32(uniform_offset, 16); } - SOKOL_VALIDATE((size_t)uniform_offset == ub_desc->size, _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); - SOKOL_VALIDATE(num_uniforms > 0, _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS); + _SG_VALIDATE((size_t)uniform_offset == ub_desc->size, VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); + _SG_VALIDATE(num_uniforms > 0, VALIDATE_SHADERDESC_NO_UB_MEMBERS); #endif - } - else { + } else { uniform_blocks_continuous = false; } } bool images_continuous = true; + int num_images = 0; for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - if (img_desc->image_type != _SG_IMAGETYPE_DEFAULT) { - SOKOL_VALIDATE(images_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS); - #if defined(SOKOL_GLES2) - SOKOL_VALIDATE(0 != img_desc->name, _SG_VALIDATE_SHADERDESC_IMG_NAME); - #endif - } - else { + if (img_desc->used) { + _SG_VALIDATE(images_continuous, VALIDATE_SHADERDESC_NO_CONT_IMAGES); + num_images++; + } else { images_continuous = false; } } + bool samplers_continuous = true; + int num_samplers = 0; + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (smp_desc->used) { + _SG_VALIDATE(samplers_continuous, VALIDATE_SHADERDESC_NO_CONT_SAMPLERS); + num_samplers++; + } else { + samplers_continuous = false; + } + } + bool image_samplers_continuous = true; + int num_image_samplers = 0; + for (int img_smp_index = 0; img_smp_index < SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + if (img_smp_desc->used) { + _SG_VALIDATE(image_samplers_continuous, VALIDATE_SHADERDESC_NO_CONT_IMAGE_SAMPLER_PAIRS); + num_image_samplers++; + const bool img_slot_in_range = (img_smp_desc->image_slot >= 0) && (img_smp_desc->image_slot < SG_MAX_SHADERSTAGE_IMAGES); + const bool smp_slot_in_range = (img_smp_desc->sampler_slot >= 0) && (img_smp_desc->sampler_slot < SG_MAX_SHADERSTAGE_SAMPLERS); + _SG_VALIDATE(img_slot_in_range && (img_smp_desc->image_slot < num_images), VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE); + _SG_VALIDATE(smp_slot_in_range && (img_smp_desc->sampler_slot < num_samplers), VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE); + #if defined(_SOKOL_ANY_GL) + _SG_VALIDATE(img_smp_desc->glsl_name != 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_NAME_REQUIRED_FOR_GL); + #endif + } else { + _SG_VALIDATE(img_smp_desc->glsl_name == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_NAME_BUT_NOT_USED); + _SG_VALIDATE(img_smp_desc->image_slot == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_IMAGE_BUT_NOT_USED); + _SG_VALIDATE(img_smp_desc->sampler_slot == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_SAMPLER_BUT_NOT_USED); + image_samplers_continuous = false; + } + } + // each image and sampler must be referenced by an image sampler + const uint32_t expected_img_slot_mask = (uint32_t)((1 << num_images) - 1); + const uint32_t expected_smp_slot_mask = (uint32_t)((1 << num_samplers) - 1); + uint32_t actual_img_slot_mask = 0; + uint32_t actual_smp_slot_mask = 0; + for (int img_smp_index = 0; img_smp_index < num_image_samplers; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + actual_img_slot_mask |= (1 << ((uint32_t)img_smp_desc->image_slot & 31)); + actual_smp_slot_mask |= (1 << ((uint32_t)img_smp_desc->sampler_slot & 31)); + } + _SG_VALIDATE(expected_img_slot_mask == actual_img_slot_mask, VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS); + _SG_VALIDATE(expected_smp_slot_mask == actual_smp_slot_mask, VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS); } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14783,41 +14882,38 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { return true; } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); - SOKOL_VALIDATE(desc->shader.id != SG_INVALID_ID, _SG_VALIDATE_PIPELINEDESC_SHADER); - for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[buf_index]; - if (l_desc->stride == 0) { + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_PIPELINEDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_PIPELINEDESC_CANARY); + _SG_VALIDATE(desc->shader.id != SG_INVALID_ID, VALIDATE_PIPELINEDESC_SHADER); + for (int buf_index = 0; buf_index < SG_MAX_VERTEX_BUFFERS; buf_index++) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[buf_index]; + if (l_state->stride == 0) { continue; } - SOKOL_VALIDATE((l_desc->stride & 3) == 0, _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); + _SG_VALIDATE((l_state->stride & 3) == 0, VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); } - SOKOL_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); + _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); - SOKOL_VALIDATE(0 != shd, _SG_VALIDATE_PIPELINEDESC_SHADER); + _SG_VALIDATE(0 != shd, VALIDATE_PIPELINEDESC_SHADER); if (shd) { - SOKOL_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PIPELINEDESC_SHADER); + _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PIPELINEDESC_SHADER); bool attrs_cont = true; for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { attrs_cont = false; continue; } - SOKOL_VALIDATE(attrs_cont, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - #if defined(SOKOL_GLES2) - /* on GLES2, vertex attribute names must be provided */ - SOKOL_VALIDATE(!_sg_strempty(&shd->gl.attrs[attr_index].name), _SG_VALIDATE_PIPELINEDESC_ATTR_NAME); - #elif defined(SOKOL_D3D11) - /* on D3D11, semantic names (and semantic indices) must be provided */ - SOKOL_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); + _SG_VALIDATE(attrs_cont, VALIDATE_PIPELINEDESC_NO_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 + _SG_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); #endif } } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14830,67 +14926,96 @@ _SOKOL_PRIVATE bool _sg_validate_pass_desc(const sg_pass_desc* desc) { return true; } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_PASSDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_PASSDESC_CANARY); bool atts_cont = true; - int width = -1, height = -1, sample_count = -1; + 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]; if (att->image.id == SG_INVALID_ID) { - SOKOL_VALIDATE(att_index > 0, _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS); atts_cont = false; continue; } - SOKOL_VALIDATE(atts_cont, _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); + _SG_VALIDATE(atts_cont, VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); + has_color_atts = true; const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); - SOKOL_ASSERT(img); - SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); - SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); - if (img->cmn.type == SG_IMAGETYPE_CUBE) { - SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); - } - else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); - } - else if (img->cmn.type == SG_IMAGETYPE_3D) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); - } - SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); - if (att_index == 0) { - width = img->cmn.width >> att->mip_level; - height = img->cmn.height >> att->mip_level; - sample_count = img->cmn.sample_count; - } - else { - SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + _SG_VALIDATE(img, VALIDATE_PASSDESC_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); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(att->slice < 6, VALIDATE_PASSDESC_FACE); + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_LAYER); + } else if (img->cmn.type == SG_IMAGETYPE_3D) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_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(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); + + // check resolve attachment + const sg_pass_attachment_desc* res_att = &desc->resolve_attachments[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); + const _sg_image_t* res_img = _sg_lookup_image(&_sg.pools, res_att->image.id); + _SG_VALIDATE(res_img, VALIDATE_PASSDESC_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); + if (res_img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(res_att->slice < 6, VALIDATE_PASSDESC_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); + } 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(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); + } + } } - SOKOL_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); } + 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; const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); - SOKOL_ASSERT(img); - SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); - SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); - if (img->cmn.type == SG_IMAGETYPE_CUBE) { - SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); - } - else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); - } - else if (img->cmn.type == SG_IMAGETYPE_3D) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); + _SG_VALIDATE(img, VALIDATE_PASSDESC_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); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(att->slice < 6, VALIDATE_PASSDESC_DEPTH_FACE); + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_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(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); } - SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); - SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); - SOKOL_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT); } - return SOKOL_VALIDATE_END(); + _SG_VALIDATE(has_color_atts || has_depth_stencil_att, VALIDATE_PASSDESC_NO_ATTACHMENTS); + return _sg_validate_end(); #endif } @@ -14902,24 +15027,30 @@ _SOKOL_PRIVATE bool _sg_validate_begin_pass(_sg_pass_t* pass) { if (_sg.desc.disable_validation) { return true; } - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_PASS); + _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* att = &pass->cmn.color_atts[i]; - const _sg_image_t* img = _sg_pass_color_image(pass, i); - if (img) { - SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); - SOKOL_VALIDATE(img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + 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); } } const _sg_image_t* ds_img = _sg_pass_ds_image(pass); if (ds_img) { const _sg_pass_attachment_t* att = &pass->cmn.ds_att; - SOKOL_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); - SOKOL_VALIDATE(ds_img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + _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); } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14931,45 +15062,43 @@ _SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { if (_sg.desc.disable_validation) { return true; } - SOKOL_VALIDATE_BEGIN(); - /* the pipeline object must be alive and valid */ - SOKOL_VALIDATE(pip_id.id != SG_INVALID_ID, _SG_VALIDATE_APIP_PIPELINE_VALID_ID); + _sg_validate_begin(); + // the pipeline object must be alive and valid + _SG_VALIDATE(pip_id.id != SG_INVALID_ID, VALIDATE_APIP_PIPELINE_VALID_ID); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_APIP_PIPELINE_EXISTS); + _SG_VALIDATE(pip != 0, VALIDATE_APIP_PIPELINE_EXISTS); if (!pip) { - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); } - SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_PIPELINE_VALID); - /* the pipeline's shader must be alive and valid */ + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_PIPELINE_VALID); + // the pipeline's shader must be alive and valid SOKOL_ASSERT(pip->shader); - SOKOL_VALIDATE(pip->shader->slot.id == pip->cmn.shader_id.id, _SG_VALIDATE_APIP_SHADER_EXISTS); - SOKOL_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_SHADER_VALID); - /* check that pipeline attributes match current pass attributes */ + _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) { - /* an offscreen pass */ - SOKOL_VALIDATE(pip->cmn.color_attachment_count == pass->cmn.num_color_atts, _SG_VALIDATE_APIP_ATT_COUNT); - for (int i = 0; i < pip->cmn.color_attachment_count; i++) { + // an offscreen pass + _SG_VALIDATE(pip->cmn.color_count == pass->cmn.num_color_atts, 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); - SOKOL_VALIDATE(pip->cmn.color_formats[i] == att_img->cmn.pixel_format, _SG_VALIDATE_APIP_COLOR_FORMAT); - SOKOL_VALIDATE(pip->cmn.sample_count == att_img->cmn.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); + _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); if (att_dsimg) { - SOKOL_VALIDATE(pip->cmn.depth_format == att_dsimg->cmn.pixel_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); - } - else { - SOKOL_VALIDATE(pip->cmn.depth_format == SG_PIXELFORMAT_NONE, _SG_VALIDATE_APIP_DEPTH_FORMAT); + _SG_VALIDATE(pip->cmn.depth.pixel_format == att_dsimg->cmn.pixel_format, VALIDATE_APIP_DEPTH_FORMAT); + } else { + _SG_VALIDATE(pip->cmn.depth.pixel_format == SG_PIXELFORMAT_NONE, VALIDATE_APIP_DEPTH_FORMAT); } - } - else { - /* default pass */ - SOKOL_VALIDATE(pip->cmn.color_attachment_count == 1, _SG_VALIDATE_APIP_ATT_COUNT); - SOKOL_VALIDATE(pip->cmn.color_formats[0] == _sg.desc.context.color_format, _SG_VALIDATE_APIP_COLOR_FORMAT); - SOKOL_VALIDATE(pip->cmn.depth_format == _sg.desc.context.depth_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); - SOKOL_VALIDATE(pip->cmn.sample_count == _sg.desc.context.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); - } - return SOKOL_VALIDATE_END(); + } 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); + } + return _sg_validate_end(); #endif } @@ -14981,87 +15110,159 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { if (_sg.desc.disable_validation) { return true; } - SOKOL_VALIDATE_BEGIN(); + _sg_validate_begin(); - /* a pipeline object must have been applied */ - SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_ABND_PIPELINE); + // a pipeline object must have been applied + _SG_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, VALIDATE_ABND_PIPELINE); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); - SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_ABND_PIPELINE_EXISTS); + _SG_VALIDATE(pip != 0, VALIDATE_ABND_PIPELINE_EXISTS); if (!pip) { - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); } - SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_ABND_PIPELINE_VALID); + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ABND_PIPELINE_VALID); SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); - /* has expected vertex buffers, and vertex buffers still exist */ - for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { + // has expected vertex buffers, and vertex buffers still exist + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++) { if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { - SOKOL_VALIDATE(pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); - /* buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER */ + _SG_VALIDATE(pip->cmn.vertex_buffer_layout_active[i], VALIDATE_ABND_VBS); + // buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); - SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_VB_EXISTS); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_VB_EXISTS); if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_VB_TYPE); - SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_VB_OVERFLOW); + _SG_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, VALIDATE_ABND_VB_TYPE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_VB_OVERFLOW); } - } - else { - /* vertex buffer provided in a slot which has no vertex layout in pipeline */ - SOKOL_VALIDATE(!pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + } else { + // vertex buffer provided in a slot which has no vertex layout in pipeline + _SG_VALIDATE(!pip->cmn.vertex_buffer_layout_active[i], VALIDATE_ABND_VBS); } } - /* index buffer expected or not, and index buffer still exists */ + // index buffer expected or not, and index buffer still exists if (pip->cmn.index_type == SG_INDEXTYPE_NONE) { - /* pipeline defines non-indexed rendering, but index buffer provided */ - SOKOL_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, _SG_VALIDATE_ABND_IB); - } - else { - /* pipeline defines indexed rendering, but no index buffer provided */ - SOKOL_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, _SG_VALIDATE_ABND_NO_IB); + // pipeline defines non-indexed rendering, but index buffer provided + _SG_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, VALIDATE_ABND_IB); + } else { + // pipeline defines indexed rendering, but no index buffer provided + _SG_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, VALIDATE_ABND_NO_IB); } if (bindings->index_buffer.id != SG_INVALID_ID) { - /* buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER */ + // buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); - SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_IB_EXISTS); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_IB_EXISTS); if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_IB_TYPE); - SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_IB_OVERFLOW); + _SG_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, VALIDATE_ABND_IB_TYPE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_IB_OVERFLOW); } } - /* has expected vertex shader images */ + // has expected vertex shader images for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; - if (bindings->vs_images[i].id != SG_INVALID_ID) { - SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); - const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); - SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_VS_IMG_EXISTS); - if (img && img->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(img->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_VS_IMG_TYPES); + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->images[i].image_type != _SG_IMAGETYPE_DEFAULT) { + _SG_VALIDATE(bindings->vs.images[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_IMAGE_BINDING); + if (bindings->vs.images[i].id != SG_INVALID_ID) { + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs.images[i].id); + _SG_VALIDATE(img != 0, VALIDATE_ABND_VS_IMG_EXISTS); + 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); + } } + } else { + _SG_VALIDATE(bindings->vs.images[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_IMAGE_BINDING); } - else { - SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); + } + + // has expected vertex shader image samplers + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->samplers[i].sampler_type != _SG_SAMPLERTYPE_DEFAULT) { + _SG_VALIDATE(bindings->vs.samplers[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_SAMPLER_BINDING); + if (bindings->vs.samplers[i].id != SG_INVALID_ID) { + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[i].id); + _SG_VALIDATE(smp != 0, VALIDATE_ABND_VS_SMP_EXISTS); + if (smp) { + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARE) { + _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_COMPARE_NEVER); + } else { + _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_VS_EXPECTED_SAMPLER_COMPARE_NEVER); + } + } + } + } else { + _SG_VALIDATE(bindings->vs.samplers[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_BINDING); } } - /* has expected fragment shader images */ + // has expected fragment shader images for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; - if (bindings->fs_images[i].id != SG_INVALID_ID) { - SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); - const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); - SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_FS_IMG_EXISTS); - if (img && img->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(img->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_FS_IMG_TYPES); + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (stage->images[i].image_type != _SG_IMAGETYPE_DEFAULT) { + _SG_VALIDATE(bindings->fs.images[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_IMAGE_BINDING); + if (bindings->fs.images[i].id != SG_INVALID_ID) { + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); + _SG_VALIDATE(img != 0, VALIDATE_ABND_FS_IMG_EXISTS); + 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); + } + } + } else { + _SG_VALIDATE(bindings->fs.images[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_IMAGE_BINDING); + } + } + + // has expected fragment shader samplers + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (stage->samplers[i].sampler_type != _SG_SAMPLERTYPE_DEFAULT) { + _SG_VALIDATE(bindings->fs.samplers[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_SAMPLER_BINDING); + if (bindings->fs.samplers[i].id != SG_INVALID_ID) { + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[i].id); + _SG_VALIDATE(smp != 0, VALIDATE_ABND_FS_SMP_EXISTS); + if (smp) { + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARE) { + _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_COMPARE_NEVER); + } else { + _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_FS_EXPECTED_SAMPLER_COMPARE_NEVER); + } + } + } + } else { + _SG_VALIDATE(bindings->fs.samplers[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING); + } + } + + // 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); } } - else { - SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); + } + for (int img_smp_index = 0; img_smp_index < pip->shader->cmn.stage[SG_SHADERSTAGE_FS].num_image_samplers; img_smp_index++) { + 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); + } } } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -15077,20 +15278,20 @@ _SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int } SOKOL_ASSERT((stage_index == SG_SHADERSTAGE_VS) || (stage_index == SG_SHADERSTAGE_FS)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_AUB_NO_PIPELINE); + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, VALIDATE_AUB_NO_PIPELINE); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); SOKOL_ASSERT(pip && (pip->slot.id == _sg.cur_pipeline.id)); SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); - /* check that there is a uniform block at 'stage' and 'ub_index' */ + // check that there is a uniform block at 'stage' and 'ub_index' const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; - SOKOL_VALIDATE(ub_index < stage->num_uniform_blocks, _SG_VALIDATE_AUB_NO_UB_AT_SLOT); + _SG_VALIDATE(ub_index < stage->num_uniform_blocks, VALIDATE_AUB_NO_UB_AT_SLOT); - /* check that the provided data size doesn't exceed the uniform block size */ - SOKOL_VALIDATE(data->size == stage->uniform_blocks[ub_index].size, _SG_VALIDATE_AUB_SIZE); + // check that the provided data size doesn't exceed the uniform block size + _SG_VALIDATE(data->size == stage->uniform_blocks[ub_index].size, VALIDATE_AUB_SIZE); - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -15104,12 +15305,12 @@ _SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const sg return true; } SOKOL_ASSERT(buf && data && data->ptr); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDATEBUF_USAGE); - SOKOL_VALIDATE(buf->cmn.size >= (int)data->size, _SG_VALIDATE_UPDATEBUF_SIZE); - SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_ONCE); - SOKOL_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_APPEND); - return SOKOL_VALIDATE_END(); + _sg_validate_begin(); + _SG_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_UPDATEBUF_USAGE); + _SG_VALIDATE(buf->cmn.size >= (int)data->size, VALIDATE_UPDATEBUF_SIZE); + _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_ONCE); + _SG_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_APPEND); + return _sg_validate_end(); #endif } @@ -15123,11 +15324,11 @@ _SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const sg return true; } SOKOL_ASSERT(buf && data && data->ptr); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_APPENDBUF_USAGE); - SOKOL_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), _SG_VALIDATE_APPENDBUF_SIZE); - SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_APPENDBUF_UPDATE); - return SOKOL_VALIDATE_END(); + _sg_validate_begin(); + _SG_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_APPENDBUF_USAGE); + _SG_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), VALIDATE_APPENDBUF_SIZE); + _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_APPENDBUF_UPDATE); + return _sg_validate_end(); #endif } @@ -15141,9 +15342,9 @@ _SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_i return true; } SOKOL_ASSERT(img && data); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDIMG_USAGE); - SOKOL_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, _SG_VALIDATE_UPDIMG_ONCE); + _sg_validate_begin(); + _SG_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_UPDIMG_USAGE); + _SG_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, VALIDATE_UPDIMG_ONCE); _sg_validate_image_data(data, img->cmn.pixel_format, img->cmn.width, @@ -15151,19 +15352,24 @@ _SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_i (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1, img->cmn.num_mipmaps, img->cmn.num_slices); - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } -/*== fill in desc default values =============================================*/ +// ██████ ███████ ███████ ██████ ██ ██ ██████ ██████ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ █████ ███████ ██ ██ ██ ██ ██████ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██████ ██████ ██ ██ ██████ ███████ ███████ +// +// >>resources _SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* desc) { sg_buffer_desc def = *desc; def.type = _sg_def(def.type, SG_BUFFERTYPE_VERTEXBUFFER); def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); if (def.size == 0) { def.size = def.data.size; - } - else if (def.data.size == 0) { + } else if (def.data.size == 0) { def.data.size = def.size; } return def; @@ -15178,19 +15384,25 @@ _SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) 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); - } - else { + } else { def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8); def.sample_count = _sg_def(def.sample_count, 1); } + return def; +} + +_SOKOL_PRIVATE sg_sampler_desc _sg_sampler_desc_defaults(const sg_sampler_desc* 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.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); + def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX); def.border_color = _sg_def(def.border_color, SG_BORDERCOLOR_OPAQUE_BLACK); + def.compare = _sg_def(def.compare, SG_COMPAREFUNC_NEVER); def.max_anisotropy = _sg_def(def.max_anisotropy, 1); - def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX); return def; } @@ -15229,10 +15441,18 @@ _SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* des } for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - if (img_desc->image_type == _SG_IMAGETYPE_DEFAULT) { + if (!img_desc->used) { + break; + } + img_desc->image_type = _sg_def(img_desc->image_type, SG_IMAGETYPE_2D); + img_desc->sample_type = _sg_def(img_desc->sample_type, SG_IMAGESAMPLETYPE_FLOAT); + } + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (!smp_desc->used) { break; } - img_desc->sampler_type = _sg_def(img_desc->sampler_type, SG_SAMPLERTYPE_FLOAT); + smp_desc->sampler_type = _sg_def(smp_desc->sampler_type, SG_SAMPLERTYPE_SAMPLE); } } return def; @@ -15258,12 +15478,17 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des 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.color_count = _sg_def(def.color_count, 1); + if (def.colors[0].pixel_format == SG_PIXELFORMAT_NONE) { + // special case depth-only rendering, enforce a color count of 0 + def.color_count = 0; + } else { + def.color_count = _sg_def(def.color_count, 1); + } if (def.color_count > SG_MAX_COLOR_ATTACHMENTS) { def.color_count = SG_MAX_COLOR_ATTACHMENTS; } for (int i = 0; i < def.color_count; i++) { - sg_color_state* cs = &def.colors[i]; + sg_color_target_state* cs = &def.colors[i]; cs->pixel_format = _sg_def(cs->pixel_format, _sg.desc.context.color_format); cs->write_mask = _sg_def(cs->write_mask, SG_COLORMASK_RGBA); sg_blend_state* bs = &def.colors[i].blend; @@ -15276,42 +15501,42 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des } for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - sg_buffer_layout_desc* b_desc = &def.layout.buffers[a_desc->buffer_index]; - b_desc->step_func = _sg_def(b_desc->step_func, SG_VERTEXSTEP_PER_VERTEX); - b_desc->step_rate = _sg_def(b_desc->step_rate, 1); + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[a_state->buffer_index]; + l_state->step_func = _sg_def(l_state->step_func, SG_VERTEXSTEP_PER_VERTEX); + l_state->step_rate = _sg_def(l_state->step_rate, 1); } - /* resolve vertex layout strides and offsets */ - int auto_offset[SG_MAX_SHADERSTAGE_BUFFERS]; + // resolve vertex layout strides and offsets + int auto_offset[SG_MAX_VERTEX_BUFFERS]; _sg_clear(auto_offset, sizeof(auto_offset)); bool use_auto_offset = true; for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - /* to use computed offsets, *all* attr offsets must be 0 */ + // to use computed offsets, *all* attr offsets must be 0 if (def.layout.attrs[attr_index].offset != 0) { use_auto_offset = false; } } for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); if (use_auto_offset) { - a_desc->offset = auto_offset[a_desc->buffer_index]; + a_state->offset = auto_offset[a_state->buffer_index]; } - auto_offset[a_desc->buffer_index] += _sg_vertexformat_bytesize(a_desc->format); + auto_offset[a_state->buffer_index] += _sg_vertexformat_bytesize(a_state->format); } - /* compute vertex strides if needed */ - for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { - sg_buffer_layout_desc* l_desc = &def.layout.buffers[buf_index]; - if (l_desc->stride == 0) { - l_desc->stride = auto_offset[buf_index]; + // compute vertex strides if needed + for (int buf_index = 0; buf_index < SG_MAX_VERTEX_BUFFERS; buf_index++) { + sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[buf_index]; + if (l_state->stride == 0) { + l_state->stride = auto_offset[buf_index]; } } @@ -15319,21 +15544,19 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des } _SOKOL_PRIVATE sg_pass_desc _sg_pass_desc_defaults(const sg_pass_desc* desc) { - /* FIXME: no values to replace in sg_pass_desc? */ + // FIXME: no values to replace in sg_pass_desc? sg_pass_desc def = *desc; return def; } -/*== allocate/initialize resource private functions ==========================*/ _SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { sg_buffer res; int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ + } else { res.id = SG_INVALID_ID; + _SG_ERROR(BUFFER_POOL_EXHAUSTED); } return res; } @@ -15343,10 +15566,21 @@ _SOKOL_PRIVATE sg_image _sg_alloc_image(void) { int slot_index = _sg_pool_alloc_index(&_sg.pools.image_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(IMAGE_POOL_EXHAUSTED); } - else { - /* pool is exhausted */ + return res; +} + +_SOKOL_PRIVATE sg_sampler _sg_alloc_sampler(void) { + sg_sampler res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.sampler_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.sampler_pool, &_sg.pools.samplers[slot_index].slot, slot_index); + } else { res.id = SG_INVALID_ID; + _SG_ERROR(SAMPLER_POOL_EXHAUSTED); } return res; } @@ -15356,10 +15590,9 @@ _SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) { int slot_index = _sg_pool_alloc_index(&_sg.pools.shader_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ + } else { res.id = SG_INVALID_ID; + _SG_ERROR(SHADER_POOL_EXHAUSTED); } return res; } @@ -15369,10 +15602,9 @@ _SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { int slot_index = _sg_pool_alloc_index(&_sg.pools.pipeline_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ + } else { res.id = SG_INVALID_ID; + _SG_ERROR(PIPELINE_POOL_EXHAUSTED); } return res; } @@ -15382,10 +15614,9 @@ _SOKOL_PRIVATE sg_pass _sg_alloc_pass(void) { int slot_index = _sg_pool_alloc_index(&_sg.pools.pass_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); - } - else { - /* pool is exhausted */ + } else { res.id = SG_INVALID_ID; + _SG_ERROR(PASS_POOL_EXHAUSTED); } return res; } @@ -15402,6 +15633,12 @@ _SOKOL_PRIVATE void _sg_dealloc_image(_sg_image_t* img) { _sg_reset_slot(&img->slot); } +_SOKOL_PRIVATE void _sg_dealloc_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC) && (smp->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.sampler_pool, _sg_slot_index(smp->slot.id)); + _sg_reset_slot(&smp->slot); +} + _SOKOL_PRIVATE void _sg_dealloc_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC) && (shd->slot.id != SG_INVALID_ID)); _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd->slot.id)); @@ -15425,9 +15662,9 @@ _SOKOL_PRIVATE void _sg_init_buffer(_sg_buffer_t* buf, const sg_buffer_desc* des 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); - } - else { + } else { buf->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED)); @@ -15438,22 +15675,35 @@ _SOKOL_PRIVATE void _sg_init_image(_sg_image_t* img, const sg_image_desc* desc) 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); - } - else { + } else { img->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED)); } +_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); + } else { + smp->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID)||(smp->slot.state == SG_RESOURCESTATE_FAILED)); +} + _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); - } - else { + } else { shd->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED)); @@ -15466,13 +15716,12 @@ _SOKOL_PRIVATE void _sg_init_pipeline(_sg_pipeline_t* pip, const sg_pipeline_des 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)) { + _sg_pipeline_common_init(&pip->cmn, desc); pip->slot.state = _sg_create_pipeline(pip, shd, desc); - } - else { + } else { pip->slot.state = SG_RESOURCESTATE_FAILED; } - } - else { + } else { pip->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); @@ -15483,34 +15732,45 @@ _SOKOL_PRIVATE void _sg_init_pass(_sg_pass_t* pass, const sg_pass_desc* desc) { SOKOL_ASSERT(desc); pass->slot.ctx_id = _sg.active_context.id; if (_sg_validate_pass_desc(desc)) { - /* lookup pass attachment image pointers */ - _sg_image_t* att_imgs[SG_MAX_COLOR_ATTACHMENTS + 1]; + // 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 }; + _sg_image_t* ds_image = 0; + // NOTE: validation already checked that all surfaces are same width/height + int width = 0; + int height = 0; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { if (desc->color_attachments[i].image.id) { - att_imgs[i] = _sg_lookup_image(&_sg.pools, desc->color_attachments[i].image.id); - if (!(att_imgs[i] && att_imgs[i]->slot.state == SG_RESOURCESTATE_VALID)) { + color_images[i] = _sg_lookup_image(&_sg.pools, desc->color_attachments[i].image.id); + if (!(color_images[i] && color_images[i]->slot.state == SG_RESOURCESTATE_VALID)) { pass->slot.state = SG_RESOURCESTATE_FAILED; return; } + const int mip_level = desc->color_attachments[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); } - else { - att_imgs[i] = 0; + if (desc->resolve_attachments[i].image.id) { + resolve_images[i] = _sg_lookup_image(&_sg.pools, desc->resolve_attachments[i].image.id); + if (!(resolve_images[i] && resolve_images[i]->slot.state == SG_RESOURCESTATE_VALID)) { + pass->slot.state = SG_RESOURCESTATE_FAILED; + return; + } } } - const int ds_att_index = SG_MAX_COLOR_ATTACHMENTS; if (desc->depth_stencil_attachment.image.id) { - att_imgs[ds_att_index] = _sg_lookup_image(&_sg.pools, desc->depth_stencil_attachment.image.id); - if (!(att_imgs[ds_att_index] && att_imgs[ds_att_index]->slot.state == SG_RESOURCESTATE_VALID)) { + ds_image = _sg_lookup_image(&_sg.pools, desc->depth_stencil_attachment.image.id); + if (!(ds_image && ds_image->slot.state == SG_RESOURCESTATE_VALID)) { pass->slot.state = SG_RESOURCESTATE_FAILED; return; } + const int mip_level = desc->depth_stencil_attachment.mip_level; + width = _sg_miplevel_dim(ds_image->cmn.width, mip_level); + height = _sg_miplevel_dim(ds_image->cmn.height, mip_level); } - else { - att_imgs[ds_att_index] = 0; - } - pass->slot.state = _sg_create_pass(pass, att_imgs, desc); - } - else { + _sg_pass_common_init(&pass->cmn, desc, width, height); + pass->slot.state = _sg_create_pass(pass, color_images, resolve_images, ds_image, desc); + } else { pass->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID)||(pass->slot.state == SG_RESOURCESTATE_FAILED)); @@ -15521,10 +15781,8 @@ _SOKOL_PRIVATE void _sg_uninit_buffer(_sg_buffer_t* buf) { if (buf->slot.ctx_id == _sg.active_context.id) { _sg_discard_buffer(buf); _sg_reset_buffer_to_alloc_state(buf); - } - else { - SG_LOG("_sg_uninit_buffer: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); + } else { + _SG_WARN(UNINIT_BUFFER_ACTIVE_CONTEXT_MISMATCH); } } @@ -15533,10 +15791,18 @@ _SOKOL_PRIVATE void _sg_uninit_image(_sg_image_t* img) { 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); } - else { - SG_LOG("_sg_uninit_image: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); +} + +_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); } } @@ -15545,10 +15811,8 @@ _SOKOL_PRIVATE void _sg_uninit_shader(_sg_shader_t* shd) { if (shd->slot.ctx_id == _sg.active_context.id) { _sg_discard_shader(shd); _sg_reset_shader_to_alloc_state(shd); - } - else { - SG_LOG("_sg_uninit_shader: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); + } else { + _SG_WARN(UNINIT_SHADER_ACTIVE_CONTEXT_MISMATCH); } } @@ -15557,10 +15821,8 @@ _SOKOL_PRIVATE void _sg_uninit_pipeline(_sg_pipeline_t* pip) { if (pip->slot.ctx_id == _sg.active_context.id) { _sg_discard_pipeline(pip); _sg_reset_pipeline_to_alloc_state(pip); - } - else { - SG_LOG("_sg_uninit_pipeline: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); + } else { + _SG_WARN(UNINIT_PIPELINE_ACTIVE_CONTEXT_MISMATCH); } } @@ -15569,10 +15831,8 @@ _SOKOL_PRIVATE void _sg_uninit_pass(_sg_pass_t* pass) { if (pass->slot.ctx_id == _sg.active_context.id) { _sg_discard_pass(pass); _sg_reset_pass_to_alloc_state(pass); - } - else { - SG_LOG("_sg_uninit_pass: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); + } else { + _SG_WARN(UNINIT_PASS_ACTIVE_CONTEXT_MISMATCH); } } @@ -15609,7 +15869,7 @@ _SOKOL_PRIVATE bool _sg_add_commit_listener(const sg_commit_listener* new_listen for (int i = 0; i < _sg.commit_listeners.upper; i++) { const sg_commit_listener* slot = &_sg.commit_listeners.items[i]; if ((slot->func == new_listener->func) && (slot->user_data == new_listener->user_data)) { - SG_LOG("attempting to add identical commit listener\n"); + _SG_ERROR(IDENTICAL_COMMIT_LISTENER); return false; } } @@ -15628,7 +15888,7 @@ _SOKOL_PRIVATE bool _sg_add_commit_listener(const sg_commit_listener* new_listen } } if (!slot) { - SG_LOG("commit listener array full\n"); + _SG_ERROR(COMMIT_LISTENER_ARRAY_FULL); return false; } *slot = *new_listener; @@ -15669,19 +15929,24 @@ _SOKOL_PRIVATE sg_desc _sg_desc_defaults(const sg_desc* desc) { res.context.sample_count = _sg_def(res.context.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.uniform_buffer_size = _sg_def(res.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); res.staging_buffer_size = _sg_def(res.staging_buffer_size, _SG_DEFAULT_STAGING_SIZE); - res.sampler_cache_size = _sg_def(res.sampler_cache_size, _SG_DEFAULT_SAMPLER_CACHE_CAPACITY); res.max_commit_listeners = _sg_def(res.max_commit_listeners, _SG_DEFAULT_MAX_COMMIT_LISTENERS); return res; } -/*== PUBLIC API FUNCTIONS ====================================================*/ - +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { SOKOL_ASSERT(desc); SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); @@ -15755,9 +16020,8 @@ SOKOL_API_IMPL sg_context sg_setup_context(void) { ctx->slot.state = _sg_create_context(ctx); SOKOL_ASSERT(ctx->slot.state == SG_RESOURCESTATE_VALID); _sg_activate_context(ctx); - } - else { - /* pool is exhausted */ + } else { + // pool is exhausted res.id = SG_INVALID_ID; } _sg.active_context = res; @@ -15782,7 +16046,7 @@ SOKOL_API_IMPL void sg_activate_context(sg_context ctx_id) { 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 */ + // NOTE: ctx can be 0 here if the context is no longer valid _sg_activate_context(ctx); } @@ -15795,7 +16059,7 @@ SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace _sg.hooks = *trace_hooks; #else static sg_trace_hooks old_hooks; - SG_LOG("sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined!"); + _SG_WARN(TRACE_HOOKS_NOT_ENABLED); #endif return old_hooks; } @@ -15814,6 +16078,13 @@ SOKOL_API_IMPL sg_image sg_alloc_image(void) { return res; } +SOKOL_API_IMPL sg_sampler sg_alloc_sampler(void) { + SOKOL_ASSERT(_sg.valid); + sg_sampler res = _sg_alloc_sampler(); + _SG_TRACE_ARGS(alloc_sampler, res); + return res; +} + SOKOL_API_IMPL sg_shader sg_alloc_shader(void) { SOKOL_ASSERT(_sg.valid); sg_shader res = _sg_alloc_shader(); @@ -15841,9 +16112,8 @@ SOKOL_API_IMPL void sg_dealloc_buffer(sg_buffer buf_id) { if (buf) { if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_dealloc_buffer(buf); - } - else { - SG_LOG("sg_dealloc_buffer: buffer must be in ALLOC state\n"); + } else { + _SG_ERROR(DEALLOC_BUFFER_INVALID_STATE); } } _SG_TRACE_ARGS(dealloc_buffer, buf_id); @@ -15855,23 +16125,34 @@ SOKOL_API_IMPL void sg_dealloc_image(sg_image img_id) { if (img) { if (img->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_dealloc_image(img); - } - else { - SG_LOG("sg_dealloc_image: image must be in ALLOC state\n"); + } else { + _SG_ERROR(DEALLOC_IMAGE_INVALID_STATE); } } _SG_TRACE_ARGS(dealloc_image, img_id); } +SOKOL_API_IMPL void sg_dealloc_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_sampler(smp); + } else { + _SG_ERROR(DEALLOC_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_sampler, smp_id); +} + SOKOL_API_IMPL void sg_dealloc_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); if (shd) { if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_dealloc_shader(shd); - } - else { - SG_LOG("sg_dealloc_shader: shader must be in ALLOC state\n"); + } else { + _SG_ERROR(DEALLOC_SHADER_INVALID_STATE); } } _SG_TRACE_ARGS(dealloc_shader, shd_id); @@ -15883,9 +16164,8 @@ SOKOL_API_IMPL void sg_dealloc_pipeline(sg_pipeline pip_id) { if (pip) { if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_dealloc_pipeline(pip); - } - else { - SG_LOG("sg_dealloc_pipeline: pipeline must be in ALLOC state\n"); + } else { + _SG_ERROR(DEALLOC_PIPELINE_INVALID_STATE); } } _SG_TRACE_ARGS(dealloc_pipeline, pip_id); @@ -15897,9 +16177,8 @@ SOKOL_API_IMPL void sg_dealloc_pass(sg_pass pass_id) { if (pass) { if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_dealloc_pass(pass); - } - else { - SG_LOG("sg_dealloc_pass: pass must be in ALLOC state\n"); + } else { + _SG_ERROR(DEALLOC_PASS_INVALID_STATE); } } _SG_TRACE_ARGS(dealloc_pass, pass_id); @@ -15913,9 +16192,8 @@ SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_init_buffer(buf, &desc_def); SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)); - } - else { - SG_LOG("sg_init_buffer: buffer must be in alloc state\n"); + } else { + _SG_ERROR(INIT_BUFFER_INVALID_STATE); } } _SG_TRACE_ARGS(init_buffer, buf_id, &desc_def); @@ -15929,14 +16207,28 @@ SOKOL_API_IMPL void sg_init_image(sg_image img_id, const sg_image_desc* desc) { if (img->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_init_image(img, &desc_def); SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)); - } - else { - SG_LOG("sg_init_image: image must be in alloc state\n"); + } else { + _SG_ERROR(INIT_IMAGE_INVALID_STATE); } } _SG_TRACE_ARGS(init_image, img_id, &desc_def); } +SOKOL_API_IMPL void sg_init_sampler(sg_sampler smp_id, const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_sampler(smp, &desc_def); + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_sampler, smp_id, &desc_def); +} + SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { SOKOL_ASSERT(_sg.valid); sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); @@ -15945,9 +16237,8 @@ SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_init_shader(shd, &desc_def); SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)); - } - else { - SG_LOG("sg_init_shader: shader must be in alloc state\n"); + } else { + _SG_ERROR(INIT_SHADER_INVALID_STATE); } } _SG_TRACE_ARGS(init_shader, shd_id, &desc_def); @@ -15961,9 +16252,8 @@ SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_init_pipeline(pip, &desc_def); SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)); - } - else { - SG_LOG("sg_init_pipeline: pipeline must be in alloc state\n"); + } else { + _SG_ERROR(INIT_PIPELINE_INVALID_STATE); } } _SG_TRACE_ARGS(init_pipeline, pip_id, &desc_def); @@ -15977,9 +16267,8 @@ SOKOL_API_IMPL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { 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)); - } - else { - SG_LOG("sg_init_pass: pass must be in alloc state\n"); + } else { + _SG_ERROR(INIT_PASS_INVALID_STATE); } } _SG_TRACE_ARGS(init_pass, pass_id, &desc_def); @@ -15992,9 +16281,8 @@ SOKOL_API_IMPL void sg_uninit_buffer(sg_buffer buf_id) { if ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)) { _sg_uninit_buffer(buf); SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC); - } - else { - SG_LOG("sg_uninit_buffer: buffer must be in VALID or FAILED state\n"); + } else { + _SG_ERROR(UNINIT_BUFFER_INVALID_STATE); } } _SG_TRACE_ARGS(uninit_buffer, buf_id); @@ -16007,14 +16295,27 @@ SOKOL_API_IMPL void sg_uninit_image(sg_image img_id) { if ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)) { _sg_uninit_image(img); SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_ALLOC); - } - else { - SG_LOG("sg_uninit_image: image must be in VALID or FAILED state\n"); + } else { + _SG_ERROR(UNINIT_IMAGE_INVALID_STATE); } } _SG_TRACE_ARGS(uninit_image, img_id); } +SOKOL_API_IMPL void sg_uninit_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_sampler, smp_id); +} + SOKOL_API_IMPL void sg_uninit_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); @@ -16022,9 +16323,8 @@ SOKOL_API_IMPL void sg_uninit_shader(sg_shader shd_id) { if ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)) { _sg_uninit_shader(shd); SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_ALLOC); - } - else { - SG_LOG("sg_uninit_shader: shader must be in VALID or FAILED state\n"); + } else { + _SG_ERROR(UNINIT_SHADER_INVALID_STATE); } } _SG_TRACE_ARGS(uninit_shader, shd_id); @@ -16037,9 +16337,8 @@ SOKOL_API_IMPL void sg_uninit_pipeline(sg_pipeline pip_id) { if ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)) { _sg_uninit_pipeline(pip); SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_ALLOC); - } - else { - SG_LOG("sg_uninit_pipeline: pipeline must be in VALID or FAILED state\n"); + } else { + _SG_ERROR(UNINIT_PIPELINE_INVALID_STATE); } } _SG_TRACE_ARGS(uninit_pipeline, pip_id); @@ -16052,15 +16351,13 @@ SOKOL_API_IMPL void sg_uninit_pass(sg_pass pass_id) { 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); - } - else { - SG_LOG("sg_uninit_pass: pass must be in VALID or FAILED state\n"); + } else { + _SG_ERROR(UNINIT_PASS_INVALID_STATE); } } _SG_TRACE_ARGS(uninit_pass, pass_id); } -/*-- set allocated resource to failed state ----------------------------------*/ SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); @@ -16068,9 +16365,8 @@ SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { buf->slot.ctx_id = _sg.active_context.id; buf->slot.state = SG_RESOURCESTATE_FAILED; - } - else { - SG_LOG("sg_fail_buffer: buffer must be in ALLOC state\n"); + } else { + _SG_ERROR(FAIL_BUFFER_INVALID_STATE); } } _SG_TRACE_ARGS(fail_buffer, buf_id); @@ -16083,14 +16379,27 @@ SOKOL_API_IMPL void sg_fail_image(sg_image img_id) { if (img->slot.state == SG_RESOURCESTATE_ALLOC) { img->slot.ctx_id = _sg.active_context.id; img->slot.state = SG_RESOURCESTATE_FAILED; - } - else { - SG_LOG("sg_fail_image: image must be in ALLOC state\n"); + } else { + _SG_ERROR(FAIL_IMAGE_INVALID_STATE); } } _SG_TRACE_ARGS(fail_image, img_id); } +SOKOL_API_IMPL void sg_fail_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _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); + } + } + _SG_TRACE_ARGS(fail_sampler, smp_id); +} + SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); @@ -16098,9 +16407,8 @@ SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { shd->slot.ctx_id = _sg.active_context.id; shd->slot.state = SG_RESOURCESTATE_FAILED; - } - else { - SG_LOG("sg_fail_shader: shader must be in ALLOC state\n"); + } else { + _SG_ERROR(FAIL_SHADER_INVALID_STATE); } } _SG_TRACE_ARGS(fail_shader, shd_id); @@ -16113,9 +16421,8 @@ SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { pip->slot.ctx_id = _sg.active_context.id; pip->slot.state = SG_RESOURCESTATE_FAILED; - } - else { - SG_LOG("sg_fail_pipeline: pipeline must be in ALLOC state\n"); + } else { + _SG_ERROR(FAIL_PIPELINE_INVALID_STATE); } } _SG_TRACE_ARGS(fail_pipeline, pip_id); @@ -16128,15 +16435,13 @@ SOKOL_API_IMPL void sg_fail_pass(sg_pass pass_id) { if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { pass->slot.ctx_id = _sg.active_context.id; pass->slot.state = SG_RESOURCESTATE_FAILED; - } - else { - SG_LOG("sg_fail_pass: pass must be in ALLOC state\n"); + } else { + _SG_ERROR(FAIL_PASS_INVALID_STATE); } } _SG_TRACE_ARGS(fail_pass, pass_id); } -/*-- get resource state */ SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); @@ -16151,6 +16456,13 @@ SOKOL_API_IMPL sg_resource_state sg_query_image_state(sg_image img_id) { return res; } +SOKOL_API_IMPL sg_resource_state sg_query_sampler_state(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + sg_resource_state res = smp ? smp->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + SOKOL_API_IMPL sg_resource_state sg_query_shader_state(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); @@ -16172,7 +16484,6 @@ SOKOL_API_IMPL sg_resource_state sg_query_pass_state(sg_pass pass_id) { return res; } -/*-- allocate and initialize resource ----------------------------------------*/ SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { SOKOL_ASSERT(_sg.valid); SOKOL_ASSERT(desc); @@ -16184,10 +16495,6 @@ SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { _sg_init_buffer(buf, &desc_def); SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)); } - else { - SG_LOG("buffer pool exhausted!"); - _SG_TRACE_NOARGS(err_buffer_pool_exhausted); - } _SG_TRACE_ARGS(make_buffer, &desc_def, buf_id); return buf_id; } @@ -16203,14 +16510,25 @@ SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) { _sg_init_image(img, &desc_def); SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)); } - else { - SG_LOG("image pool exhausted!"); - _SG_TRACE_NOARGS(err_image_pool_exhausted); - } _SG_TRACE_ARGS(make_image, &desc_def, img_id); return img_id; } +SOKOL_API_IMPL sg_sampler sg_make_sampler(const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc); + sg_sampler smp_id = _sg_alloc_sampler(); + if (smp_id.id != SG_INVALID_ID) { + _sg_sampler_t* smp = _sg_sampler_at(&_sg.pools, smp_id.id); + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_sampler(smp, &desc_def); + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_sampler, &desc_def, smp_id); + return smp_id; +} + SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { SOKOL_ASSERT(_sg.valid); SOKOL_ASSERT(desc); @@ -16222,10 +16540,6 @@ SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { _sg_init_shader(shd, &desc_def); SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)); } - else { - SG_LOG("shader pool exhausted!"); - _SG_TRACE_NOARGS(err_shader_pool_exhausted); - } _SG_TRACE_ARGS(make_shader, &desc_def, shd_id); return shd_id; } @@ -16241,10 +16555,6 @@ SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { _sg_init_pipeline(pip, &desc_def); SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)); } - else { - SG_LOG("pipeline pool exhausted!"); - _SG_TRACE_NOARGS(err_pipeline_pool_exhausted); - } _SG_TRACE_ARGS(make_pipeline, &desc_def, pip_id); return pip_id; } @@ -16260,15 +16570,10 @@ SOKOL_API_IMPL sg_pass sg_make_pass(const sg_pass_desc* desc) { _sg_init_pass(pass, &desc_def); SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)); } - else { - SG_LOG("pass pool exhausted!"); - _SG_TRACE_NOARGS(err_pass_pool_exhausted); - } _SG_TRACE_ARGS(make_pass, &desc_def, pass_id); return pass_id; } -/*-- destroy resource --------------------------------------------------------*/ SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); _SG_TRACE_ARGS(destroy_buffer, buf_id); @@ -16301,6 +16606,22 @@ SOKOL_API_IMPL void sg_destroy_image(sg_image img_id) { } } +SOKOL_API_IMPL void sg_destroy_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_sampler, smp_id); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + SOKOL_API_IMPL void sg_destroy_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); _SG_TRACE_ARGS(destroy_shader, shd_id); @@ -16358,7 +16679,7 @@ SOKOL_API_IMPL void sg_begin_default_pass(const sg_pass_action* pass_action, int _sg.cur_pass.id = SG_INVALID_ID; _sg.pass_valid = true; _sg_begin_pass(0, &pa, width, height); - _SG_TRACE_ARGS(begin_default_pass, pass_action, 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) { @@ -16375,23 +16696,16 @@ SOKOL_API_IMPL void sg_begin_pass(sg_pass pass_id, const sg_pass_action* pass_ac _sg.pass_valid = true; sg_pass_action pa; _sg_resolve_default_pass_action(pass_action, &pa); - const _sg_image_t* img = _sg_pass_color_image(pass, 0); - SOKOL_ASSERT(img); - const int w = img->cmn.width; - const int h = img->cmn.height; - _sg_begin_pass(pass, &pa, w, h); - _SG_TRACE_ARGS(begin_pass, pass_id, pass_action); - } - else { + _sg_begin_pass(pass, &pa, pass->cmn.width, pass->cmn.height); + _SG_TRACE_ARGS(begin_pass, pass_id, &pa); + } else { _sg.pass_valid = false; - _SG_TRACE_NOARGS(err_pass_invalid); } } SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { SOKOL_ASSERT(_sg.valid); if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg_apply_viewport(x, y, width, height, origin_top_left); @@ -16405,7 +16719,6 @@ 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); if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg_apply_scissor_rect(x, y, width, height, origin_top_left); @@ -16421,11 +16734,9 @@ SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { _sg.bindings_valid = false; if (!_sg_validate_apply_pipeline(pip_id)) { _sg.next_draw_valid = false; - _SG_TRACE_NOARGS(err_draw_invalid); return; } if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg.cur_pipeline = pip_id; @@ -16443,7 +16754,6 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); if (!_sg_validate_apply_bindings(bindings)) { _sg.next_draw_valid = false; - _SG_TRACE_NOARGS(err_draw_invalid); return; } _sg.bindings_valid = true; @@ -16451,16 +16761,15 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); SOKOL_ASSERT(pip); - _sg_buffer_t* vbs[SG_MAX_SHADERSTAGE_BUFFERS] = { 0 }; + _sg_buffer_t* vbs[SG_MAX_VERTEX_BUFFERS] = { 0 }; int num_vbs = 0; - for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++, num_vbs++) { + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++, num_vbs++) { if (bindings->vertex_buffers[i].id) { vbs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); SOKOL_ASSERT(vbs[i]); _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vbs[i]->slot.state); _sg.next_draw_valid &= !vbs[i]->cmn.append_overflow; - } - else { + } else { break; } } @@ -16476,12 +16785,11 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; int num_vs_imgs = 0; for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_vs_imgs++) { - if (bindings->vs_images[i].id) { - vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); + if (bindings->vs.images[i].id) { + vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs.images[i].id); SOKOL_ASSERT(vs_imgs[i]); _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vs_imgs[i]->slot.state); - } - else { + } else { break; } } @@ -16489,24 +16797,45 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; int num_fs_imgs = 0; for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_fs_imgs++) { - if (bindings->fs_images[i].id) { - fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); + if (bindings->fs.images[i].id) { + fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); SOKOL_ASSERT(fs_imgs[i]); _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == fs_imgs[i]->slot.state); + } else { + break; + } + } + + _sg_sampler_t* vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = { 0 }; + int num_vs_smps = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, num_vs_smps++) { + if (bindings->vs.samplers[i].id) { + vs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[i].id); + SOKOL_ASSERT(vs_smps[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vs_smps[i]->slot.state); + } else { + break; } - else { + } + + _sg_sampler_t* fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = { 0 }; + int num_fs_smps = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, num_fs_smps++) { + if (bindings->fs.samplers[i].id) { + fs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[i].id); + SOKOL_ASSERT(fs_smps[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == fs_smps[i]->slot.state); + } else { break; } } + if (_sg.next_draw_valid) { const int* vb_offsets = bindings->vertex_buffer_offsets; int ib_offset = bindings->index_buffer_offset; - _sg_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + _sg_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); _SG_TRACE_ARGS(apply_bindings, bindings); } - else { - _SG_TRACE_NOARGS(err_draw_invalid); - } } SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) { @@ -16516,15 +16845,12 @@ SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const SOKOL_ASSERT(data && data->ptr && (data->size > 0)); if (!_sg_validate_apply_uniforms(stage, ub_index, data)) { _sg.next_draw_valid = false; - _SG_TRACE_NOARGS(err_draw_invalid); return; } if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } if (!_sg.next_draw_valid) { - _SG_TRACE_NOARGS(err_draw_invalid); return; } _sg_apply_uniforms(stage, ub_index, data); @@ -16538,26 +16864,22 @@ SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instance SOKOL_ASSERT(num_instances >= 0); #if defined(SOKOL_DEBUG) if (!_sg.bindings_valid) { - SG_LOG("attempting to draw without resource bindings"); + _SG_WARN(DRAW_WITHOUT_BINDINGS); } #endif if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } if (!_sg.next_draw_valid) { - _SG_TRACE_NOARGS(err_draw_invalid); return; } if (!_sg.bindings_valid) { - _SG_TRACE_NOARGS(err_bindings_invalid); 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) */ if ((0 == num_elements) || (0 == num_instances)) { - _SG_TRACE_NOARGS(err_draw_invalid); return; } _sg_draw(base_element, num_elements, num_instances); @@ -16567,7 +16889,6 @@ 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); if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg_end_pass(); @@ -16598,9 +16919,9 @@ SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const sg_range* data) { if ((data->size > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { if (_sg_validate_update_buffer(buf, data)) { SOKOL_ASSERT(data->size <= (size_t)buf->cmn.size); - /* only one update allowed per buffer and frame */ + // only one update allowed per buffer and frame SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); - /* update and append on same buffer in same frame not allowed */ + // update and append on same buffer in same frame not allowed SOKOL_ASSERT(buf->cmn.append_frame_index != _sg.frame_index); _sg_update_buffer(buf, data); buf->cmn.update_frame_index = _sg.frame_index; @@ -16615,7 +16936,7 @@ SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); int result; if (buf) { - /* rewind append cursor in a new frame */ + // rewind append cursor in a new frame if (buf->cmn.append_frame_index != _sg.frame_index) { buf->cmn.append_pos = 0; buf->cmn.append_overflow = false; @@ -16627,7 +16948,7 @@ SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { if (buf->slot.state == SG_RESOURCESTATE_VALID) { if (_sg_validate_append_buffer(buf, data)) { if (!buf->cmn.append_overflow && (data->size > 0)) { - /* update and append on same buffer in same frame not allowed */ + // update and append on same buffer in same frame not allowed SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); int copied_num_bytes = _sg_append_buffer(buf, data, buf->cmn.append_frame_index != _sg.frame_index); buf->cmn.append_pos += copied_num_bytes; @@ -16636,9 +16957,8 @@ SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { } } result = start_pos; - } - else { - /* FIXME: should we return -1 here? */ + } else { + // FIXME: should we return -1 here? result = 0; } _SG_TRACE_ARGS(append_buffer, buf_id, data, result); @@ -16658,7 +16978,7 @@ SOKOL_API_IMPL bool sg_query_buffer_will_overflow(sg_buffer buf_id, size_t size) bool result = false; if (buf) { int append_pos = buf->cmn.append_pos; - /* rewind append cursor in a new frame */ + // rewind append cursor in a new frame if (buf->cmn.append_frame_index != _sg.frame_index) { append_pos = 0; } @@ -16745,8 +17065,19 @@ SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { info.num_slots = img->cmn.num_slots; info.active_slot = img->cmn.active_slot; #endif - info.width = img->cmn.width; - info.height = img->cmn.height; + } + return info; +} + +SOKOL_API_IMPL sg_sampler_info sg_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_info info; + _sg_clear(&info, sizeof(info)); + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.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; } @@ -16790,6 +17121,143 @@ SOKOL_API_IMPL sg_pass_info sg_query_pass_info(sg_pass pass_id) { return info; } +SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + desc.size = (size_t)buf->cmn.size; + desc.type = buf->cmn.type; + desc.usage = buf->cmn.usage; + } + return desc; +} + +SOKOL_API_IMPL sg_image_desc sg_query_image_desc(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_image_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + desc.type = img->cmn.type; + desc.render_target = img->cmn.render_target; + desc.width = img->cmn.width; + desc.height = img->cmn.height; + desc.num_slices = img->cmn.num_slices; + desc.num_mipmaps = img->cmn.num_mipmaps; + desc.usage = img->cmn.usage; + desc.pixel_format = img->cmn.pixel_format; + desc.sample_count = img->cmn.sample_count; + } + return desc; +} + +SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_desc(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + desc.min_filter = smp->cmn.min_filter; + desc.mag_filter = smp->cmn.mag_filter; + desc.mipmap_filter = smp->cmn.mipmap_filter; + desc.wrap_u = smp->cmn.wrap_u; + desc.wrap_v = smp->cmn.wrap_v; + desc.wrap_w = smp->cmn.wrap_w; + desc.min_lod = smp->cmn.min_lod; + desc.max_lod = smp->cmn.max_lod; + desc.border_color = smp->cmn.border_color; + desc.compare = smp->cmn.compare; + desc.max_anisotropy = smp->cmn.max_anisotropy; + } + return desc; +} + +SOKOL_API_IMPL sg_shader_desc sg_query_shader_desc(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_shader_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + for (int stage_idx = 0; stage_idx < SG_NUM_SHADER_STAGES; stage_idx++) { + sg_shader_stage_desc* stage_desc = (stage_idx == 0) ? &desc.vs : &desc.fs; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_idx]; + for (int ub_idx = 0; ub_idx < stage->num_uniform_blocks; ub_idx++) { + sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_idx]; + const _sg_shader_uniform_block_t* ub = &stage->uniform_blocks[ub_idx]; + ub_desc->size = ub->size; + } + for (int img_idx = 0; img_idx < stage->num_images; img_idx++) { + sg_shader_image_desc* img_desc = &stage_desc->images[img_idx]; + const _sg_shader_image_t* img = &stage->images[img_idx]; + img_desc->used = true; + img_desc->image_type = img->image_type; + img_desc->sample_type = img->sample_type; + img_desc->multisampled = img->multisampled; + } + for (int smp_idx = 0; smp_idx < stage->num_samplers; smp_idx++) { + sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_idx]; + const _sg_shader_sampler_t* smp = &stage->samplers[smp_idx]; + smp_desc->used = true; + smp_desc->sampler_type = smp->sampler_type; + } + for (int img_smp_idx = 0; img_smp_idx < stage->num_image_samplers; img_smp_idx++) { + sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_idx]; + const _sg_shader_image_sampler_t* img_smp = &stage->image_samplers[img_smp_idx]; + img_smp_desc->used = true; + img_smp_desc->image_slot = img_smp->image_slot; + img_smp_desc->sampler_slot = img_smp->sampler_slot; + img_smp_desc->glsl_name = 0; + } + } + } + return desc; +} + +SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + desc.shader = pip->cmn.shader_id; + desc.layout = pip->cmn.layout; + desc.depth = pip->cmn.depth; + desc.stencil = pip->cmn.stencil; + desc.color_count = pip->cmn.color_count; + for (int i = 0; i < pip->cmn.color_count; i++) { + desc.colors[i] = pip->cmn.colors[i]; + } + desc.primitive_type = pip->cmn.primitive_type; + desc.index_type = pip->cmn.index_type; + desc.cull_mode = pip->cmn.cull_mode; + desc.face_winding = pip->cmn.face_winding; + desc.sample_count = pip->cmn.sample_count; + desc.blend_color = pip->cmn.blend_color; + desc.alpha_to_coverage_enabled = pip->cmn.alpha_to_coverage_enabled; + } + return desc; +} + +SOKOL_API_IMPL sg_pass_desc sg_query_pass_desc(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + sg_pass_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; + } + 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; + } + return desc; +} + SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) { SOKOL_ASSERT(_sg.valid && desc); return _sg_buffer_desc_defaults(desc); @@ -16800,6 +17268,11 @@ SOKOL_API_IMPL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) return _sg_image_desc_defaults(desc); } +SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_sampler_desc_defaults(desc); +} + SOKOL_API_IMPL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) { SOKOL_ASSERT(_sg.valid && desc); return _sg_shader_desc_defaults(desc); @@ -16827,8 +17300,7 @@ 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 { + } else { return 0; } #else @@ -16840,8 +17312,7 @@ SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { #if defined(SOKOL_METAL) if (nil != _sg.mtl.cmd_encoder) { return (__bridge const void*) _sg.mtl.cmd_encoder; - } - else { + } else { return 0; } #else @@ -16853,4 +17324,4 @@ SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { #pragma warning(pop) #endif -#endif /* SOKOL_GFX_IMPL */ +#endif // SOKOL_GFX_IMPL diff --git a/thirdparty/sokol_glue.h b/thirdparty/sokol_glue.h index 74ecb7c..7216f6a 100644 --- a/thirdparty/sokol_glue.h +++ b/thirdparty/sokol_glue.h @@ -118,7 +118,6 @@ SOKOL_API_IMPL sg_context_desc sapp_sgcontext(void) { desc.color_format = (sg_pixel_format) sapp_color_format(); desc.depth_format = (sg_pixel_format) sapp_depth_format(); desc.sample_count = sapp_sample_count(); - desc.gl.force_gles2 = sapp_gles2(); desc.metal.device = sapp_metal_get_device(); desc.metal.renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor; desc.metal.drawable_cb = sapp_metal_get_drawable; diff --git a/threedee.glsl b/threedee.glsl index 6f6c2e7..95947f4 100644 --- a/threedee.glsl +++ b/threedee.glsl @@ -82,7 +82,8 @@ uniform skeleton_vs_params { vec2 bones_tex_size; }; -uniform sampler2D bones_tex; +uniform texture2D bones_tex; +uniform sampler vs_skeleton_smp; float decode_normalized_float32(vec4 v) { @@ -110,7 +111,7 @@ void main() { { for(int col = 0; col < 4; col++) { - bone_mat[col][row] = decode_normalized_float32(texture(bones_tex, vec2((0.5 + col*4 + row)/bones_tex_size.x, y_coord))); + bone_mat[col][row] = decode_normalized_float32(texture(sampler2D(bones_tex, vs_skeleton_smp), vec2((0.5 + col*4 + row)/bones_tex_size.x, y_coord))); } } @@ -172,8 +173,10 @@ void main() { @fs fs -uniform sampler2D tex; -uniform sampler2D shadow_map; +uniform texture2D tex; +uniform texture2D shadow_map; +uniform sampler fs_smp; +uniform samplerShadow fs_shadow_smp; uniform fs_params { int shadow_map_dimension; @@ -193,7 +196,7 @@ float decodeDepth(vec4 rgba) { return dot(rgba, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0)); } -float do_shadow_sample(sampler2D shadowMap, vec2 uv, float scene_depth, float n_dot_l) { +float do_shadow_sample(texture2D shadowMap, vec2 uv, float scene_depth, float n_dot_l) { { //WebGL does not support GL_CLAMP_TO_BORDER, or border colors at all it seems, so we have to check explicitly. //This will probably slow down other versions which do support texture borders, but the current system does @@ -201,7 +204,7 @@ float do_shadow_sample(sampler2D shadowMap, vec2 uv, float scene_depth, float n_ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) return 1.0; } - float map_depth = decodeDepth(texture(shadowMap, uv)); + float map_depth = decodeDepth(texture(sampler2D(shadowMap, fs_shadow_smp), uv)); // float bias = max(0.03f * (1.0f - n_dot_l), 0.005f); // bias = clamp(bias, 0.0, 0.01); @@ -216,7 +219,7 @@ float do_shadow_sample(sampler2D shadowMap, vec2 uv, float scene_depth, float n_ } -float bilinear_shadow_sample(sampler2D shadowMap, vec2 uv, int texture_width, int texture_height, float scene_depth_light_space, float n_dot_l) { +float bilinear_shadow_sample(texture2D shadowMap, vec2 uv, int texture_width, int texture_height, float scene_depth_light_space, float n_dot_l) { vec2 texture_dim = vec2(float(texture_width), float(texture_height)); vec2 texel_dim = vec2(1.0 / float(texture_width ), 1.0 / float(texture_height)); @@ -244,7 +247,7 @@ float bilinear_shadow_sample(sampler2D shadowMap, vec2 uv, int texture_width, in return result; } -float calculate_shadow_factor(sampler2D shadowMap, vec4 light_space_fragment_position, float n_dot_l) { +float calculate_shadow_factor(texture2D shadowMap, vec4 light_space_fragment_position, float n_dot_l) { float shadow = 1.0; vec3 projected_coords = light_space_fragment_position.xyz / light_space_fragment_position.w; @@ -275,7 +278,7 @@ float calculate_shadow_factor(sampler2D shadowMap, vec4 light_space_fragment_pos } void main() { - vec4 col = texture(tex, uv); + vec4 col = texture(sampler2D(tex, fs_smp), uv); bool alpha_blend = bool(alpha_blend_int); @@ -323,7 +326,8 @@ void main() { @fs fs_shadow_mapping -uniform sampler2D tex; +uniform texture2D tex; +uniform sampler fs_shadow_mapping_smp; in vec3 pos; in vec2 uv; @@ -341,7 +345,7 @@ vec4 encodeDepth(float v) { } void main() { - vec4 col = texture(tex, uv); + vec4 col = texture(sampler2D(tex, fs_shadow_mapping_smp), uv); if(col.a < 0.5) { discard; @@ -366,7 +370,9 @@ void main() { @end @fs fs_twodee -uniform sampler2D twodee_tex; +uniform texture2D twodee_tex; +uniform sampler fs_twodee_smp; + uniform twodee_fs_params { vec4 tint; @@ -387,7 +393,7 @@ out vec4 frag_color; void main() { // clip space is from [-1,1] [left, right]of screen on X, and [-1,1] [bottom, top] of screen on Y if(pos.x < clip_ul.x || pos.x > clip_lr.x || pos.y < clip_lr.y || pos.y > clip_ul.y) discard; - frag_color = texture(twodee_tex, uv) * tint; + frag_color = texture(sampler2D(twodee_tex, fs_twodee_smp), uv) * tint; if(frag_color.a <= alpha_clip_threshold) { discard; @@ -397,7 +403,9 @@ void main() { @end @fs fs_twodee_outline -uniform sampler2D twodee_tex; +uniform texture2D twodee_tex; +uniform sampler fs_twodee_outline_smp; + uniform twodee_fs_params { vec4 tint; @@ -419,30 +427,41 @@ void main() { // clip space is from [-1,1] [left, right]of screen on X, and [-1,1] [bottom, top] of screen on Y if(pos.x < clip_ul.x || pos.x > clip_lr.x || pos.y < clip_lr.y || pos.y > clip_ul.y) discard; - float left = texture(twodee_tex, uv + vec2(-1, 0)/tex_size).a; - float right = texture(twodee_tex, uv + vec2(1, 0)/tex_size).a; - float up = texture(twodee_tex, uv + vec2(0, 1)/tex_size).a; - float down = texture(twodee_tex, uv + vec2(0, -1)/tex_size).a; - - if( - false - || left > 0.1 && right < 0.1 - || left < 0.1 && right > 0.1 - || up < 0.1 && down > 0.1 - || up > 0.1 && down < 0.1 - ) - { - frag_color = vec4(1.0); - } - else - { - frag_color = vec4(0.0); - } + // 5-tap tent filter: centre, left, right, up, down + float c = texture(sampler2D(twodee_tex, fs_twodee_outline_smp), uv).a; + float l = texture(sampler2D(twodee_tex, fs_twodee_outline_smp), uv + vec2(-1, 0)/tex_size).a; + float r = texture(sampler2D(twodee_tex, fs_twodee_outline_smp), uv + vec2(+1, 0)/tex_size).a; + float u = texture(sampler2D(twodee_tex, fs_twodee_outline_smp), uv + vec2(0, +1)/tex_size).a; + float d = texture(sampler2D(twodee_tex, fs_twodee_outline_smp), uv + vec2(0, -1)/tex_size).a; + + // if centre pixel is ~1, it is inside a shape. + // if centre pixel is ~0, it is outside a shape. + // if it is in the middle, it is near an MSAA-resolved edge. + // we parallel-compute the inside AND outside glows, and then lerp using c. + float lerp_t = c; + + // buffer is linear-space to be ACES-corrected later, but MSAA happens in gamma space; + // I want a gamma-space blend; so I cheaply do pow(x, 2) by computing x*x. + c *= c; l *= l; r *= r; u *= u; d *= d; + + float accum_o = (c + l + r + u + d); + float accum_i = 5 - (c + l + r + u + d); + accum_o = 0.3 * accum_o; + accum_i = 0.3 * accum_i; + accum_o = clamp(accum_o, 0, 1); + accum_i = clamp(accum_i, 0, 1); + accum_o = sqrt(accum_o); // cheap gamma-undo + accum_i = sqrt(accum_i); // cheap gamma-undo + + float accum = mix(accum_o, accum_i, lerp_t); + frag_color = vec4(1, 1, 1, accum); } @end @fs fs_twodee_color_correction -uniform sampler2D twodee_tex; +uniform texture2D twodee_tex; +uniform sampler fs_twodee_color_correction_smp; + uniform twodee_fs_params { vec4 tint; @@ -473,7 +492,7 @@ void main() { // clip space is from [-1,1] [left, right]of screen on X, and [-1,1] [bottom, top] of screen on Y if(pos.x < clip_ul.x || pos.x > clip_lr.x || pos.y < clip_lr.y || pos.y > clip_ul.y) discard; - vec4 col = texture(twodee_tex, uv); + vec4 col = texture(sampler2D(twodee_tex, fs_twodee_color_correction_smp), uv); col.rgb = acesFilm(col.rgb); @@ -484,7 +503,8 @@ void main() { @fs fs_outline -uniform sampler2D tex; +uniform texture2D tex; +uniform sampler fs_outline_smp; in vec3 pos; in vec2 uv; @@ -495,7 +515,7 @@ in vec4 world_space_frag_pos; out vec4 frag_color; void main() { - vec4 col = texture(tex, uv); + vec4 col = texture(sampler2D(tex, fs_outline_smp), uv); if(col.a < 0.5) { discard;