Attempt to implement DrawStencilShadowPass

This commit is contained in:
Robert Beckebans 2022-03-16 17:43:55 +01:00
parent 51294e6549
commit ccd5086d20
5 changed files with 185 additions and 305 deletions

View file

@ -755,6 +755,13 @@ void idRenderBackend::GetCurrentBindingLayout()
.addItem( nvrhi::BindingSetItem::Sampler( 0, ( nvrhi::ISampler* )GetImageAt( 0 )->GetSampler( samplerCache ) ) );
}
*/
else if( type == BINDING_LAYOUT_DRAW_SHADOW )
{
pendingBindingSetDescs[0].bindings =
{
nvrhi::BindingSetItem::ConstantBuffer( 0, renderProgManager.ConstantBuffer() )
};
}
else if( type == BINDING_LAYOUT_DRAW_INTERACTION )
{
pendingBindingSetDescs[0].bindings =
@ -1057,11 +1064,11 @@ void idRenderBackend::GL_Clear( bool color, bool depth, bool stencil, byte stenc
nvrhi::utils::ClearColorAttachment( commandList, globalFramebuffers.hdrFBO->GetApiObject(), 0, nvrhi::Color( 0.f ) );
}
if( depth )
if( depth || stencil )
{
nvrhi::ITexture* depthTexture = ( nvrhi::ITexture* )( globalImages->currentDepthImage->GetTextureID() );
const nvrhi::FormatInfo& depthFormatInfo = nvrhi::getFormatInfo( depthTexture->getDesc().format );
commandList->clearDepthStencilTexture( depthTexture, nvrhi::AllSubresources, true, 1.f, depthFormatInfo.hasStencil, 0 );
commandList->clearDepthStencilTexture( depthTexture, nvrhi::AllSubresources, depth, 1.f, depthFormatInfo.hasStencil, stencilValue );
}
}
@ -1318,6 +1325,169 @@ extern idCVar r_useStencilShadowPreload;
void idRenderBackend::DrawStencilShadowPass( const drawSurf_t* drawSurf, const bool renderZPass )
{
#if 0
if( renderZPass )
{
// Z-pass
uint64 stencil = GLS_STENCIL_OP_FAIL_KEEP | GLS_STENCIL_OP_ZFAIL_KEEP | GLS_STENCIL_OP_PASS_INCR
| GLS_BACK_STENCIL_OP_FAIL_KEEP | GLS_BACK_STENCIL_OP_ZFAIL_KEEP | GLS_BACK_STENCIL_OP_PASS_DECR;
GL_State( ( glStateBits & ~GLS_STENCIL_OP_BITS ) | stencil );
}
else if( r_useStencilShadowPreload.GetBool() )
{
// preload + Z-pass
uint64 stencil = GLS_STENCIL_OP_FAIL_KEEP | GLS_STENCIL_OP_ZFAIL_DECR | GLS_STENCIL_OP_PASS_DECR
| GLS_BACK_STENCIL_OP_FAIL_KEEP | GLS_BACK_STENCIL_OP_ZFAIL_INCR | GLS_BACK_STENCIL_OP_PASS_INCR;
GL_State( ( glStateBits & ~GLS_STENCIL_OP_BITS ) | stencil );
}
else
{
// Z-fail (Carmack's Reverse)
uint64 stencil = GLS_STENCIL_OP_FAIL_KEEP | GLS_STENCIL_OP_ZFAIL_DECR | GLS_STENCIL_OP_PASS_KEEP
| GLS_BACK_STENCIL_OP_FAIL_KEEP | GLS_BACK_STENCIL_OP_ZFAIL_INCR | GLS_BACK_STENCIL_OP_PASS_KEEP;
GL_State( ( glStateBits & ~GLS_STENCIL_OP_BITS ) | stencil );
}
// get vertex buffer
const vertCacheHandle_t vbHandle = drawSurf->shadowCache;
idVertexBuffer* vertexBuffer;
if( vertexCache.CacheIsStatic( vbHandle ) )
{
vertexBuffer = &vertexCache.staticData.vertexBuffer;
}
else
{
const uint64 frameNum = ( int )( vbHandle >> VERTCACHE_FRAME_SHIFT ) & VERTCACHE_FRAME_MASK;
if( frameNum != ( ( vertexCache.currentFrame - 1 ) & VERTCACHE_FRAME_MASK ) )
{
idLib::Warning( "DrawStencilShadowPass, vertexBuffer == NULL" );
return;
}
vertexBuffer = &vertexCache.frameData[vertexCache.drawListNum].vertexBuffer;
}
const uint vertOffset = ( uint )( vbHandle >> VERTCACHE_OFFSET_SHIFT ) & VERTCACHE_OFFSET_MASK;
bool changeState = false;
if( currentVertexOffset != vertOffset )
{
currentVertexOffset = vertOffset;
}
if( currentVertexBuffer != ( nvrhi::IBuffer* )vertexBuffer->GetAPIObject() || !r_useStateCaching.GetBool() )
{
currentVertexBuffer = vertexBuffer->GetAPIObject();
changeState = true;
}
// get index buffer
const vertCacheHandle_t ibHandle = drawSurf->indexCache;
idIndexBuffer* indexBuffer;
if( vertexCache.CacheIsStatic( ibHandle ) )
{
indexBuffer = &vertexCache.staticData.indexBuffer;
}
else
{
const uint64 frameNum = ( int )( ibHandle >> VERTCACHE_FRAME_SHIFT ) & VERTCACHE_FRAME_MASK;
if( frameNum != ( ( vertexCache.currentFrame - 1 ) & VERTCACHE_FRAME_MASK ) )
{
idLib::Warning( "DrawStencilShadowPass, indexBuffer == NULL" );
return;
}
indexBuffer = &vertexCache.frameData[vertexCache.drawListNum].indexBuffer;
}
const uint indexOffset = ( uint )( ibHandle >> VERTCACHE_OFFSET_SHIFT ) & VERTCACHE_OFFSET_MASK;
if( currentIndexOffset != indexOffset )
{
currentIndexOffset = indexOffset;
}
//RENDERLOG_PRINTF( "Binding Buffers: %p:%i %p:%i\n", vertexBuffer, vertOffset, indexBuffer, indexOffset );
if( currentIndexBuffer != ( nvrhi::IBuffer* )indexBuffer->GetAPIObject() || !r_useStateCaching.GetBool() )
{
currentIndexBuffer = indexBuffer->GetAPIObject();
changeState = true;
}
GetCurrentBindingLayout();
// RB: for debugging
int program = renderProgManager.CurrentProgram();
int bindingLayoutType = renderProgManager.BindingLayoutType();
auto& info = renderProgManager.GetProgramInfo( program );
for( int i = 0; i < info.bindingLayouts->Num(); i++ )
{
if( !currentBindingSets[i] || *currentBindingSets[i]->getDesc() != pendingBindingSetDescs[i] )
{
currentBindingSets[i] = bindingCache.GetOrCreateBindingSet( pendingBindingSetDescs[i], ( *info.bindingLayouts )[i] );
changeState = true;
}
}
renderProgManager.CommitConstantBuffer( commandList );
PipelineKey key{ glStateBits, program, depthBias, slopeScaleBias, currentFrameBuffer };
auto pipeline = pipelineCache.GetOrCreatePipeline( key );
if( currentPipeline != pipeline )
{
currentPipeline = pipeline;
changeState = true;
}
if( changeState )
{
nvrhi::GraphicsState state;
for( int i = 0; i < info.bindingLayouts->Num(); i++ )
{
state.bindings.push_back( currentBindingSets[i] );
}
state.indexBuffer = { currentIndexBuffer, nvrhi::Format::R16_UINT, 0 };
state.vertexBuffers = { { currentVertexBuffer, 0, 0 } };
state.pipeline = pipeline;
state.framebuffer = currentFrameBuffer->GetApiObject();
nvrhi::Viewport viewport{ ( float )currentViewport.x1,
( float )currentViewport.x2,
( float )currentViewport.y1,
( float )currentViewport.y2,
currentViewport.zmin,
currentViewport.zmax };
state.viewport.addViewportAndScissorRect( viewport );
if( !currentScissor.IsEmpty() )
{
state.viewport.addScissorRect( nvrhi::Rect( currentScissor.x1, currentScissor.x2, currentScissor.y1, currentScissor.y2 ) );
}
commandList->setGraphicsState( state );
}
nvrhi::DrawArguments args;
if( drawSurf->jointCache )
{
args.startVertexLocation = currentVertexOffset / sizeof( idShadowVertSkinned );
}
else
{
args.startVertexLocation = currentVertexOffset / sizeof( idShadowVert );
}
args.startIndexLocation = currentIndexOffset / sizeof( uint16 );
args.vertexCount = drawSurf->numIndexes;
commandList->drawIndexed( args );
pc.c_drawElements++;
pc.c_drawIndexes += drawSurf->numIndexes;
#endif
}

