From f0657a03b58bd799263d8ba1e3ac76333c64b1e1 Mon Sep 17 00:00:00 2001 From: Aleksandr Tretyakov Date: Mon, 20 Nov 2023 22:17:32 +0500 Subject: [PATCH] Describe light culling shader resources. --- ext/combined-shader-language-parser | 2 +- .../light_culling/CalculateGridFrustums.comp | 2 +- .../final/light_culling/LightCulling.comp | 19 +- .../CalculateGridFrustums.comp.hlsl | 2 +- .../hlsl/light_culling/LightCulling.comp.hlsl | 19 +- .../include/light_culling/LightCulling.glsl | 318 +++++++++++++++++- .../render/directx/DirectXRenderer.cpp | 7 + .../private/render/vulkan/VulkanRenderer.cpp | 58 ++-- .../general/EngineShaderConstantMacros.hpp | 12 + .../LightingShaderResourceManager.cpp | 25 +- .../resources/LightingShaderResourceManager.h | 30 +- .../glsl/DescriptorSetLayoutGenerator.cpp | 35 +- .../glsl/DescriptorSetLayoutGenerator.h | 11 +- .../private/shader/glsl/GlslEngineShaders.hpp | 10 +- .../private/shader/hlsl/HlslEngineShaders.hpp | 10 +- 15 files changed, 476 insertions(+), 84 deletions(-) diff --git a/ext/combined-shader-language-parser b/ext/combined-shader-language-parser index 49e22203a..17a4ac848 160000 --- a/ext/combined-shader-language-parser +++ b/ext/combined-shader-language-parser @@ -1 +1 @@ -Subproject commit 49e22203ac465396f665791a3e1b0a2ab47e7d7e +Subproject commit 17a4ac848d44c60c587e5415babbe235bcea6bb7 diff --git a/res/engine/shaders/glsl/final/light_culling/CalculateGridFrustums.comp b/res/engine/shaders/glsl/final/light_culling/CalculateGridFrustums.comp index bc621bc1f..a83a9d869 100644 --- a/res/engine/shaders/glsl/final/light_culling/CalculateGridFrustums.comp +++ b/res/engine/shaders/glsl/final/light_culling/CalculateGridFrustums.comp @@ -3,7 +3,7 @@ #include "../../../include/light_culling/CalculateGridTileFrustum.glsl" /** - * Defines how much threads should be executed in the X and the Y dimensions of a grid tile. + * Defines how much threads should be executed in the X and the Y dimensions. * This macro also defines how much pixels there are in one grid tile. */ #ifndef THREADS_IN_GROUP_XY diff --git a/res/engine/shaders/glsl/final/light_culling/LightCulling.comp b/res/engine/shaders/glsl/final/light_culling/LightCulling.comp index 96943ab0f..4a2d61054 100644 --- a/res/engine/shaders/glsl/final/light_culling/LightCulling.comp +++ b/res/engine/shaders/glsl/final/light_culling/LightCulling.comp @@ -1,7 +1,11 @@ #version 450 -#include "../../../include/light_culling/CalculateGridTileFrustum.glsl" +#include "../../../include/light_culling/LightCulling.glsl" +/** + * Defines how much threads should be executed in the X and the Y dimensions. + * This macro also defines how much pixels there are in one grid tile. + */ #ifndef THREADS_IN_GROUP_XY FAIL; #endif @@ -12,6 +16,15 @@ void main(){ // - Presentation "DirectX 11 Rendering in Battlefield 3" (2011) by Johan Andersson, DICE. // - "Forward+: A Step Toward Film-Style Shading in Real Time", Takahiro Harada (2012). - // Get depth. - float depth = texelFetch(depthTexture, gl_GlobalInvocationID.xy, 0).r; + // Get depth of this pixel. + float depth = texelFetch(depthTexture, ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y), 0).r; + + if (gl_LocalInvocationIndex == 0){ + // Only one thread in the group should initialize group shared variables. + initializeGroupSharedVariables(gl_WorkGroupID.x, gl_WorkGroupID.y); + } + + // Make sure all group shared writes were finished and all threads from the group reached this line. + groupMemoryBarrier(); // wait for shared writes to finish + barrier(); // wait for threads of group to reach this line } \ No newline at end of file diff --git a/res/engine/shaders/hlsl/light_culling/CalculateGridFrustums.comp.hlsl b/res/engine/shaders/hlsl/light_culling/CalculateGridFrustums.comp.hlsl index 17a405495..857dacd2f 100644 --- a/res/engine/shaders/hlsl/light_culling/CalculateGridFrustums.comp.hlsl +++ b/res/engine/shaders/hlsl/light_culling/CalculateGridFrustums.comp.hlsl @@ -1,7 +1,7 @@ #include "../../include/light_culling/CalculateGridTileFrustum.glsl" /** - * Defines how much threads should be executed in the X and the Y dimensions of a grid tile. + * Defines how much threads should be executed in the X and the Y dimensions. * This macro also defines how much pixels there are in one grid tile. */ #ifndef THREADS_IN_GROUP_XY diff --git a/res/engine/shaders/hlsl/light_culling/LightCulling.comp.hlsl b/res/engine/shaders/hlsl/light_culling/LightCulling.comp.hlsl index a6c35fa99..baa29c66a 100644 --- a/res/engine/shaders/hlsl/light_culling/LightCulling.comp.hlsl +++ b/res/engine/shaders/hlsl/light_culling/LightCulling.comp.hlsl @@ -1,15 +1,28 @@ #include "../../include/light_culling/LightCulling.glsl" +/** + * Defines how much threads should be executed in the X and the Y dimensions. + * This macro also defines how much pixels there are in one grid tile. + */ #ifndef THREADS_IN_GROUP_XY _Static_assert(false, "thread count in group - macro not defined"); #endif +/** 1 thread per pixel in a tile. 1 thread group per tile. */ [numthreads(THREADS_IN_GROUP_XY, THREADS_IN_GROUP_XY, 1 )] -void csLightCulling(uint3 dispatchThreadID : SV_DispatchThreadID){ +void csLightCulling(uint3 threadIdInDispatch : SV_DispatchThreadID, uint threadIdInGroup : SV_GroupIndex, uint3 groupIdInDispatch : SV_GroupID){ // Sources: // - Presentation "DirectX 11 Rendering in Battlefield 3" (2011) by Johan Andersson, DICE. // - "Forward+: A Step Toward Film-Style Shading in Real Time", Takahiro Harada (2012). - // Get depth. - float depth = depthTexture.Load(int3(dispatchThreadID.xy, 0)).r; + // Get depth of this pixel. + float depth = depthTexture.Load(int3(threadIdInDispatch.xy, 0)).r; + + if (threadIdInGroup == 0){ + // Only one thread in the group should initialize group shared variables. + initializeGroupSharedVariables(groupIdInDispatch.x, groupIdInDispatch.y); + } + + // Make sure all group shared writes were finished and all threads from the group reached this line. + GroupMemoryBarrierWithGroupSync(); } \ No newline at end of file diff --git a/res/engine/shaders/include/light_culling/LightCulling.glsl b/res/engine/shaders/include/light_culling/LightCulling.glsl index 289448c7c..955df4cbb 100644 --- a/res/engine/shaders/include/light_culling/LightCulling.glsl +++ b/res/engine/shaders/include/light_culling/LightCulling.glsl @@ -19,6 +19,17 @@ layout(std140, binding = 1) readonly buffer CalculatedFrustumsBuffer{ } calculatedFrustums; } +/** Stores some additional information (some information not available as built-in semantics). */ +#glsl layout(binding = 2) uniform ThreadGroupCount { +#hlsl struct ThreadGroupCount{ + /** Total number of thread groups dispatched in the X direction. */ + uint iThreadGroupCountX; + + /** Total number of thread groups dispatched in the Y direction. */ + uint iThreadGroupCountY; +#glsl } threadGroupCount; +#hlsl }; ConstantBuffer threadGroupCount : register(b0, space5); + // -------------------------------------------------------------------------------------------------------------------- // counters // -------------------------------------------------------------------------------------------------------------------- @@ -26,7 +37,7 @@ layout(std140, binding = 1) readonly buffer CalculatedFrustumsBuffer{ /** A single `uint` value stored at index 0 - global counter into the light list with point lights for opaque geometry. */ #hlsl RWStructuredBuffer globalCounterIntoOpaquePointLightIndexList : register(u0, space5); #glsl{ -layout(std140, binding = 2) buffer GlobalCounterIntoOpaquePointLightIndexList{ +layout(std140, binding = 3) buffer GlobalCounterIntoOpaquePointLightIndexList{ uint iCounter; } globalCounterIntoOpaquePointLightIndexList; } @@ -34,7 +45,7 @@ layout(std140, binding = 2) buffer GlobalCounterIntoOpaquePointLightIndexList{ /** A single `uint` value stored at index 0 - global counter into the light list with spot lights for opaque geometry. */ #hlsl RWStructuredBuffer globalCounterIntoOpaqueSpotLightIndexList : register(u1, space5); #glsl{ -layout(std140, binding = 3) buffer GlobalCounterIntoOpaqueSpotLightIndexList{ +layout(std140, binding = 4) buffer GlobalCounterIntoOpaqueSpotLightIndexList{ uint iCounter; } globalCounterIntoOpaqueSpotLightIndexList; } @@ -42,7 +53,7 @@ layout(std140, binding = 3) buffer GlobalCounterIntoOpaqueSpotLightIndexList{ /** A single `uint` value stored at index 0 - global counter into the light list with directional lights for opaque geometry. */ #hlsl RWStructuredBuffer globalCounterIntoOpaqueDirectionalLightIndexList : register(u2, space5); #glsl{ -layout(std140, binding = 4) buffer GlobalCounterIntoOpaqueDirectionalLightIndexList{ +layout(std140, binding = 5) buffer GlobalCounterIntoOpaqueDirectionalLightIndexList{ uint iCounter; } globalCounterIntoOpaqueDirectionalLightIndexList; } @@ -52,7 +63,7 @@ layout(std140, binding = 4) buffer GlobalCounterIntoOpaqueDirectionalLightIndexL /** A single `uint` value stored at index 0 - global counter into the light list with point lights for transparent geometry. */ #hlsl RWStructuredBuffer globalCounterIntoTransparentPointLightIndexList : register(u3, space5); #glsl{ -layout(std140, binding = 5) buffer GlobalCounterIntoTransparentPointLightIndexList{ +layout(std140, binding = 6) buffer GlobalCounterIntoTransparentPointLightIndexList{ uint iCounter; } globalCounterIntoTransparentPointLightIndexList; } @@ -60,7 +71,7 @@ layout(std140, binding = 5) buffer GlobalCounterIntoTransparentPointLightIndexLi /** A single `uint` value stored at index 0 - global counter into the light list with spot lights for transparent geometry. */ #hlsl RWStructuredBuffer globalCounterIntoTransparentSpotLightIndexList : register(u4, space5); #glsl{ -layout(std140, binding = 6) buffer GlobalCounterIntoTransparentSpotLightIndexList{ +layout(std140, binding = 7) buffer GlobalCounterIntoTransparentSpotLightIndexList{ uint iCounter; } globalCounterIntoTransparentSpotLightIndexList; } @@ -68,7 +79,7 @@ layout(std140, binding = 6) buffer GlobalCounterIntoTransparentSpotLightIndexLis /** A single `uint` value stored at index 0 - global counter into the light list with directional lights for transparent geometry. */ #hlsl RWStructuredBuffer globalCounterIntoTransparentDirectionalLightIndexList : register(u5, space5); #glsl{ -layout(std140, binding = 7) buffer GlobalCounterIntoTransparentDirectionalLightIndexList{ +layout(std140, binding = 8) buffer GlobalCounterIntoTransparentDirectionalLightIndexList{ uint iCounter; } globalCounterIntoTransparentDirectionalLightIndexList; } @@ -80,7 +91,7 @@ layout(std140, binding = 7) buffer GlobalCounterIntoTransparentDirectionalLightI /** Stores indices into array of point lights for opaque geometry. */ #hlsl RWStructuredBuffer opaquePointLightIndexList : register(u6, space5); #glsl{ -layout(std140, binding = 8) buffer OpaquePointLightIndexListBuffer{ +layout(std140, binding = 9) buffer OpaquePointLightIndexListBuffer{ uint iLightIndex; } opaquePointLightIndexList; } @@ -88,7 +99,7 @@ layout(std140, binding = 8) buffer OpaquePointLightIndexListBuffer{ /** Stores indices into array of spot lights for opaque geometry. */ #hlsl RWStructuredBuffer opaqueSpotLightIndexList : register(u7, space5); #glsl{ -layout(std140, binding = 9) buffer OpaqueSpotLightIndexListBuffer{ +layout(std140, binding = 10) buffer OpaqueSpotLightIndexListBuffer{ uint iLightIndex; } opaqueSpotLightIndexList; } @@ -96,7 +107,7 @@ layout(std140, binding = 9) buffer OpaqueSpotLightIndexListBuffer{ /** Stores indices into array of directional lights for opaque geometry. */ #hlsl RWStructuredBuffer opaqueDirectionalLightIndexList : register(u8, space5); #glsl{ -layout(std140, binding = 10) buffer OpaqueDirectionalLightIndexListBuffer{ +layout(std140, binding = 11) buffer OpaqueDirectionalLightIndexListBuffer{ uint iLightIndex; } opaqueDirectionalLightIndexList; } @@ -106,7 +117,7 @@ layout(std140, binding = 10) buffer OpaqueDirectionalLightIndexListBuffer{ /** Stores indices into array of point lights for transparent geometry. */ #hlsl RWStructuredBuffer transparentPointLightIndexList : register(u9, space5); #glsl{ -layout(std140, binding = 11) buffer TransparentPointLightIndexListBuffer{ +layout(std140, binding = 12) buffer TransparentPointLightIndexListBuffer{ uint iLightIndex; } transparentPointLightIndexList; } @@ -114,7 +125,7 @@ layout(std140, binding = 11) buffer TransparentPointLightIndexListBuffer{ /** Stores indices into array of spot lights for transparent geometry. */ #hlsl RWStructuredBuffer transparentSpotLightIndexList : register(u10, space5); #glsl{ -layout(std140, binding = 12) buffer TransparentSpotLightIndexListBuffer{ +layout(std140, binding = 13) buffer TransparentSpotLightIndexListBuffer{ uint iLightIndex; } transparentSpotLightIndexList; } @@ -122,7 +133,7 @@ layout(std140, binding = 12) buffer TransparentSpotLightIndexListBuffer{ /** Stores indices into array of directional lights for transparent geometry. */ #hlsl RWStructuredBuffer transparentDirectionalLightIndexList : register(u11, space5); #glsl{ -layout(std140, binding = 13) buffer TransparentDirectionalLightIndexListBuffer{ +layout(std140, binding = 14) buffer TransparentDirectionalLightIndexListBuffer{ uint iLightIndex; } transparentDirectionalLightIndexList; } @@ -131,4 +142,285 @@ layout(std140, binding = 13) buffer TransparentDirectionalLightIndexListBuffer{ // light grids // -------------------------------------------------------------------------------------------------------------------- -// TODO \ No newline at end of file +/** Light grid where every pixel stores 2 values: offset into light index list and the number of elements to read from that offset. */ +#hlsl RWTexture2D opaquePointLightGrid : register(u12, space5); +#glsl layout (binding=15, rg32ui) uniform uimage2D opaquePointLightGrid; + +/** Light grid where every pixel stores 2 values: offset into light index list and the number of elements to read from that offset. */ +#hlsl RWTexture2D opaqueSpotLightGrid : register(u13, space5); +#glsl layout (binding=16, rg32ui) uniform uimage2D opaqueSpotLightGrid; + +/** Light grid where every pixel stores 2 values: offset into light index list and the number of elements to read from that offset. */ +#hlsl RWTexture2D opaqueDirectionalLightGrid : register(u14, space5); +#glsl layout (binding=17, rg32ui) uniform uimage2D opaqueDirectionalLightGrid; + +// -------------------------------------------------------------------------------------------------------------------- + +/** Light grid where every pixel stores 2 values: offset into light index list and the number of elements to read from that offset. */ +#hlsl RWTexture2D transparentPointLightGrid : register(u15, space5); +#glsl layout (binding=18, rg32ui) uniform uimage2D transparentPointLightGrid; + +/** Light grid where every pixel stores 2 values: offset into light index list and the number of elements to read from that offset. */ +#hlsl RWTexture2D transparentSpotLightGrid : register(u16, space5); +#glsl layout (binding=19, rg32ui) uniform uimage2D transparentSpotLightGrid; + +/** Light grid where every pixel stores 2 values: offset into light index list and the number of elements to read from that offset. */ +#hlsl RWTexture2D transparentDirectionalLightGrid : register(u17, space5); +#glsl layout (binding=20, rg32ui) uniform uimage2D transparentDirectionalLightGrid; + +// -------------------------------------------------------------------------------------------------------------------- +// general group shared (tile) variables +// -------------------------------------------------------------------------------------------------------------------- + +/** Minimum depth in a light grid tile. Stored as `uint` instead of `float` to allow atomic operations. */ +shared uint iTileMinDepth; + +/** Maximum depth in a light grid tile. Stored as `uint` instead of `float` to allow atomic operations. */ +shared uint iTileMaxDepth; + +/** Frustum of a light grid tile. */ +shared Frustum tileFrustum; + +// -------------------------------------------------------------------------------------------------------------------- +// light count - group shared (tile) variables +// -------------------------------------------------------------------------------------------------------------------- + +/** Stores total number of point lights that intersect frustum of a grid tile for opaque geometry. */ +shared uint iOpaquePointLightCountIntersectingTileFrustum; + +/** Stores total number of spot lights that intersect frustum of a grid tile for opaque geometry. */ +shared uint iOpaqueSpotLightCountIntersectingTileFrustum; + +/** Stores total number of directional lights that intersect frustum of a grid tile for opaque geometry. */ +shared uint iOpaqueDirectionalLightCountIntersectingTileFrustum; + +// -------------------------------------------------------------------------------------------------------------------- + +/** Stores total number of point lights that intersect frustum of a grid tile for transparent geometry. */ +shared uint iTransparentPointLightCountIntersectingTileFrustum; + +/** Stores total number of spot lights that intersect frustum of a grid tile for transparent geometry. */ +shared uint iTransparentSpotLightCountIntersectingTileFrustum; + +/** Stores total number of directional lights that intersect frustum of a grid tile for transparent geometry. */ +shared uint iTransparentDirectionalLightCountIntersectingTileFrustum; + +// -------------------------------------------------------------------------------------------------------------------- +// light index list offset - group shared (tile) variables +// -------------------------------------------------------------------------------------------------------------------- + +/** Offset into the global point light index list for opaque geometry. */ +shared uint iOpaquePointLightIndexListStartOffset; + +/** Offset into the global spot light index list for opaque geometry. */ +shared uint iOpaqueSpotLightIndexListStartOffset; + +/** Offset into the global directional light index list for opaque geometry. */ +shared uint iOpaqueDirectionalLightIndexListStartOffset; + +// -------------------------------------------------------------------------------------------------------------------- + +/** Offset into the global point light index list for transparent geometry. */ +shared uint iTransparentPointLightIndexListStartOffset; + + +/** Offset into the global spot light index list for transparent geometry. */ +shared uint iTransparentSpotLightIndexListStartOffset; + +/** Offset into the global directional light index list for transparent geometry. */ +shared uint iTransparentDirectionalLightIndexListStartOffset; + +// -------------------------------------------------------------------------------------------------------------------- +// local light index list - group shared (tile) variables +// -------------------------------------------------------------------------------------------------------------------- + +// allowing N lights of a specific type to be in a tile, this limit should never be reached +#define TILE_LIGHT_INDEX_LIST_SIZE AVERAGE_NUM_LIGHTS_OF_SPECIFIC_TYPE_PER_TILE * 5 + +/** Local light index list that will be copied to the global list index list. */ +shared uint tileOpaquePointLightIndexList[TILE_LIGHT_INDEX_LIST_SIZE]; + +/** Local light index list that will be copied to the global list index list. */ +shared uint tileOpaqueSpotLightIndexList[TILE_LIGHT_INDEX_LIST_SIZE]; + +/** Local light index list that will be copied to the global list index list. */ +shared uint tileOpaqueDirectionalLightIndexList[TILE_LIGHT_INDEX_LIST_SIZE]; + +// -------------------------------------------------------------------------------------------------------------------- + +/** Local light index list that will be copied to the global list index list. */ +shared uint tileTransparentPointLightIndexList[TILE_LIGHT_INDEX_LIST_SIZE]; + +/** Local light index list that will be copied to the global list index list. */ +shared uint tileTransparentSpotLightIndexList[TILE_LIGHT_INDEX_LIST_SIZE]; + +/** Local light index list that will be copied to the global list index list. */ +shared uint tileTransparentDirectionalLightIndexList[TILE_LIGHT_INDEX_LIST_SIZE]; + +// -------------------------------------------------------------------------------------------------------------------- +// general functions +// -------------------------------------------------------------------------------------------------------------------- + +/** + * Initialized thread group shared variables. + * + * @remark Should be called by a single thread in a thread group. + * + * @param iThreadGroupXIdInDispatch X ID of the current group in dispatch (i.e. X ID of the current light grid tile). + * @param iThreadGroupYIdInDispatch Y ID of the current group in dispatch (i.e. Y ID of the current light grid tile). + */ +void initializeGroupSharedVariables(uint iThreadGroupXIdInDispatch, uint iThreadGroupYIdInDispatch){ + // Initialize depth values. + iTileMinDepth = 0xffffffff; // uint max + iTileMaxDepth = 0; + + // Initialize counters. + iOpaquePointLightCountIntersectingTileFrustum = 0; + iOpaqueSpotLightCountIntersectingTileFrustum = 0; + iOpaqueDirectionalLightCountIntersectingTileFrustum = 0; + iTransparentPointLightCountIntersectingTileFrustum = 0; + iTransparentSpotLightCountIntersectingTileFrustum = 0; + iTransparentDirectionalLightCountIntersectingTileFrustum = 0; + + // Initialize offsets. + iOpaquePointLightIndexListStartOffset = 0; + iOpaqueSpotLightIndexListStartOffset = 0; + iOpaqueDirectionalLightIndexListStartOffset = 0; + iTransparentPointLightIndexListStartOffset = 0; + iTransparentSpotLightIndexListStartOffset = 0; + iTransparentDirectionalLightIndexListStartOffset = 0; + + // Prepare index to get tile frustum. + const uint iFrustumIndex = (iThreadGroupYIdInDispatch * threadGroupCount.iThreadGroupCountX) + iThreadGroupXIdInDispatch; + + // Initialize tile frustum. + tileFrustum = +#hlsl calculatedFrustums[iFrustumIndex]; +#glsl calculatedFrustums.array[iFrustumIndex]; +} + +// -------------------------------------------------------------------------------------------------------------------- +// functions to add lights to tile +// -------------------------------------------------------------------------------------------------------------------- + +/** + * Adds an index to the specified point light to array of lights that affect this tile (this thread group) + * for opaque geometry. + * + * @param iPointLightIndex Index to a point light to add. + */ +void addPointLightToTileOpaqueLightIndexList(uint iPointLightIndex){ + // Prepare index to light list. + uint iIndexToPointLightList = 0; + + // Atomically increment counter of point lights in tile. +#hlsl InterlockedAdd(iOpaquePointLightCountIntersectingTileFrustum, 1, iIndexToPointLightList); +#glsl iIndexToPointLightList = atomicAdd(iOpaquePointLightCountIntersectingTileFrustum, 1); + + // Make sure we won't access out of bound. + if (iIndexToPointLightList < TILE_LIGHT_INDEX_LIST_SIZE){ + tileOpaquePointLightIndexList[iIndexToPointLightList] = iPointLightIndex; + } +} + +/** + * Adds an index to the specified spot light to array of lights that affect this tile (this thread group) + * for opaque geometry. + * + * @param iSpotLightIndex Index to a spot light to add. + */ +void addSpotLightToTileOpaqueLightIndexList(uint iSpotLightIndex){ + // Prepare index to light list. + uint iIndexToSpotLightList = 0; + + // Atomically increment counter of spot lights in tile. +#hlsl InterlockedAdd(iOpaqueSpotLightCountIntersectingTileFrustum, 1, iIndexToSpotLightList); +#glsl iIndexToSpotLightList = atomicAdd(iOpaqueSpotLightCountIntersectingTileFrustum, 1); + + // Make sure we won't access out of bound. + if (iIndexToSpotLightList < TILE_LIGHT_INDEX_LIST_SIZE){ + tileOpaqueSpotLightIndexList[iIndexToSpotLightList] = iSpotLightIndex; + } +} + +/** + * Adds an index to the specified directional light to array of lights that affect this tile (this thread group) + * for opaque geometry. + * + * @param iDirectionalLightIndex Index to a directional light to add. + */ +void addDirectionalLightToTileOpaqueLightIndexList(uint iDirectionalLightIndex){ + // Prepare index to light list. + uint iIndexToDirectionalLightList = 0; + + // Atomically increment counter of directional lights in tile. +#hlsl InterlockedAdd(iOpaqueDirectionalLightCountIntersectingTileFrustum, 1, iIndexToDirectionalLightList); +#glsl iIndexToDirectionalLightList = atomicAdd(iOpaqueDirectionalLightCountIntersectingTileFrustum, 1); + + // Make sure we won't access out of bound. + if (iIndexToDirectionalLightList < TILE_LIGHT_INDEX_LIST_SIZE){ + tileOpaqueDirectionalLightIndexList[iIndexToDirectionalLightList] = iDirectionalLightIndex; + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +/** + * Adds an index to the specified point light to array of lights that affect this tile (this thread group) + * for transparent geometry. + * + * @param iPointLightIndex Index to a point light to add. + */ +void addPointLightToTileTransparentLightIndexList(uint iPointLightIndex){ + // Prepare index to light list. + uint iIndexToPointLightList = 0; + + // Atomically increment counter of point lights in tile. +#hlsl InterlockedAdd(iTransparentPointLightCountIntersectingTileFrustum, 1, iIndexToPointLightList); +#glsl iIndexToPointLightList = atomicAdd(iTransparentPointLightCountIntersectingTileFrustum, 1); + + // Make sure we won't access out of bound. + if (iIndexToPointLightList < TILE_LIGHT_INDEX_LIST_SIZE){ + tileTransparentPointLightIndexList[iIndexToPointLightList] = iPointLightIndex; + } +} + +/** + * Adds an index to the specified spot light to array of lights that affect this tile (this thread group) + * for transparent geometry. + * + * @param iSpotLightIndex Index to a spot light to add. + */ +void addSpotLightToTileTransparentLightIndexList(uint iSpotLightIndex){ + // Prepare index to light list. + uint iIndexToSpotLightList = 0; + + // Atomically increment counter of spot lights in tile. +#hlsl InterlockedAdd(iTransparentSpotLightCountIntersectingTileFrustum, 1, iIndexToSpotLightList); +#glsl iIndexToSpotLightList = atomicAdd(iTransparentSpotLightCountIntersectingTileFrustum, 1); + + // Make sure we won't access out of bound. + if (iIndexToSpotLightList < TILE_LIGHT_INDEX_LIST_SIZE){ + tileTransparentSpotLightIndexList[iIndexToSpotLightList] = iSpotLightIndex; + } +} + +/** + * Adds an index to the specified directional light to array of lights that affect this tile (this thread group) + * for transparent geometry. + * + * @param iDirectionalLightIndex Index to a directional light to add. + */ +void addDirectionalLightToTileTransparentLightIndexList(uint iDirectionalLightIndex){ + // Prepare index to light list. + uint iIndexToDirectionalLightList = 0; + + // Atomically increment counter of directional lights in tile. +#hlsl InterlockedAdd(iTransparentDirectionalLightCountIntersectingTileFrustum, 1, iIndexToDirectionalLightList); +#glsl iIndexToDirectionalLightList = atomicAdd(iTransparentDirectionalLightCountIntersectingTileFrustum, 1); + + // Make sure we won't access out of bound. + if (iIndexToDirectionalLightList < TILE_LIGHT_INDEX_LIST_SIZE){ + tileTransparentDirectionalLightIndexList[iIndexToDirectionalLightList] = iDirectionalLightIndex; + } +} \ No newline at end of file diff --git a/src/engine_lib/private/render/directx/DirectXRenderer.cpp b/src/engine_lib/private/render/directx/DirectXRenderer.cpp index b7e41f559..9b128ad7e 100644 --- a/src/engine_lib/private/render/directx/DirectXRenderer.cpp +++ b/src/engine_lib/private/render/directx/DirectXRenderer.cpp @@ -53,12 +53,19 @@ namespace ne { #endif DirectXRenderer::DirectXRenderer(GameManager* pGameManager) : Renderer(pGameManager) { + // Make sure we use the same formats as in Vulkan. static_assert( backBufferFormat == DXGI_FORMAT_R8G8B8A8_UNORM, "also change format in Vulkan renderer for (visual) consistency"); static_assert( depthStencilBufferFormat == DXGI_FORMAT_D24_UNORM_S8_UINT, "also change format in Vulkan renderer for (visual) consistency"); + + // Self check for light culling compute shader: + static_assert( + depthStencilBufferFormat == DXGI_FORMAT_D24_UNORM_S8_UINT, + "light culling compute shader expects the depth values to be in range [0..1], please review the " + "light culling compute shader and make sure it works correctly"); } std::vector DirectXRenderer::getEngineShadersToCompile() const { diff --git a/src/engine_lib/private/render/vulkan/VulkanRenderer.cpp b/src/engine_lib/private/render/vulkan/VulkanRenderer.cpp index 9c3db18d6..8c9db0849 100644 --- a/src/engine_lib/private/render/vulkan/VulkanRenderer.cpp +++ b/src/engine_lib/private/render/vulkan/VulkanRenderer.cpp @@ -30,6 +30,7 @@ namespace ne { VulkanRenderer::VulkanRenderer(GameManager* pGameManager) : Renderer(pGameManager) { + // Make sure we use the same formats as in DirectX. static_assert( swapChainImageFormat == VK_FORMAT_B8G8R8A8_UNORM, "also change format in DirectX renderer for (visual) consistency"); @@ -39,6 +40,12 @@ namespace ne { static_assert( depthImageFormat == VK_FORMAT_D24_UNORM_S8_UINT, "also change format in DirectX renderer for (visual) consistency"); + + // Self check for light culling compute shader: + static_assert( + depthImageFormat == VK_FORMAT_D24_UNORM_S8_UINT, + "light culling compute shader expects the depth values to be in range [0..1], please review the " + "light culling compute shader and make sure it works correctly"); } std::variant VulkanRenderer::getMaxSupportedAntialiasingQuality() const { @@ -484,14 +491,20 @@ namespace ne { std::variant VulkanRenderer::isDeviceSuitable(VkPhysicalDevice pGpu) { // Get device properties. - VkPhysicalDeviceProperties deviceProperties; - vkGetPhysicalDeviceProperties(pGpu, &deviceProperties); + VkPhysicalDeviceDepthStencilResolveProperties depthStencilResolve{}; + depthStencilResolve.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES; + depthStencilResolve.pNext = nullptr; + + VkPhysicalDeviceProperties2 deviceProperties; + deviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + deviceProperties.pNext = &depthStencilResolve; + vkGetPhysicalDeviceProperties2(pGpu, &deviceProperties); // Make sure this GPU supports used Vulkan version. - if (deviceProperties.apiVersion < iUsedVulkanVersion) { + if (deviceProperties.properties.apiVersion < iUsedVulkanVersion) { return std::format( "GPU \"{}\" does not support used Vulkan version {}", - deviceProperties.deviceName, + deviceProperties.properties.deviceName, getUsedApiVersion()); } @@ -505,7 +518,8 @@ namespace ne { const auto queueFamiliesIndices = std::get(std::move(queueFamiliesResult)); if (!queueFamiliesIndices.isComplete()) { return std::format( - "GPU \"{}\" does not support all required queue families", deviceProperties.deviceName); + "GPU \"{}\" does not support all required queue families", + deviceProperties.properties.deviceName); } // Make sure this GPU supports all used device extensions. @@ -519,7 +533,7 @@ namespace ne { if (!sMissingDeviceExtension.empty()) { return std::format( "GPU \"{}\" does not support required device extension \"{}\"", - deviceProperties.deviceName, + deviceProperties.properties.deviceName, sMissingDeviceExtension); } @@ -538,13 +552,9 @@ namespace ne { // Prepare a linked list of features that will be filled in `vkGetPhysicalDeviceFeatures2` below // so that we can check their support. - VkPhysicalDeviceDepthStencilResolveProperties depthStencilResolve{}; - depthStencilResolve.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES; - depthStencilResolve.pNext = nullptr; - VkPhysicalDeviceDescriptorIndexingFeatures descriptorIndexingFeatures{}; descriptorIndexingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES; - descriptorIndexingFeatures.pNext = &depthStencilResolve; + descriptorIndexingFeatures.pNext = nullptr; VkPhysicalDeviceFeatures2 deviceFeatures2{}; deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; @@ -557,16 +567,16 @@ namespace ne { // (because RenderSettings don't check whether it's supported or not when deserialized/changed) if (deviceFeatures2.features.samplerAnisotropy == VK_FALSE) { return std::format( - "GPU \"{}\" does not support anisotropic filtering", deviceProperties.deviceName); + "GPU \"{}\" does not support anisotropic filtering", deviceProperties.properties.deviceName); } // Make sure that maximum push constants size that we use is supported. if (VulkanPushConstantsManager::getMaxPushConstantsSizeInBytes() > - deviceProperties.limits.maxPushConstantsSize) { + deviceProperties.properties.limits.maxPushConstantsSize) { return std::format( "GPU \"{}\" max push constants size is only {} while we expect {}", - deviceProperties.deviceName, - deviceProperties.limits.maxPushConstantsSize, + deviceProperties.properties.deviceName, + deviceProperties.properties.limits.maxPushConstantsSize, VulkanPushConstantsManager::getMaxPushConstantsSizeInBytes()); } @@ -576,17 +586,19 @@ namespace ne { descriptorIndexingFeatures.descriptorBindingPartiallyBound == VK_FALSE || descriptorIndexingFeatures.runtimeDescriptorArray == VK_FALSE) { return std::format( - "GPU \"{}\" does not support used indexing features", deviceProperties.deviceName); + "GPU \"{}\" does not support used indexing features", deviceProperties.properties.deviceName); } // Make sure used depth resolve mode is supported. if ((depthStencilResolve.supportedDepthResolveModes & depthResolveMode) == 0) { return std::format( - "GPU \"{}\" does not support used depth resolve mode", deviceProperties.deviceName); + "GPU \"{}\" does not support used depth resolve mode", + deviceProperties.properties.deviceName); } if ((depthStencilResolve.supportedStencilResolveModes & stencilResolveMode) == 0) { return std::format( - "GPU \"{}\" does not support used stencil resolve mode", deviceProperties.deviceName); + "GPU \"{}\" does not support used stencil resolve mode", + deviceProperties.properties.deviceName); } // Make sure engine texture resource formats are supported as storage images. @@ -597,7 +609,7 @@ namespace ne { // Get format support details. VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties( - pPhysicalDevice, + pGpu, VulkanResourceManager::convertTextureResourceFormatToVkFormat(format), &formatProperties); @@ -606,7 +618,7 @@ namespace ne { return std::format( "GPU \"{}\" does not support one of the used texture resource formats to be used as " "a storage image", - deviceProperties.deviceName); + deviceProperties.properties.deviceName); } } @@ -1855,9 +1867,9 @@ namespace ne { VK_SAMPLE_COUNT_1_BIT, // 1 sample depthImageFormat, depthImageTiling, - VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + VK_IMAGE_ASPECT_DEPTH_BIT); // specify only depth aspect because we only care about it if (std::holds_alternative(result)) [[unlikely]] { auto error = std::get(std::move(result)); error.addCurrentLocationToErrorStack(); diff --git a/src/engine_lib/private/shader/general/EngineShaderConstantMacros.hpp b/src/engine_lib/private/shader/general/EngineShaderConstantMacros.hpp index a260b2054..4654d5aff 100644 --- a/src/engine_lib/private/shader/general/EngineShaderConstantMacros.hpp +++ b/src/engine_lib/private/shader/general/EngineShaderConstantMacros.hpp @@ -19,6 +19,18 @@ namespace ne { /** Macro value. */ static inline const auto sValue = "16"; }; + + /** + * Defines how much lights (of a specific type) are expected to be on average in a light grid + * tile for opaque or transparent geometry. + */ + struct AverageNumLightsOfSpecificTypePerTileMacro { + /** Macro name. */ + static inline const auto sName = "AVERAGE_NUM_LIGHTS_OF_SPECIFIC_TYPE_PER_TILE"; + + /** Macro value. */ + static inline const auto sValue = "200"; + }; }; }; } diff --git a/src/engine_lib/private/shader/general/resources/LightingShaderResourceManager.cpp b/src/engine_lib/private/shader/general/resources/LightingShaderResourceManager.cpp index efef2376d..924c3c8d9 100644 --- a/src/engine_lib/private/shader/general/resources/LightingShaderResourceManager.cpp +++ b/src/engine_lib/private/shader/general/resources/LightingShaderResourceManager.cpp @@ -585,7 +585,7 @@ namespace ne { #if defined(DEBUG) && defined(WIN32) static_assert( - sizeof(LightingShaderResourceManager) == 240, "consider notifying new arrays here"); // NOLINT + sizeof(LightingShaderResourceManager) == 256, "consider notifying new arrays here"); // NOLINT #elif defined(DEBUG) static_assert( sizeof(LightingShaderResourceManager) == 144, "consider notifying new arrays here"); // NOLINT @@ -626,7 +626,7 @@ namespace ne { #if defined(DEBUG) && defined(WIN32) static_assert( - sizeof(LightingShaderResourceManager) == 240, "consider notifying new arrays here"); // NOLINT + sizeof(LightingShaderResourceManager) == 256, "consider notifying new arrays here"); // NOLINT #elif defined(DEBUG) static_assert( sizeof(LightingShaderResourceManager) == 144, "consider notifying new arrays here"); // NOLINT @@ -655,7 +655,7 @@ namespace ne { #if defined(DEBUG) && defined(WIN32) static_assert( - sizeof(LightingShaderResourceManager) == 240, "consider notifying new arrays here"); // NOLINT + sizeof(LightingShaderResourceManager) == 256, "consider notifying new arrays here"); // NOLINT #elif defined(DEBUG) static_assert( sizeof(LightingShaderResourceManager) == 144, "consider notifying new arrays here"); // NOLINT @@ -1043,7 +1043,7 @@ namespace ne { #if defined(DEBUG) && defined(WIN32) static_assert( - sizeof(LightingShaderResourceManager) == 240, "consider creating new arrays here"); // NOLINT + sizeof(LightingShaderResourceManager) == 256, "consider creating new arrays here"); // NOLINT #elif defined(DEBUG) static_assert( sizeof(LightingShaderResourceManager) == 144, "consider creating new arrays here"); // NOLINT @@ -1065,7 +1065,7 @@ namespace ne { std::scoped_lock renderGuard(*pRenderer->getRenderResourcesMutex()); pRenderer->waitForGpuToFinishWorkUpToThisPoint(); - // Get tile size. + // Get tile size (this value also describes threads in one thread group). size_t iTileSizeInPixels = 0; try { iTileSizeInPixels = std::stoull( @@ -1075,14 +1075,14 @@ namespace ne { "failed to convert frustum grid tile size to an integer, error: {}", exception.what())); }; - // Calculate tile count. + // Calculate tile count (using INT/INT to "floor" if not divisible equally). const auto iTileCountX = static_cast(renderResolution.first / iTileSizeInPixels); const auto iTileCountY = static_cast(renderResolution.second / iTileSizeInPixels); // Calculate frustum count. const size_t iFrustumCount = iTileCountX * iTileCountY; - // Calculate thread group count. + // Calculate thread group count (we should dispatch 1 thread per tile). const auto iThreadGroupCountX = static_cast( std::ceil(static_cast(iTileCountX) / static_cast(iTileSizeInPixels))); const auto iThreadGroupCountY = static_cast( @@ -1134,6 +1134,10 @@ namespace ne { pComputeInterface->submitForExecution(iThreadGroupCountX, iThreadGroupCountY, 1); } + // Save tile count to be used by light culling shader. + iLastUpdateTileCountX = iTileCountX; + iLastUpdateTileCountY = iTileCountY; + return {}; } @@ -1271,8 +1275,9 @@ namespace ne { // Resource that stores calculated grid of frustums is binded inside of the update function // for shader that calculates that grid. - // Queue shader execution. - pComputeInterface->submitForExecution(16, 16, 1); // TODO + // Queue shader execution (we need to dispatch 1 thread group per tile). + pComputeInterface->submitForExecution( + frustumGridShader.iLastUpdateTileCountX, frustumGridShader.iLastUpdateTileCountY, 1); return {}; } @@ -1341,7 +1346,7 @@ namespace ne { #if defined(DEBUG) && defined(WIN32) static_assert( - sizeof(LightingShaderResourceManager) == 240, "consider resetting new arrays here"); // NOLINT + sizeof(LightingShaderResourceManager) == 256, "consider resetting new arrays here"); // NOLINT #elif defined(DEBUG) static_assert( sizeof(LightingShaderResourceManager) == 144, "consider resetting new arrays here"); // NOLINT diff --git a/src/engine_lib/private/shader/general/resources/LightingShaderResourceManager.h b/src/engine_lib/private/shader/general/resources/LightingShaderResourceManager.h index 07042fa85..6554ad9e3 100644 --- a/src/engine_lib/private/shader/general/resources/LightingShaderResourceManager.h +++ b/src/engine_lib/private/shader/general/resources/LightingShaderResourceManager.h @@ -529,7 +529,7 @@ namespace ne { #if defined(DEBUG) static_assert( - sizeof(LightingShaderResourceManager) == 240, "consider adding new arrays here"); // NOLINT + sizeof(LightingShaderResourceManager) == 256, "consider adding new arrays here"); // NOLINT #endif } #endif @@ -566,31 +566,31 @@ namespace ne { */ struct ComputeInfo { /** Total number of thread groups dispatched in the X direction. */ - alignas(iVkScalarAlignment) unsigned int iThreadGroupCountX; + alignas(iVkScalarAlignment) unsigned int iThreadGroupCountX = 0; /** Total number of thread groups dispatched in the Y direction. */ - alignas(iVkScalarAlignment) unsigned int iThreadGroupCountY; + alignas(iVkScalarAlignment) unsigned int iThreadGroupCountY = 0; /** Total number of tiles in the X direction. */ - alignas(iVkScalarAlignment) unsigned int iTileCountX; + alignas(iVkScalarAlignment) unsigned int iTileCountX = 0; /** Total number of tiles in the Y direction. */ - alignas(iVkScalarAlignment) unsigned int iTileCountY; + alignas(iVkScalarAlignment) unsigned int iTileCountY = 0; /** Maximum depth value. */ - alignas(iVkScalarAlignment) float maxDepth; + alignas(iVkScalarAlignment) float maxDepth = 0.0F; }; /** Data that is used to convert coordinates from screen space to view space. */ struct ScreenToViewData { /** Inverse of the projection matrix. */ - alignas(iVkMat4Alignment) glm::mat4 inverseProjectionMatrix; + alignas(iVkMat4Alignment) glm::mat4 inverseProjectionMatrix = glm::identity(); /** Width of the viewport (might be smaller that the actual screen size). */ - alignas(iVkScalarAlignment) unsigned int iRenderResolutionWidth; + alignas(iVkScalarAlignment) unsigned int iRenderResolutionWidth = 0; /** Height of the viewport (might be smaller that the actual screen size). */ - alignas(iVkScalarAlignment) unsigned int iRenderResolutionHeight; + alignas(iVkScalarAlignment) unsigned int iRenderResolutionHeight = 0; }; /** Groups buffers that we bind to a compute shader. */ @@ -645,6 +645,18 @@ namespace ne { /** Shader resources. */ ShaderResources resources; + /** + * Total number of tiles in the X direction that was used when @ref updateData + * was called the last time. + */ + unsigned int iLastUpdateTileCountX = 0; + + /** + * Total number of tiles in the X direction that was used when @ref updateData + * was called the last time. + */ + unsigned int iLastUpdateTileCountY = 0; + /** `true` if @ref initialize was called, `false` otherwise. */ bool bIsInitialized = false; diff --git a/src/engine_lib/private/shader/glsl/DescriptorSetLayoutGenerator.cpp b/src/engine_lib/private/shader/glsl/DescriptorSetLayoutGenerator.cpp index 40332a7c3..ce01bfe27 100644 --- a/src/engine_lib/private/shader/glsl/DescriptorSetLayoutGenerator.cpp +++ b/src/engine_lib/private/shader/glsl/DescriptorSetLayoutGenerator.cpp @@ -88,6 +88,10 @@ namespace ne { info.resourceType = GlslResourceType::STORAGE_BUFFER; break; } + case (SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE): { + info.resourceType = GlslResourceType::STORAGE_IMAGE; + break; + } default: { return Error( std::format("type the resource \"{}\" is not supported", descriptorBinding->name)); @@ -274,7 +278,7 @@ namespace ne { } // Generate layout binding. - auto layoutBindingResult = generateLayoutBinding(iBindingIndex, bindingInfo); + auto layoutBindingResult = generateLayoutBinding(iBindingIndex, bindingInfo, false); if (std::holds_alternative(layoutBindingResult)) [[unlikely]] { auto error = std::get(std::move(layoutBindingResult)); error.addCurrentLocationToErrorStack(); @@ -366,7 +370,7 @@ namespace ne { } // Generate layout binding. - auto layoutBindingResult = generateLayoutBinding(iBindingIndex, bindingInfo); + auto layoutBindingResult = generateLayoutBinding(iBindingIndex, bindingInfo, false); if (std::holds_alternative(layoutBindingResult)) [[unlikely]] { auto error = std::get(std::move(layoutBindingResult)); error.addCurrentLocationToErrorStack(); @@ -638,7 +642,7 @@ namespace ne { } // Generate layout binding. - auto layoutBindingResult = generateLayoutBinding(iBindingIndex, bindingInfo); + auto layoutBindingResult = generateLayoutBinding(iBindingIndex, bindingInfo, true); if (std::holds_alternative(layoutBindingResult)) [[unlikely]] { auto error = std::get(std::move(layoutBindingResult)); error.addCurrentLocationToErrorStack(); @@ -805,7 +809,9 @@ namespace ne { std::variant, Error> DescriptorSetLayoutGenerator::generateLayoutBinding( - uint32_t iBindingIndex, const Collected::DescriptorSetLayoutBindingInfo& bindingInfo) { + uint32_t iBindingIndex, + const Collected::DescriptorSetLayoutBindingInfo& bindingInfo, + bool bIsComputeShader) { VkDescriptorSetLayoutBinding layoutBinding{}; VkDescriptorBindingFlags bindingFlags{}; @@ -816,7 +822,8 @@ namespace ne { layoutBinding.descriptorCount = 1; // Set stage. - layoutBinding.stageFlags = VK_SHADER_STAGE_ALL; + layoutBinding.stageFlags = + bIsComputeShader ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS; // Set descriptor type. switch (bindingInfo.resourceType) { @@ -830,14 +837,20 @@ namespace ne { } case (GlslResourceType::COMBINED_SAMPLER): { layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - layoutBinding.pImmutableSamplers = nullptr; - // Override descriptor count. - layoutBinding.descriptorCount = DescriptorConstants::iBindlessTextureArrayDescriptorCount; + if (!bIsComputeShader) { + // Override descriptor count. + layoutBinding.descriptorCount = DescriptorConstants::iBindlessTextureArrayDescriptorCount; + + // Specify flags for bindless bindings. + bindingFlags = + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT; + } - // Specify flags for bindless bindings. - bindingFlags = - VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT; + break; + } + case (GlslResourceType::STORAGE_IMAGE): { + layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; break; } default: { diff --git a/src/engine_lib/private/shader/glsl/DescriptorSetLayoutGenerator.h b/src/engine_lib/private/shader/glsl/DescriptorSetLayoutGenerator.h index 070d51d19..a00a4223b 100644 --- a/src/engine_lib/private/shader/glsl/DescriptorSetLayoutGenerator.h +++ b/src/engine_lib/private/shader/glsl/DescriptorSetLayoutGenerator.h @@ -24,6 +24,7 @@ namespace ne { UNIFORM_BUFFER, STORAGE_BUFFER, COMBINED_SAMPLER, + STORAGE_IMAGE, // ... add new resource types here ... }; @@ -175,14 +176,18 @@ namespace ne { /** * Generates Vulkan layout binding that could be used to create a descriptor set layout. * - * @param iBindingIndex Index of the binding that was specified in the GLSL code. - * @param bindingInfo Information about the GLSL resource used in this binding. + * @param iBindingIndex Index of the binding that was specified in the GLSL code. + * @param bindingInfo Information about the GLSL resource used in this binding. + * @param bIsComputeShader `true` if this binding is being generated for a compute shader, otherwise + * `false`. * * @return Error if something went wrong, otherwise generated binding with flags. */ static std::variant, Error> generateLayoutBinding( - uint32_t iBindingIndex, const Collected::DescriptorSetLayoutBindingInfo& bindingInfo); + uint32_t iBindingIndex, + const Collected::DescriptorSetLayoutBindingInfo& bindingInfo, + bool bIsComputeShader); /** Name of the `uniform` buffer used to store frame data in GLSL shaders. */ static constexpr auto pFrameUniformBufferName = "frameData"; diff --git a/src/engine_lib/private/shader/glsl/GlslEngineShaders.hpp b/src/engine_lib/private/shader/glsl/GlslEngineShaders.hpp index d179df127..325b6c53d 100644 --- a/src/engine_lib/private/shader/glsl/GlslEngineShaders.hpp +++ b/src/engine_lib/private/shader/glsl/GlslEngineShaders.hpp @@ -50,8 +50,12 @@ namespace ne { ShaderType::COMPUTE_SHADER, "main", {{ - EngineShaderConstantMacros::ForwardPlus::FrustumGridThreadsInGroupXyMacro::sName, - EngineShaderConstantMacros::ForwardPlus::FrustumGridThreadsInGroupXyMacro::sValue, - }}); + EngineShaderConstantMacros::ForwardPlus::FrustumGridThreadsInGroupXyMacro::sName, + EngineShaderConstantMacros::ForwardPlus::FrustumGridThreadsInGroupXyMacro::sValue, + }, + { + EngineShaderConstantMacros::ForwardPlus::AverageNumLightsOfSpecificTypePerTileMacro::sName, + EngineShaderConstantMacros::ForwardPlus::AverageNumLightsOfSpecificTypePerTileMacro::sValue, + }}); }; } // namespace ne diff --git a/src/engine_lib/private/shader/hlsl/HlslEngineShaders.hpp b/src/engine_lib/private/shader/hlsl/HlslEngineShaders.hpp index f95821925..8b5a03ed2 100644 --- a/src/engine_lib/private/shader/hlsl/HlslEngineShaders.hpp +++ b/src/engine_lib/private/shader/hlsl/HlslEngineShaders.hpp @@ -50,8 +50,12 @@ namespace ne { ShaderType::COMPUTE_SHADER, "csLightCulling", {{ - EngineShaderConstantMacros::ForwardPlus::FrustumGridThreadsInGroupXyMacro::sName, - EngineShaderConstantMacros::ForwardPlus::FrustumGridThreadsInGroupXyMacro::sValue, - }}); + EngineShaderConstantMacros::ForwardPlus::FrustumGridThreadsInGroupXyMacro::sName, + EngineShaderConstantMacros::ForwardPlus::FrustumGridThreadsInGroupXyMacro::sValue, + }, + { + EngineShaderConstantMacros::ForwardPlus::AverageNumLightsOfSpecificTypePerTileMacro::sName, + EngineShaderConstantMacros::ForwardPlus::AverageNumLightsOfSpecificTypePerTileMacro::sValue, + }}); }; } // namespace ne