diff --git a/extensions/pl_draw_backend_ext.c b/extensions/pl_draw_backend_ext.c index b8269ce0..2633ef4a 100644 --- a/extensions/pl_draw_backend_ext.c +++ b/extensions/pl_draw_backend_ext.c @@ -706,13 +706,13 @@ pl__use_nearest_sampler(const plDrawList2D* ptDrawlist, const plDrawCommand* tCm void pl_use_nearest_sampler(plDrawLayer2D* ptLayer) { - gptDraw->add_callback(ptLayer, pl__use_nearest_sampler, NULL, 0); + gptDraw->add_2d_callback(ptLayer, pl__use_nearest_sampler, NULL, 0); } void pl_use_linear_sampler(plDrawLayer2D* ptLayer) { - gptDraw->add_callback(ptLayer, plDrawCallbackResetRenderState, NULL, 0); + gptDraw->add_2d_callback(ptLayer, plDrawCallbackResetRenderState, NULL, 0); } void @@ -837,24 +837,15 @@ pl_submit_2d_drawlist(plDrawList2D* ptDrawlist, plRenderEncoder* ptEncoder, floa gptGfx->bind_vertex_buffer(ptEncoder, ptBufferInfo->tVertexBuffer); gptGfx->bind_shader(ptEncoder, tCurrentShader); + // track if user shader callback has bound a custom shader (to prevent automatic shader switching) + bool bUserShaderActive = false; + const uint32_t uCmdCount = pl_sb_size(ptDrawlist->sbtDrawCommands); for(uint32_t i = 0u; i < uCmdCount; i++) { plDrawCommand cmd = ptDrawlist->sbtDrawCommands[i]; - if(cmd.bSdf && !bSdf) - { - gptGfx->bind_shader(ptEncoder, ptEntry->tSecondaryPipeline); - tCurrentShader = ptEntry->tSecondaryPipeline; - bSdf = true; - } - else if(!cmd.bSdf && bSdf) - { - gptGfx->bind_shader(ptEncoder, ptEntry->tRegularPipeline); - tCurrentShader = ptEntry->tRegularPipeline; - bSdf = false; - } - + // regular callback (state changes like sampler switching) if(cmd.tUserCallback != NULL) { if(cmd.tUserCallback == plDrawCallbackResetRenderState) @@ -863,13 +854,38 @@ pl_submit_2d_drawlist(plDrawList2D* ptDrawlist, plRenderEncoder* ptEncoder, floa gptGfx->bind_vertex_buffer(ptEncoder, ptBufferInfo->tVertexBuffer); gptGfx->bind_shader(ptEncoder, tCurrentShader); gptDrawBackendCtx->tCurrentSamplerBindGroup = gptDrawBackendCtx->tFontSamplerBindGroup; - + bUserShaderActive = false; } else + { cmd.tUserCallback(ptDrawlist, &cmd); + } + } + // user shader (binds custom shader, suppresses bSdf switching) + else if(cmd.ptUserShader != NULL) + { + gptGfx->bind_shader(ptEncoder, *cmd.ptUserShader); + tCurrentShader = *cmd.ptUserShader; + bUserShaderActive = true; } else { + // only switch shaders for actual draw commands when no user shader is active + if(!bUserShaderActive) + { + if(cmd.bSdf && !bSdf) + { + gptGfx->bind_shader(ptEncoder, ptEntry->tSecondaryPipeline); + tCurrentShader = ptEntry->tSecondaryPipeline; + bSdf = true; + } + else if(!cmd.bSdf && bSdf) + { + gptGfx->bind_shader(ptEncoder, ptEntry->tRegularPipeline); + tCurrentShader = ptEntry->tRegularPipeline; + bSdf = false; + } + } if(pl_rect_width(&cmd.tClip) == 0) { @@ -904,7 +920,10 @@ pl_submit_2d_drawlist(plDrawList2D* ptDrawlist, plRenderEncoder* ptEncoder, floa gptGfx->set_scissor_region(ptEncoder, &tScissor); } + // use font atlas texture as fallback when no texture specified plBindGroupHandle tTexture = {.uData = cmd.tTextureId}; + if(tTexture.uData == 0) + tTexture.uData = gptDraw->get_current_font_atlas()->tTexture; plBindGroupHandle atBindGroups[] = { gptDrawBackendCtx->tCurrentSamplerBindGroup, tTexture diff --git a/extensions/pl_draw_ext.c b/extensions/pl_draw_ext.c index e550a32a..0fac78f4 100644 --- a/extensions/pl_draw_ext.c +++ b/extensions/pl_draw_ext.c @@ -636,7 +636,7 @@ void pl_add_2d_callback(plDrawLayer2D* ptLayer, plDrawCallback tCallback, void* pUserData, uint32_t uUserDataSize) { - plDrawCommand tNewDrawCommand = + plDrawCommand tNewDrawCommand = { .tUserCallback = tCallback, .uUserCallbackDataSize = uUserDataSize, @@ -647,6 +647,23 @@ pl_add_2d_callback(plDrawLayer2D* ptLayer, plDrawCallback tCallback, void* pUser ptLayer->ptLastCommand = NULL; } +void +pl_set_2d_shader(plDrawLayer2D* ptLayer, plShaderHandle* ptShader) +{ + // create a shader-only command (no draw data, just shader switch) + // backend will bind this shader when processing the command stream + plDrawCommand tNewDrawCommand = + { + .ptUserShader = ptShader // NULL clears user shader, non-NULL activates it + }; + pl_sb_push(ptLayer->sbtCommandBuffer, tNewDrawCommand); + + // force next draw primitive to create a new command rather than + // merging with the previous one - ensures draws after shader switch + // get their own commands that will use the new shader + ptLayer->ptLastCommand = NULL; +} + static void pl_add_line(plDrawLayer2D* ptLayer, plVec2 p0, plVec2 p1, plDrawLineOptions tOptions) { @@ -3874,7 +3891,7 @@ pl_load_draw_ext(plApiRegistryI* ptApiRegistry, bool bReload) .get_clip_rect = pl_get_clip_rect, .add_line = pl_add_line, .add_lines = pl_add_lines, - .add_callback = pl_add_2d_callback, + .add_2d_callback = pl_add_2d_callback, .add_text = pl_add_text_ex, .add_text_clipped = pl_add_text_clipped_ex, .add_triangle = pl_add_triangle, @@ -3896,7 +3913,7 @@ pl_load_draw_ext(plApiRegistryI* ptApiRegistry, bool bReload) .add_image_quad_ex = pl_add_image_quad_ex, .add_bezier_quad = pl_add_bezier_quad, .add_bezier_cubic = pl_add_bezier_cubic, - .add_callback = pl_add_2d_callback + .set_2d_shader = pl_set_2d_shader }; pl_set_api(ptApiRegistry, plDrawI, &tApi); diff --git a/extensions/pl_draw_ext.h b/extensions/pl_draw_ext.h index c83916f6..531cd851 100644 --- a/extensions/pl_draw_ext.h +++ b/extensions/pl_draw_ext.h @@ -88,6 +88,8 @@ typedef struct _plFontConfig plFontConfig; // configuration for loading typedef struct _plFontChar plFontChar; // internal type typedef struct _plFontGlyph plFontGlyph; // internal type typedef struct _plFontCustomRect plFontCustomRect; // internal type +typedef struct _plRenderEncoder plRenderEncoder; // pl_graphics_ext.h +typedef union plShaderHandle plShaderHandle; // pl_graphics_ext.h // advanced callbacks (you probably shouldn't be using this, mostly for backends) typedef void (*plDrawCallback)(const plDrawList2D*, const plDrawCommand*); @@ -182,7 +184,8 @@ typedef struct _plDrawI const plRect* (*get_clip_rect) (plDrawList2D*); // advanced (you probably shouldn't be using this, mostly for backends) - void (*add_callback)(plDrawLayer2D*, plDrawCallback, void* userData, uint32_t userDataSize); // userDataSize not setup yet + void (*add_2d_callback)(plDrawLayer2D*, plDrawCallback, void* userData, uint32_t userDataSize); + void (*set_2d_shader)(plDrawLayer2D*, plShaderHandle*); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~3D~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -417,7 +420,8 @@ typedef struct _plDrawCommand plTextureID tTextureId; plRect tClip; bool bSdf; - plDrawCallback tUserCallback; + plDrawCallback tUserCallback; + plShaderHandle* ptUserShader; void* pUserCallbackData; uint32_t uUserCallbackDataSize; } plDrawCommand;