View file

@ -309,7 +309,7 @@ void PipelineCache::GetRenderState( uint64 stateBits, PipelineKey key, nvrhi::Re
mask = mask & ~nvrhi::ColorMask::Alpha;
}
renderTarget.enableBlend();
//renderTarget.enableBlend();
renderTarget.setColorWriteMask( mask );
}

View file

@ -5,7 +5,7 @@ Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2014 Carl Kenner
Copyright (C) 2016-2017 Dustin Land
Copyright (C) 2013-2021 Robert Beckebans
Copyright (C) 2013-2022 Robert Beckebans
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -92,305 +92,6 @@ static ID_INLINE void SetFragmentParm( renderParm_t rp, const float* value )
renderProgManager.SetUniformValue( rp, value );
}
/*
====================
PrintState
====================
*/
#if 0
void PrintState( uint64 stateBits, uint64* stencilBits )
{
if( renderLog.Active() == 0 )
{
return;
}
renderLog.OpenBlock( "GL_State" );
// culling
renderLog.Printf( "Culling: " );
switch( stateBits & GLS_CULL_BITS )
{
case GLS_CULL_FRONTSIDED:
renderLog.Printf_NoIndent( "FRONTSIDED -> BACK" );
break;
case GLS_CULL_BACKSIDED:
renderLog.Printf_NoIndent( "BACKSIDED -> FRONT" );
break;
case GLS_CULL_TWOSIDED:
renderLog.Printf_NoIndent( "TWOSIDED" );
break;
default:
renderLog.Printf_NoIndent( "NA" );
break;
}
renderLog.Printf_NoIndent( "\n" );
// polygon mode
renderLog.Printf( "PolygonMode: %s\n", ( stateBits & GLS_POLYMODE_LINE ) ? "LINE" : "FILL" );
// color mask
renderLog.Printf( "ColorMask: " );
renderLog.Printf_NoIndent( ( stateBits & GLS_REDMASK ) ? "_" : "R" );
renderLog.Printf_NoIndent( ( stateBits & GLS_GREENMASK ) ? "_" : "G" );
renderLog.Printf_NoIndent( ( stateBits & GLS_BLUEMASK ) ? "_" : "B" );
renderLog.Printf_NoIndent( ( stateBits & GLS_ALPHAMASK ) ? "_" : "A" );
renderLog.Printf_NoIndent( "\n" );
// blend
renderLog.Printf( "Blend: src=" );
switch( stateBits & GLS_SRCBLEND_BITS )
{
case GLS_SRCBLEND_ZERO:
renderLog.Printf_NoIndent( "ZERO" );
break;
case GLS_SRCBLEND_ONE:
renderLog.Printf_NoIndent( "ONE" );
break;
case GLS_SRCBLEND_DST_COLOR:
renderLog.Printf_NoIndent( "DST_COLOR" );
break;
case GLS_SRCBLEND_ONE_MINUS_DST_COLOR:
renderLog.Printf_NoIndent( "ONE_MINUS_DST_COLOR" );
break;
case GLS_SRCBLEND_SRC_ALPHA:
renderLog.Printf_NoIndent( "SRC_ALPHA" );
break;
case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA:
renderLog.Printf_NoIndent( "ONE_MINUS_SRC_ALPHA" );
break;
case GLS_SRCBLEND_DST_ALPHA:
renderLog.Printf_NoIndent( "DST_ALPHA" );
break;
case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA:
renderLog.Printf_NoIndent( "ONE_MINUS_DST_ALPHA" );
break;
default:
renderLog.Printf_NoIndent( "NA" );
break;
}
renderLog.Printf_NoIndent( ", dst=" );
switch( stateBits & GLS_DSTBLEND_BITS )
{
case GLS_DSTBLEND_ZERO:
renderLog.Printf_NoIndent( "ZERO" );
break;
case GLS_DSTBLEND_ONE:
renderLog.Printf_NoIndent( "ONE" );
break;
case GLS_DSTBLEND_SRC_COLOR:
renderLog.Printf_NoIndent( "SRC_COLOR" );
break;
case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR:
renderLog.Printf_NoIndent( "ONE_MINUS_SRC_COLOR" );
break;
case GLS_DSTBLEND_SRC_ALPHA:
renderLog.Printf_NoIndent( "SRC_ALPHA" );
break;
case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA:
renderLog.Printf_NoIndent( "ONE_MINUS_SRC_ALPHA" );
break;
case GLS_DSTBLEND_DST_ALPHA:
renderLog.Printf_NoIndent( "DST_ALPHA" );
break;
case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA:
renderLog.Printf_NoIndent( "ONE_MINUS_DST_ALPHA" );
break;
default:
renderLog.Printf_NoIndent( "NA" );
}
renderLog.Printf_NoIndent( "\n" );
// depth func
renderLog.Printf( "DepthFunc: " );
switch( stateBits & GLS_DEPTHFUNC_BITS )
{
case GLS_DEPTHFUNC_EQUAL:
renderLog.Printf_NoIndent( "EQUAL" );
break;
case GLS_DEPTHFUNC_ALWAYS:
renderLog.Printf_NoIndent( "ALWAYS" );
break;
case GLS_DEPTHFUNC_LESS:
renderLog.Printf_NoIndent( "LEQUAL" );
break;
case GLS_DEPTHFUNC_GREATER:
renderLog.Printf_NoIndent( "GEQUAL" );
break;
default:
renderLog.Printf_NoIndent( "NA" );
break;
}
renderLog.Printf_NoIndent( "\n" );
// depth mask
renderLog.Printf( "DepthWrite: %s\n", ( stateBits & GLS_DEPTHMASK ) ? "FALSE" : "TRUE" );
renderLog.Printf( "DepthBounds: %s\n", ( stateBits & GLS_DEPTH_TEST_MASK ) ? "TRUE" : "FALSE" );
// depth bias
renderLog.Printf( "DepthBias: %s\n", ( stateBits & GLS_POLYGON_OFFSET ) ? "TRUE" : "FALSE" );
// stencil
auto printStencil = [&]( stencilFace_t face, uint64 bits, uint64 mask, uint64 ref )
{
renderLog.Printf( "Stencil: %s, ", ( bits & ( GLS_STENCIL_FUNC_BITS | GLS_STENCIL_OP_BITS ) ) ? "ON" : "OFF" );
renderLog.Printf_NoIndent( "Face=" );
switch( face )
{
case STENCIL_FACE_FRONT:
renderLog.Printf_NoIndent( "FRONT" );
break;
case STENCIL_FACE_BACK:
renderLog.Printf_NoIndent( "BACK" );
break;
default:
renderLog.Printf_NoIndent( "BOTH" );
break;
}
renderLog.Printf_NoIndent( ", Func=" );
switch( bits & GLS_STENCIL_FUNC_BITS )
{
case GLS_STENCIL_FUNC_NEVER:
renderLog.Printf_NoIndent( "NEVER" );
break;
case GLS_STENCIL_FUNC_LESS:
renderLog.Printf_NoIndent( "LESS" );
break;
case GLS_STENCIL_FUNC_EQUAL:
renderLog.Printf_NoIndent( "EQUAL" );
break;
case GLS_STENCIL_FUNC_LEQUAL:
renderLog.Printf_NoIndent( "LEQUAL" );
break;
case GLS_STENCIL_FUNC_GREATER:
renderLog.Printf_NoIndent( "GREATER" );
break;
case GLS_STENCIL_FUNC_NOTEQUAL:
renderLog.Printf_NoIndent( "NOTEQUAL" );
break;
case GLS_STENCIL_FUNC_GEQUAL:
renderLog.Printf_NoIndent( "GEQUAL" );
break;
case GLS_STENCIL_FUNC_ALWAYS:
renderLog.Printf_NoIndent( "ALWAYS" );
break;
default:
renderLog.Printf_NoIndent( "NA" );
break;
}
renderLog.Printf_NoIndent( ", OpFail=" );
switch( bits & GLS_STENCIL_OP_FAIL_BITS )
{
case GLS_STENCIL_OP_FAIL_KEEP:
renderLog.Printf_NoIndent( "KEEP" );
break;
case GLS_STENCIL_OP_FAIL_ZERO:
renderLog.Printf_NoIndent( "ZERO" );
break;
case GLS_STENCIL_OP_FAIL_REPLACE:
renderLog.Printf_NoIndent( "REPLACE" );
break;
case GLS_STENCIL_OP_FAIL_INCR:
renderLog.Printf_NoIndent( "INCR" );
break;
case GLS_STENCIL_OP_FAIL_DECR:
renderLog.Printf_NoIndent( "DECR" );
break;
case GLS_STENCIL_OP_FAIL_INVERT:
renderLog.Printf_NoIndent( "INVERT" );
break;
case GLS_STENCIL_OP_FAIL_INCR_WRAP:
renderLog.Printf_NoIndent( "INCR_WRAP" );
break;
case GLS_STENCIL_OP_FAIL_DECR_WRAP:
renderLog.Printf_NoIndent( "DECR_WRAP" );
break;
default:
renderLog.Printf_NoIndent( "NA" );
break;
}
renderLog.Printf_NoIndent( ", ZFail=" );
switch( bits & GLS_STENCIL_OP_ZFAIL_BITS )
{
case GLS_STENCIL_OP_ZFAIL_KEEP:
renderLog.Printf_NoIndent( "KEEP" );
break;
case GLS_STENCIL_OP_ZFAIL_ZERO:
renderLog.Printf_NoIndent( "ZERO" );
break;
case GLS_STENCIL_OP_ZFAIL_REPLACE:
renderLog.Printf_NoIndent( "REPLACE" );
break;
case GLS_STENCIL_OP_ZFAIL_INCR:
renderLog.Printf_NoIndent( "INCR" );
break;
case GLS_STENCIL_OP_ZFAIL_DECR:
renderLog.Printf_NoIndent( "DECR" );
break;
case GLS_STENCIL_OP_ZFAIL_INVERT:
renderLog.Printf_NoIndent( "INVERT" );
break;
case GLS_STENCIL_OP_ZFAIL_INCR_WRAP:
renderLog.Printf_NoIndent( "INCR_WRAP" );
break;
case GLS_STENCIL_OP_ZFAIL_DECR_WRAP:
renderLog.Printf_NoIndent( "DECR_WRAP" );
break;
default:
renderLog.Printf_NoIndent( "NA" );
break;
}
renderLog.Printf_NoIndent( ", OpPass=" );
switch( bits & GLS_STENCIL_OP_PASS_BITS )
{
case GLS_STENCIL_OP_PASS_KEEP:
renderLog.Printf_NoIndent( "KEEP" );
break;
case GLS_STENCIL_OP_PASS_ZERO:
renderLog.Printf_NoIndent( "ZERO" );
break;
case GLS_STENCIL_OP_PASS_REPLACE:
renderLog.Printf_NoIndent( "REPLACE" );
break;
case GLS_STENCIL_OP_PASS_INCR:
renderLog.Printf_NoIndent( "INCR" );
break;
case GLS_STENCIL_OP_PASS_DECR:
renderLog.Printf_NoIndent( "DECR" );
break;
case GLS_STENCIL_OP_PASS_INVERT:
renderLog.Printf_NoIndent( "INVERT" );
break;
case GLS_STENCIL_OP_PASS_INCR_WRAP:
renderLog.Printf_NoIndent( "INCR_WRAP" );
break;
case GLS_STENCIL_OP_PASS_DECR_WRAP:
renderLog.Printf_NoIndent( "DECR_WRAP" );
break;
default:
renderLog.Printf_NoIndent( "NA" );
break;
}
renderLog.Printf_NoIndent( ", mask=%llu, ref=%llu\n", mask, ref );
};
uint32 mask = uint32( ( stateBits & GLS_STENCIL_FUNC_MASK_BITS ) >> GLS_STENCIL_FUNC_MASK_SHIFT );
uint32 ref = uint32( ( stateBits & GLS_STENCIL_FUNC_REF_BITS ) >> GLS_STENCIL_FUNC_REF_SHIFT );
if( stateBits & GLS_SEPARATE_STENCIL )
{
printStencil( STENCIL_FACE_FRONT, stencilBits[ 0 ], mask, ref );
printStencil( STENCIL_FACE_BACK, stencilBits[ 1 ], mask, ref );
}
else
{
printStencil( STENCIL_FACE_NUM, stateBits, mask, ref );
}
renderLog.CloseBlock();
}
#endif
/*
================
RB_SetMVP

View file

@ -844,6 +844,7 @@ enum bindingLayoutType_t
BINDING_LAYOUT_BLIT,
BINDING_LAYOUT_DRAW_AO,
BINDING_LAYOUT_DRAW_AO1,
BINDING_LAYOUT_DRAW_SHADOW,
BINDING_LAYOUT_DRAW_INTERACTION,
BINDING_LAYOUT_DRAW_INTERACTION_SM,
BINDING_LAYOUT_DRAW_FOG,

View file

@ -226,6 +226,14 @@ void idRenderProgManager::Init( nvrhi::IDevice* _device )
bindingLayouts[BINDING_LAYOUT_DRAW_AO1] = { device->createBindingLayout( aoLayoutDesc2 ), samplerOneBindingLayout };
nvrhi::BindingLayoutDesc shadowLayout;
shadowLayout.visibility = nvrhi::ShaderType::All;
shadowLayout.bindings =
{
nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 )
};
bindingLayouts[BINDING_LAYOUT_DRAW_SHADOW] = { device->createBindingLayout( shadowLayout ) };
auto interactionBindingLayout = nvrhi::BindingLayoutDesc()
.setVisibility( nvrhi::ShaderType::All )
.addItem( nvrhi::BindingLayoutItem::VolatileConstantBuffer( 0 ) )
@ -402,8 +410,8 @@ void idRenderProgManager::Init( nvrhi::IDevice* _device )
{ BUILTIN_DEPTH, "builtin/depth", "", { {"USE_GPU_SKINNING", "0" } }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT },
{ BUILTIN_DEPTH_SKINNED, "builtin/depth", "_skinned", { {"USE_GPU_SKINNING", "1" } }, true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT },
{ BUILTIN_SHADOW, "builtin/lighting/shadow", "", { {"USE_GPU_SKINNING", "0" } }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_SHADOW_VERT, BINDING_LAYOUT_DEFAULT },
{ BUILTIN_SHADOW_SKINNED, "builtin/lighting/shadow", "_skinned", { {"USE_GPU_SKINNING", "1" } }, true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_SHADOW_VERT_SKINNED, BINDING_LAYOUT_DEFAULT },
{ BUILTIN_SHADOW, "builtin/lighting/shadow", "", { {"USE_GPU_SKINNING", "0" } }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_SHADOW_VERT, BINDING_LAYOUT_DRAW_SHADOW },
{ BUILTIN_SHADOW_SKINNED, "builtin/lighting/shadow", "_skinned", { {"USE_GPU_SKINNING", "1" } }, true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_SHADOW_VERT_SKINNED, BINDING_LAYOUT_DRAW_SHADOW },
{ BUILTIN_SHADOW_DEBUG, "builtin/debug/shadowDebug", "", { {"USE_GPU_SKINNING", "0" } }, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT },
{ BUILTIN_SHADOW_DEBUG_SKINNED, "builtin/debug/shadowDebug", "_skinned", { {"USE_GPU_SKINNING", "1" } }, true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT },