diff --git a/HPL2/include/graphics/ImageBindlessPool.h b/HPL2/include/graphics/ImageBindlessPool.h new file mode 100644 index 000000000..4dfc54813 --- /dev/null +++ b/HPL2/include/graphics/ImageBindlessPool.h @@ -0,0 +1,41 @@ +#pragma once + +#include "graphics/ForgeHandles.h" +#include "graphics/ForgeRenderer.h" +#include "graphics/IndexPool.h" + +#include +#include + +namespace hpl { + class Image; + class TextureDescriptorPool; + class ImageBindlessPool { + public: + static uint32_t constexpr MinimumFrameCount = ForgeRenderer::SwapChainLength * 4; + + struct ImageReserveEntry { + uint16_t m_prev = UINT16_MAX; + uint16_t m_next = UINT16_MAX; + uint32_t m_handle = IndexPool::InvalidHandle; + uint32_t m_frameCount = 0; + Image* m_image = nullptr; + }; + ImageBindlessPool(); + ImageBindlessPool(TextureDescriptorPool* pool, uint32_t reserveSize); + ~ImageBindlessPool(); + void reset(const ForgeRenderer::Frame& frame); // reset and prepare for the next frame + uint32_t request(Image* image); + + private: + + TextureDescriptorPool* m_texturePool = nullptr; + std::vector m_pool; + uint32_t m_allocSize = 0; + + uint16_t m_headIndex = UINT16_MAX; + uint16_t m_tailIndex = UINT16_MAX; + + uint32_t m_currentFrame = 0; + }; +} // namespace hpl diff --git a/HPL2/include/graphics/IndexPool.h b/HPL2/include/graphics/IndexPool.h index b8b602aaa..9c216cfca 100644 --- a/HPL2/include/graphics/IndexPool.h +++ b/HPL2/include/graphics/IndexPool.h @@ -13,6 +13,7 @@ namespace hpl { public: explicit IndexPool(uint32_t reserve); IndexPool(); + static constexpr uint32_t InvalidHandle = UINT32_MAX; uint32_t requestId(); inline uint32_t reserve() {return m_reserve;} diff --git a/HPL2/include/graphics/RendererDeferred2.h b/HPL2/include/graphics/RendererDeferred2.h index 4675c4df5..b76f168d6 100644 --- a/HPL2/include/graphics/RendererDeferred2.h +++ b/HPL2/include/graphics/RendererDeferred2.h @@ -21,6 +21,7 @@ #include "engine/RTTI.h" #include "graphics/CommandBufferPool.h" +#include "graphics/ImageBindlessPool.h" #include "graphics/SceneResource.h" #include "graphics/ShadowCache.h" #include "graphics/TextureDescriptorPool.h" @@ -82,6 +83,7 @@ namespace hpl { static constexpr uint32_t MaxIndirectDrawArgs = 4096; static constexpr uint32_t PointLightCount = 256; + static constexpr uint32_t SpotLightCount = 256; static constexpr float ShadowDistanceMedium = 10; static constexpr float ShadowDistanceLow = 20; static constexpr float ShadowDistanceNone = 40; @@ -91,6 +93,8 @@ namespace hpl { static constexpr uint32_t LightClusterWidth = 16; static constexpr uint32_t LightClusterHeight = 16; static constexpr uint32_t LightClusterLightCount = 128; + static constexpr uint32_t TransientImagePoolCount = 256; + struct ViewportData { public: @@ -176,15 +180,19 @@ namespace hpl { cRenderList m_rendererList; // Lights + ImageBindlessPool m_transientImagePool; SharedRootSignature m_lightClusterRootSignature; std::array m_lightDescriptorPerFrameSet; SharedShader m_lightClusterShader; + SharedShader m_lightClusterSpotlightShader; SharedShader m_clearLightClusterShader; SharedPipeline m_pointLightClusterPipeline; + SharedPipeline m_spotLightClusterPipeline; SharedPipeline m_clearClusterPipeline; std::array m_lightClustersBuffer; std::array m_lightClusterCountBuffer; std::array m_pointLightBuffer; + std::array m_spotlightBuffer; }; }; // namespace hpl diff --git a/HPL2/include/graphics/SceneResource.h b/HPL2/include/graphics/SceneResource.h index 03b317ab2..f42c3b493 100644 --- a/HPL2/include/graphics/SceneResource.h +++ b/HPL2/include/graphics/SceneResource.h @@ -77,11 +77,26 @@ namespace hpl::resource { }; struct ScenePointLight { - mat4 m_mvp; - float3 m_lightPos; + float3 m_worldPos; + uint32_t m_config; + float4 m_lightColor; + float m_radius; + uint32_t m_pad0; + uint32_t m_pad1; + uint32_t m_pad2; + }; + + struct SceneSpotLight { + mat4 m_viewProjection; + float3 m_worldPos; uint m_config; + float3 m_direction; + float m_angle; float4 m_lightColor; float m_radius; + uint32_t m_pad0; + uint32_t m_pad1; + uint32_t m_pad2; }; struct DiffuseMaterial { diff --git a/HPL2/include/scene/LightSpot.h b/HPL2/include/scene/LightSpot.h index 2b1fa9dbe..d540115cf 100644 --- a/HPL2/include/scene/LightSpot.h +++ b/HPL2/include/scene/LightSpot.h @@ -33,9 +33,6 @@ namespace hpl { class cLightSpot : public iLight { - #ifdef __GNUC__ - typedef iLight __super; - #endif public: cLightSpot(tString asName, cResources *apResources); ~cLightSpot(); diff --git a/HPL2/resource/ShaderList.fsl b/HPL2/resource/ShaderList.fsl index ea9170dbe..916892a0b 100644 --- a/HPL2/resource/ShaderList.fsl +++ b/HPL2/resource/ShaderList.fsl @@ -334,6 +334,10 @@ #include "scene_light_cluster.comp.fsl" #end +#comp scene_light_cluster_spotlight.comp + #include "scene_light_cluster_spotlight.comp.fsl" +#end + #comp scene_light_cluster_clear.comp #include "scene_light_cluster_clear.comp.fsl" #end diff --git a/HPL2/resource/deferred_light_spotlight.frag.fsl b/HPL2/resource/deferred_light_spotlight.frag.fsl index 02dcb98e4..8b68cbd72 100644 --- a/HPL2/resource/deferred_light_spotlight.frag.fsl +++ b/HPL2/resource/deferred_light_spotlight.frag.fsl @@ -46,7 +46,7 @@ float4 PS_MAIN(PsIn In) float3 diffuseColor = color.xyz * Get(lightObjectBuffer)[Get(lightId)].lightColor.xyz * fLDotN; float t = 0; if(HasShadowMap(Get(lightObjectBuffer)[Get(lightId)].config)) { - const float depthBias = 0.0005; + const float depthBias = 0.0005; #ifdef SHADOW_JITTER_SIZE float2 shadowTexelSize = 1.0/ GetDimensions(Get(shadowMap), NO_SAMPLER).xy; diff --git a/HPL2/resource/math_utils.h.fsl b/HPL2/resource/math_utils.h.fsl index 6f42882e2..c92f1993f 100644 --- a/HPL2/resource/math_utils.h.fsl +++ b/HPL2/resource/math_utils.h.fsl @@ -1,6 +1,8 @@ #ifndef _MATH_UTILITIES_H_ #define _MATH_UTILITIES_H_ +#define PI 3.141592654f + INLINE float3x3 ToNormalMat(float4x4 invModel, float4x4 invView) { return transpose(mul(float3x3(invModel[0].xyz, invModel[1].xyz, invModel[2].xyz), float3x3(invView[0].xyz, invView[1].xyz, invView[2].xyz))); } @@ -20,4 +22,20 @@ float3 WorldSpaceToTangent(float3 dir, float3 normal, float3 tangent, float3 bit return float3(a,b,c); } +float4 boundingSphereForSpotlight(in float3 origin, in float3 forward, in float size, in float angle) +{ + float4 boundingSphere; + if(angle > PI/4.0f) + { + boundingSphere.xyz = origin + cos(angle) * size * forward; + boundingSphere.w = sin(angle) * size; + } + else + { + boundingSphere.xyz = origin + size / (2.0f * cos(angle)) * forward; + boundingSphere.w = size / (2.0f * cos(angle)); + } + + return boundingSphere; +} #endif diff --git a/HPL2/resource/scene_defs.h.fsl b/HPL2/resource/scene_defs.h.fsl index 09930f2e8..b71ffb191 100644 --- a/HPL2/resource/scene_defs.h.fsl +++ b/HPL2/resource/scene_defs.h.fsl @@ -121,12 +121,39 @@ STRUCT(UniformObject) #define MATERIAL_INDEX(id) ((MATERIAL_INDEX_MASK & (id)) >> MATERIAL_INDEX_BIT) STRUCT(PointLight) { - DATA(float4x4, mvp, None); DATA(float3, lightPos, none); DATA(uint, config, none); DATA(float4, lightColor, none); DATA(float, lightRadius, none); + DATA(uint, __pad0, NONE); + DATA(uint, __pad1, NONE); + DATA(uint, __pad2, NONE); +}; + +STRUCT(SpotLight) { + DATA(mat4, viewProjection, none); + DATA(float3, lightPos, none); + DATA(uint, config, none); + DATA(float3, direction, none); + DATA(float, angle, none); + DATA(float4, lightColor, none); + DATA(float, radius, none); + DATA(uint, __pad0, NONE); + DATA(uint, __pad1, NONE); + DATA(uint, __pad2, NONE); }; +#define LIGHT_TYPE_POINT_LIGHT 1 +#define LIGHT_TYPE_SPOT_LIGHT 2 + +#define LIGHT_ID_BIT 0 +#define LIGHT_INDEX_BIT 8 + +#define LIGHT_ID_MASK 0xff // 0000 0000 0000 0000 0000 0000 1111 1111 +#define LIGHT_INDEX_MASK 0xffff00 // 0000 0000 1111 1111 1111 1111 0000 0000 + +#define LIGHT_ID(id) ((LIGHT_ID_MASK & (id)) >> LIGHT_ID_BIT) +#define LIGHT_INDEX(id) ((LIGHT_INDEX_MASK & (id)) >> LIGHT_INDEX_BIT) +#define LIGHT_ENCODE(id, index) (((id << LIGHT_ID_BIT) & LIGHT_ID_MASK) | ((index << LIGHT_INDEX_BIT) & LIGHT_INDEX_MASK)) STRUCT(WorldInfo) { DATA(float, worldFogStart, None); diff --git a/HPL2/resource/scene_light_cluster.comp.fsl b/HPL2/resource/scene_light_cluster.comp.fsl index d6f6fe6f6..accc282d4 100644 --- a/HPL2/resource/scene_light_cluster.comp.fsl +++ b/HPL2/resource/scene_light_cluster.comp.fsl @@ -59,7 +59,7 @@ void CS_MAIN(SV_GroupThreadID(uint3) threadInGroupId, SV_GroupID(uint3) groupId) AtomicAdd(Get(lightClustersCount)[LIGHT_CLUSTER_COUNT_POS(threadInGroupId.x, threadInGroupId.y)], 1, lightArrayPos); // Add light id to cluster - AtomicExchange(Get(lightClusters)[LIGHT_CLUSTER_DATA_POS(lightArrayPos, threadInGroupId.x, threadInGroupId.y)], groupId.x, lightArrayPos); + AtomicExchange(Get(lightClusters)[LIGHT_CLUSTER_DATA_POS(lightArrayPos, threadInGroupId.x, threadInGroupId.y)], LIGHT_ENCODE(LIGHT_TYPE_POINT_LIGHT, groupId.x), lightArrayPos); } } diff --git a/HPL2/resource/scene_light_cluster_resource.h.fsl b/HPL2/resource/scene_light_cluster_resource.h.fsl index 4af509554..c45bcc6e0 100644 --- a/HPL2/resource/scene_light_cluster_resource.h.fsl +++ b/HPL2/resource/scene_light_cluster_resource.h.fsl @@ -5,9 +5,10 @@ CBUFFER(sceneInfo, UPDATE_FREQ_PER_FRAME, b0, binding = 0) { DATA(ViewportInfo, viewports[64], None); }; -RES(Buffer(PointLight), pointLights, UPDATE_FREQ_PER_FRAME, t0, binding = 1); -RES(RWBuffer(atomic_uint), lightClustersCount, UPDATE_FREQ_PER_FRAME, u0, binding = 2); -RES(RWBuffer(atomic_uint), lightClusters, UPDATE_FREQ_PER_FRAME, u1, binding = 3); +RES(Buffer(SpotLight), spotLights, UPDATE_FREQ_PER_FRAME, t0, binding = 1); +RES(Buffer(PointLight), pointLights, UPDATE_FREQ_PER_FRAME, t0, binding = 2); +RES(RWBuffer(atomic_uint), lightClustersCount, UPDATE_FREQ_PER_FRAME, u0, binding = 3); +RES(RWBuffer(atomic_uint), lightClusters, UPDATE_FREQ_PER_FRAME, u1, binding = 4); diff --git a/HPL2/resource/scene_light_cluster_spotlight.comp.fsl b/HPL2/resource/scene_light_cluster_spotlight.comp.fsl new file mode 100644 index 000000000..1282a6ffd --- /dev/null +++ b/HPL2/resource/scene_light_cluster_spotlight.comp.fsl @@ -0,0 +1,70 @@ +#include "scene_light_cluster_resource.h.fsl" +#include "math_utils.h.fsl" + + +NUM_THREADS(LIGHT_CLUSTER_WIDTH, LIGHT_CLUSTER_HEIGHT, 1) +void CS_MAIN(SV_GroupThreadID(uint3) threadInGroupId, SV_GroupID(uint3) groupId) +{ + INIT_MAIN; + const float invClusterWidth = 1.0f / float(LIGHT_CLUSTER_WIDTH); + const float invClusterHeight = 1.0f / float(LIGHT_CLUSTER_HEIGHT); + ViewportInfo viewInfo = Get(viewports)[PRIMARY_VIEWPORT_INDEX]; + float2 size = viewInfo.rect.zw; + float4x4 viewProjMat = mul(viewInfo.projMat, viewInfo.viewMat); + + // aspect ratio is hard coded to 4/3 + const float aspectRatio = 4.0/3.0; + SpotLight lightData = Get(spotLights)[groupId.x]; + + float4 sphere = boundingSphereForSpotlight(lightData.lightPos, lightData.direction, lightData.radius, lightData.angle); + + float4 lightPosWorldSpace = float4(sphere.xyz, 1.0f); + float4 lightPosClipSpace = mul(viewProjMat, lightPosWorldSpace); + float invLightPosW = 1.0f / lightPosClipSpace.w; + float3 lightPos = lightPosClipSpace.xyz * invLightPosW; + + float fov = 2.0 * atan((2.0 * viewInfo.zNear)/viewInfo.projMat[1][1]); + float projRadius = 2.0f * sphere.w * (1 / tan(fov * 0.5f)) * invLightPosW; + projRadius *= size.x > size.y ? aspectRatio : 1 / aspectRatio; + + // Early exit light if it's behind the camera + if (lightPosClipSpace.w < 0.0f && -lightPosClipSpace.w > sphere.w) { + RETURN(); + } + + lightPos.x *= aspectRatio; + + // Cluster coordinates in post perspective clip space + float clusterLeft = float(threadInGroupId.x) * invClusterWidth; + float clusterTop = float(threadInGroupId.y) * invClusterHeight; + float clusterRight = clusterLeft + invClusterWidth; + float clusterBottom = clusterTop + invClusterHeight; + + // Transform coordinates from range [0..1] to range [-1..1] + clusterLeft = clusterLeft * 2.0f - 1.0f; + clusterTop = clusterTop * 2.0f - 1.0f; + clusterRight = clusterRight * 2.0f - 1.0f; + clusterBottom = clusterBottom * 2.0f - 1.0f; + + clusterLeft *= aspectRatio; + clusterRight *= aspectRatio; + + float clusterCenterX = (clusterLeft + clusterRight) * 0.5f; + float clusterCenterY = (clusterTop + clusterBottom) * 0.5f; + float clusterRadius = distance(float2(clusterLeft, clusterTop), float2(clusterRight, clusterBottom)) * 0.5f; + + // Check if the light projection overlaps the cluster: add the light bit to this cluster coords + float distanceToCenter = distance(float2(clusterCenterX, clusterCenterY), lightPos.xy); + + if (distanceToCenter - clusterRadius < abs(projRadius)) + { + // Increase light count on this cluster + uint lightArrayPos = 0; + AtomicAdd(Get(lightClustersCount)[LIGHT_CLUSTER_COUNT_POS(threadInGroupId.x, threadInGroupId.y)], 1, lightArrayPos); + + // Add light id to cluster + AtomicExchange(Get(lightClusters)[LIGHT_CLUSTER_DATA_POS(lightArrayPos, threadInGroupId.x, threadInGroupId.y)], LIGHT_ENCODE(LIGHT_TYPE_SPOT_LIGHT, groupId.x), lightArrayPos); + } +} + + diff --git a/HPL2/resource/scene_resource.h.fsl b/HPL2/resource/scene_resource.h.fsl index 086bc903d..098098ad3 100644 --- a/HPL2/resource/scene_resource.h.fsl +++ b/HPL2/resource/scene_resource.h.fsl @@ -28,9 +28,10 @@ RES(SamplerState, nearPointWrapSampler, UPDATE_FREQ_NONE, s1, binding = 11); RES(Tex2D(float4), visibilityTexture, UPDATE_FREQ_PER_FRAME, t0, binding = 12); RES(Tex2D(float4), dissolveTexture, UPDATE_FREQ_NONE, t0, binding = 13); -RES(Buffer(PointLight), pointLights, UPDATE_FREQ_PER_FRAME, t0, binding = 14); -RES(RWBuffer(atomic_uint), lightClustersCount, UPDATE_FREQ_PER_FRAME, u0, binding = 15); -RES(RWBuffer(atomic_uint), lightClusters, UPDATE_FREQ_PER_FRAME, u1, binding = 16); +RES(Buffer(SpotLight), spotLights, UPDATE_FREQ_PER_FRAME, t0, binding = 14); +RES(Buffer(PointLight), pointLights, UPDATE_FREQ_PER_FRAME, t0, binding = 15); +RES(RWBuffer(atomic_uint), lightClustersCount, UPDATE_FREQ_PER_FRAME, u0, binding = 16); +RES(RWBuffer(atomic_uint), lightClusters, UPDATE_FREQ_PER_FRAME, u1, binding = 17); RES(SamplerState, sceneFilters[SCENE_MAX_FILTER_COUNT], UPDATE_FREQ_NONE, s1, binding = 20); RES(Tex2D(float4), sceneTextures[SCENE_MAX_TEXTURE_COUNT], UPDATE_FREQ_PER_FRAME, t5, binding = 20 + SCENE_MAX_FILTER_COUNT); diff --git a/HPL2/resource/visibility_shade_pass.frag.fsl b/HPL2/resource/visibility_shade_pass.frag.fsl index 82e161e92..5015879a9 100644 --- a/HPL2/resource/visibility_shade_pass.frag.fsl +++ b/HPL2/resource/visibility_shade_pass.frag.fsl @@ -79,7 +79,7 @@ PsOut PS_MAIN(PsIn In) float4 clipPos2 = mul(mvp, float4(v2pos, 1.0f)); - float2 twoOverWindowSize = 2.0 / viewInfo.rect.zw; + float2 twoOverWindowSize = 1.5 / viewInfo.rect.zw; BarycentricDeriv derivativesOut = CalcFullBary(clipPos0, clipPos1, clipPos2, In.screenPos, twoOverWindowSize); // Interpolate the 1/w (one_over_w) for all three vertices of the triangle using the barycentric coordinates and the delta vector float w = dot(float3(clipPos0.w, clipPos1.w, clipPos2.w), derivativesOut.m_lambda); @@ -151,25 +151,64 @@ PsOut PS_MAIN(PsIn In) uint numLightsInCluster = Get(lightClustersCount)[LIGHT_CLUSTER_COUNT_POS(clusterCoords.x, clusterCoords.y)]; float4 outputColor = float4(0.0,0,0, 0); for(uint j = 0; j < numLightsInCluster; j++) { - uint lightId = Get(lightClusters)[LIGHT_CLUSTER_DATA_POS(j, clusterCoords.x, clusterCoords.y)]; - PointLight pl = Get(pointLights)[lightId]; - float4 lightPosWorldSpace = float4(pl.lightPos.xyz, 1.0f); - float4 lightCameraSpace = mul(viewInfo.viewMat, lightPosWorldSpace); - - const float3 lightDir = (lightCameraSpace.xyz - viewPosition) * (1.0 / pl.lightRadius); - const float3 normalLightDir = normalize(lightDir); - const float attenuation = saturate(1.0f - dot(lightDir , lightDir)); - const float fLDotN = max(dot(worldNormal, normalLightDir), 0.0); - - float specularValue = 0.0; - float3 lightDiffuseColor = pl.lightColor.xyz * fLDotN; - if(pl.lightColor.w > 0.0) { - float3 halfVec = normalize(normalLightDir + normalize(-viewPosition.xyz)); - float specIntensity = specular.x; - float specPower = exp2(specular.y * 10.0) + 1.0; - specularValue = pl.lightColor.w * specIntensity * pow( clamp( dot( halfVec, worldNormal), 0.0, 1.0), specPower ); - } - outputColor += float4(((specularValue.xxx * pl.lightColor.xyz) + (diffuseColor.xyz * pl.lightColor.xyz * fLDotN)) * attenuation, 0.0); + uint lightEncoding = Get(lightClusters)[LIGHT_CLUSTER_DATA_POS(j, clusterCoords.x, clusterCoords.y)]; + uint id = LIGHT_ID(lightEncoding); + uint index = LIGHT_INDEX(lightEncoding); + + switch(id) { + case LIGHT_TYPE_POINT_LIGHT: { + PointLight pl = Get(pointLights)[index]; + float4 lightPosWorldSpace = float4(pl.lightPos.xyz, 1.0f); + float4 lightCameraSpace = mul(viewInfo.viewMat, lightPosWorldSpace); + + const float3 lightDir = (lightCameraSpace.xyz - viewPosition) * (1.0 / pl.lightRadius); + const float3 normalLightDir = normalize(lightDir); + const float attenuation = saturate(1.0f - dot(lightDir , lightDir)); + const float fLDotN = max(dot(worldNormal, normalLightDir), 0.0); + + float specularValue = 0.0; + float3 lightDiffuseColor = pl.lightColor.xyz * fLDotN; + if(pl.lightColor.w > 0.0) { + float3 halfVec = normalize(normalLightDir + normalize(-viewPosition.xyz)); + float specIntensity = specular.x; + float specPower = exp2(specular.y * 10.0) + 1.0; + specularValue = pl.lightColor.w * specIntensity * pow( clamp( dot( halfVec, worldNormal), 0.0, 1.0), specPower ); + } + outputColor += float4(((specularValue.xxx * pl.lightColor.xyz) + (diffuseColor.xyz * pl.lightColor.xyz * fLDotN)) * attenuation, 0.0); + break; + } + case LIGHT_TYPE_SPOT_LIGHT: { + SpotLight spotlight = Get(spotLights)[index]; + + float4 lightPosWorldSpace = float4(spotlight.lightPos.xyz, 1.0f); + float4 lightCameraSpace = mul(viewInfo.viewMat, lightPosWorldSpace); + + const float3 lightDir = (lightCameraSpace.xyz - viewPosition) * (1.0 / spotlight.radius); + const float3 normalLightDir = normalize(lightDir); + float attenuation = saturate(1.0f - dot(lightDir , lightDir)); + const float fLDotN = max(dot(worldNormal, normalLightDir), 0.0); + + float oneMinusCos = 1.0 - dot(normalLightDir, spotlight.direction); + attenuation *= saturate(1.0 - (oneMinusCos / (1.0 - cos(spotlight.angle * 0.5)))); + + float4 projectionUV = mul(spotlight.viewProjection, float4(position, 1.0)); + projectionUV.xyz = (projectionUV.xyz / projectionUV.w); + projectionUV.xy = projectionUV.xy * 0.5 + 0.5; + projectionUV.w = 1.0; + + float specularValue = 0.0; + float3 lightDiffuseColor = spotlight.lightColor.xyz * fLDotN; + if(spotlight.lightColor.w > 0.0) { + float3 halfVec = normalize(normalLightDir + normalize(-viewPosition.xyz)); + float specIntensity = specular.x; + float specPower = exp2(specular.y * 10.0) + 1.0; + specularValue = spotlight.lightColor.w * specIntensity * pow( clamp( dot( halfVec, worldNormal), 0.0, 1.0), specPower ); + } + outputColor += float4(((specularValue.xxx * spotlight.lightColor.xyz) + (diffuseColor.xyz * spotlight.lightColor.xyz * fLDotN)) * attenuation, 0.0); + break; + } + } + } float4 illuminationSample = float4(0,0,0,0); diff --git a/HPL2/sources/graphics/ImageBindlessPool.cpp b/HPL2/sources/graphics/ImageBindlessPool.cpp new file mode 100644 index 000000000..15dc06c78 --- /dev/null +++ b/HPL2/sources/graphics/ImageBindlessPool.cpp @@ -0,0 +1,108 @@ +#include "graphics/ImageBindlessPool.h" +#include "graphics/Image.h" +#include "graphics/IndexPool.h" +#include "graphics/TextureDescriptorPool.h" + +#include + +#include + +namespace hpl { + ImageBindlessPool::ImageBindlessPool() { + + } + ImageBindlessPool::ImageBindlessPool(TextureDescriptorPool* pool, uint32_t minimumReserve): + m_texturePool(pool), + m_allocSize(minimumReserve) { + m_pool.resize(m_allocSize * 1.5f); + } + + + void ImageBindlessPool::reset(const ForgeRenderer::Frame& frame) { + m_currentFrame = frame.m_currentFrame; + while(m_headIndex != UINT16_MAX && + (m_currentFrame - m_pool[m_headIndex].m_frameCount) <= MinimumFrameCount) { + auto& head = m_pool[m_headIndex]; + // free data + m_texturePool->dispose(head.m_handle); + head.m_handle = IndexPool::InvalidHandle; + head.m_image = nullptr; + + // move the head back + m_headIndex = head.m_prev; + + // clear the next and previous pointers + head.m_next = UINT16_MAX; + head.m_prev = UINT16_MAX; + + if(m_headIndex != UINT16_MAX) { + m_pool[m_headIndex].m_next = UINT16_MAX; + } else { + m_tailIndex = UINT16_MAX; + } + } + } + ImageBindlessPool::~ImageBindlessPool() { + + while(m_headIndex != UINT16_MAX) { + auto& head = m_pool[m_headIndex]; + m_texturePool->dispose(head.m_handle); + + // move the head back + m_headIndex = head.m_prev; + } + + } + + uint32_t ImageBindlessPool::request(Image* image) { + ASSERT(m_texturePool); + uint32_t targetIndex = folly::hash::fnv32_buf(image, sizeof(Image*)) % m_pool.size(); + auto refreshAppendSlot = [&](uint32_t index) { + auto& current = m_pool[index]; + + if (m_currentFrame == current.m_frameCount || + m_tailIndex == index || + (m_headIndex == index && m_tailIndex == index)) { + current.m_frameCount = m_currentFrame; + return; + } else if (m_headIndex == index) { + m_headIndex = current.m_prev; + } + + if(current.m_prev != UINT16_MAX) { + m_pool[current.m_prev].m_next = current.m_next; + } + if(current.m_next != UINT16_MAX) { + m_pool[current.m_next].m_prev = current.m_prev; + } + current.m_prev = UINT16_MAX; + current.m_next = UINT16_MAX; + current.m_frameCount = m_currentFrame; + if(m_tailIndex != UINT16_MAX) { + current.m_next = m_tailIndex; + m_pool[m_tailIndex].m_prev = index; + m_tailIndex = index; + } else { + m_tailIndex = index; + m_headIndex = index; + } + + }; + + uint32_t index = targetIndex; + do { + auto& current = m_pool[index]; + if(current.m_image == image) { + refreshAppendSlot(index); + return current.m_handle; + } else if(current.m_image == nullptr) { + current.m_image = image; + current.m_handle = m_texturePool->request(image->GetTexture()); + refreshAppendSlot(index); + return current.m_handle; + } + index = (index + 1) % m_pool.size(); + } while(index != targetIndex); + return IndexPool::InvalidHandle; + } +} // namespace hpl diff --git a/HPL2/sources/graphics/IndexPool.cpp b/HPL2/sources/graphics/IndexPool.cpp index 8dea54cf6..08cce6009 100644 --- a/HPL2/sources/graphics/IndexPool.cpp +++ b/HPL2/sources/graphics/IndexPool.cpp @@ -29,7 +29,7 @@ namespace hpl { entry.m_end--; return res; } - return UINT32_MAX; + return IndexPool::InvalidHandle; } void IndexPool::returnId(uint32_t index) { diff --git a/HPL2/sources/graphics/RendererDeferred2.cpp b/HPL2/sources/graphics/RendererDeferred2.cpp index 45f48f7aa..d6c3ceaa3 100644 --- a/HPL2/sources/graphics/RendererDeferred2.cpp +++ b/HPL2/sources/graphics/RendererDeferred2.cpp @@ -2,10 +2,12 @@ #include "graphics/DrawPacket.h" #include "graphics/ForgeRenderer.h" #include "graphics/GraphicsTypes.h" +#include "graphics/ImageBindlessPool.h" #include "graphics/SceneResource.h" #include "graphics/TextureDescriptorPool.h" #include "resources/TextureManager.h" #include "scene/Light.h" +#include "scene/LightSpot.h" #include "scene/RenderableContainer.h" #include "tinyimageformat_base.h" #include @@ -44,6 +46,7 @@ namespace hpl { cRendererDeferred2::cRendererDeferred2(cGraphics* apGraphics, cResources* apResources, std::shared_ptr debug) : iRenderer("Deferred2", apGraphics, apResources) { m_sceneTexture2DPool = TextureDescriptorPool(ForgeRenderer::SwapChainLength, resource::MaxSceneTextureCount); + m_transientImagePool = ImageBindlessPool(&m_sceneTexture2DPool, TransientImagePoolCount); // Indirect Argument signature auto* forgeRenderer = Interface::Get(); { @@ -106,6 +109,12 @@ namespace hpl { addShader(forgeRenderer->Rend(), &loadDesc, shader); return true; }); + m_lightClusterSpotlightShader.Load(forgeRenderer->Rend(), [&](Shader** shader) { + ShaderLoadDesc loadDesc = {}; + loadDesc.mStages[0].pFileName = "scene_light_cluster_spotlight.comp"; + addShader(forgeRenderer->Rend(), &loadDesc, shader); + return true; + }); m_clearLightClusterShader.Load(forgeRenderer->Rend(), [&](Shader** shader) { ShaderLoadDesc loadDesc = {}; loadDesc.mStages[0].pFileName = "scene_light_cluster_clear.comp"; @@ -128,7 +137,9 @@ namespace hpl { }); m_lightClusterRootSignature.Load(forgeRenderer->Rend(), [&](RootSignature** signature) { RootSignatureDesc rootSignatureDesc = {}; - std::array shaders = { m_lightClusterShader.m_handle, m_clearLightClusterShader.m_handle }; + std::array shaders = { m_lightClusterSpotlightShader.m_handle, + m_lightClusterShader.m_handle, + m_clearLightClusterShader.m_handle }; rootSignatureDesc.ppShaders = shaders.data(); rootSignatureDesc.mShaderCount = shaders.size(); addRootSignature(forgeRenderer->Rend(), &rootSignatureDesc, signature); @@ -268,6 +279,16 @@ namespace hpl { addPipeline(forgeRenderer->Rend(), &pipelineDesc, pipeline); return true; }); + m_spotLightClusterPipeline.Load(forgeRenderer->Rend(), [&](Pipeline** pipeline) { + PipelineDesc pipelineDesc = {}; + pipelineDesc.mType = PIPELINE_TYPE_COMPUTE; + ComputePipelineDesc& computePipelineDesc = pipelineDesc.mComputeDesc; + computePipelineDesc.pShaderProgram = m_lightClusterSpotlightShader.m_handle; + computePipelineDesc.pRootSignature = m_lightClusterRootSignature.m_handle; + addPipeline(forgeRenderer->Rend(), &pipelineDesc, pipeline); + return true; + }); + m_clearClusterPipeline.Load(forgeRenderer->Rend(), [&](Pipeline** pipeline) { PipelineDesc pipelineDesc = {}; pipelineDesc.mType = PIPELINE_TYPE_COMPUTE; @@ -321,12 +342,29 @@ namespace hpl { bufferDesc.mDesc.mSize = bufferDesc.mDesc.mElementCount * bufferDesc.mDesc.mStructStride; bufferDesc.mDesc.mStartState = RESOURCE_STATE_UNORDERED_ACCESS; bufferDesc.pData = nullptr; - bufferDesc.mDesc.pName = "Point Light"; + bufferDesc.mDesc.pName = "PointLight"; bufferDesc.ppBuffer = buffer; addResource(&bufferDesc, nullptr); return true; }); + m_spotlightBuffer[i].Load([&](Buffer** buffer) { + BufferLoadDesc bufferDesc = {}; + bufferDesc.mDesc.mDescriptors = DESCRIPTOR_TYPE_BUFFER_RAW | DESCRIPTOR_TYPE_RW_BUFFER; + bufferDesc.mDesc.mMemoryUsage = RESOURCE_MEMORY_USAGE_CPU_TO_GPU; + bufferDesc.mDesc.mFirstElement = 0; + bufferDesc.mDesc.mStructStride = sizeof(resource::SceneSpotLight); + bufferDesc.mDesc.mElementCount = SpotLightCount; + bufferDesc.mDesc.mSize = bufferDesc.mDesc.mElementCount * bufferDesc.mDesc.mStructStride; + bufferDesc.mDesc.mStartState = RESOURCE_STATE_UNORDERED_ACCESS; + bufferDesc.pData = nullptr; + bufferDesc.mDesc.pName = "SpotLight"; + bufferDesc.ppBuffer = buffer; + addResource(&bufferDesc, nullptr); + + return true; + }); + m_objectUniformBuffer[i].Load([&](Buffer** buffer) { BufferLoadDesc desc = {}; desc.mDesc.mDescriptors = DESCRIPTOR_TYPE_BUFFER; @@ -406,6 +444,7 @@ namespace hpl { { std::array params = { DescriptorData{ .pName = "pointLights", .ppBuffers = &m_pointLightBuffer[swapchainIndex].m_handle }, + DescriptorData{ .pName = "spotLights", .ppBuffers = &m_spotlightBuffer[swapchainIndex].m_handle }, DescriptorData{ .pName = "lightClustersCount", .ppBuffers = &m_lightClusterCountBuffer[swapchainIndex].m_handle }, DescriptorData{ .pName = "lightClusters", .ppBuffers = &m_lightClustersBuffer[swapchainIndex].m_handle }, DescriptorData{ .pName = "sceneObjects", .ppBuffers = &m_objectUniformBuffer[swapchainIndex].m_handle }, @@ -417,6 +456,7 @@ namespace hpl { } { std::array params = { + DescriptorData{ .pName = "spotLights", .ppBuffers = &m_spotlightBuffer[swapchainIndex].m_handle }, DescriptorData{ .pName = "pointLights", .ppBuffers = &m_pointLightBuffer[swapchainIndex].m_handle }, DescriptorData{ .pName = "lightClustersCount", .ppBuffers = &m_lightClusterCountBuffer[swapchainIndex].m_handle }, DescriptorData{ .pName = "lightClusters", .ppBuffers = &m_lightClustersBuffer[swapchainIndex].m_handle }, @@ -562,6 +602,7 @@ namespace hpl { updateDescriptorSet( forgeRenderer->Rend(), 0, m_sceneDescriptorPerFrameSet[frame.m_frameIndex].m_handle, params.size(), params.data()); }); + m_transientImagePool.reset(frame); m_objectDescriptorLookup.clear(); m_indirectDrawIndex = 0; m_objectIndex = 0; @@ -706,7 +747,7 @@ namespace hpl { std::vector translucenctRenderables; RangeSubsetAlloc indirectAllocSession(m_indirectDrawIndex); - + // diffuse - building indirect draw list for (auto& renderable : m_rendererList.GetSolidObjects()) { cMaterial* material = renderable->GetMaterial(); if (!material || renderable->GetCoverageAmount() < 1.0 || cMaterial::IsTranslucent(material->Descriptor().m_id) || @@ -738,6 +779,7 @@ namespace hpl { } RangeSubsetAlloc::RangeSubset opaqueIndirectArgs = indirectAllocSession.End(); + // diffuse build indirect list for opaque for (auto& renderable : translucenctRenderables) { cMaterial* material = renderable->GetMaterial(); if (!material) { @@ -764,11 +806,12 @@ namespace hpl { indirectDrawArgs->mInstanceCount = 1; endUpdateResource(&updateDesc); } + // diffuse translucent indirect RangeSubsetAlloc::RangeSubset translucentIndirectArgs = indirectAllocSession.End(); - { cmdBindRenderTargets(cmd, 0, NULL, NULL, NULL, NULL, NULL, -1, -1); uint32_t pointlightCount = 0; + uint32_t spotlightCount = 0; for(auto& light: m_rendererList.GetLights()) { switch(light->GetLightType()) { case eLightType_Point: { @@ -779,11 +822,38 @@ namespace hpl { auto pointLightData = reinterpret_cast(pointlightUpdateDesc.pMappedData); const cColor color = light->GetDiffuseColor(); pointLightData->m_radius = light->GetRadius(); - pointLightData->m_lightPos = v3ToF3(cMath::ToForgeVec3(light->GetWorldPosition())); + pointLightData->m_worldPos = v3ToF3(cMath::ToForgeVec3(light->GetWorldPosition())); pointLightData->m_lightColor = float4(color.r, color.g, color.b, color.a); endUpdateResource(&pointlightUpdateDesc); break; } + case eLightType_Spot: { + cLightSpot* pLightSpot = static_cast(light); + float fFarHeight = pLightSpot->GetTanHalfFOV() * pLightSpot->GetRadius() * 2.0f; + // Note: Aspect might be wonky if there is no gobo. + float fFarWidth = fFarHeight * pLightSpot->GetAspect(); + cMatrixf mtxDestRender = + cMath::MatrixScale(cVector3f(fFarWidth, fFarHeight, pLightSpot->GetRadius())); // x and y = "far plane", z = radius + cMatrixf mtxDestTransform = cMath::MatrixMul(apFrustum->GetViewMatrix(), pLightSpot->GetWorldMatrix()); + cVector3f forward = cMath::MatrixMul3x3(mtxDestTransform, cVector3f(0, 0, 1)); + const cColor color = light->GetDiffuseColor(); + //mtxDestRender = cMath::MatrixMul(mtxDestTransform, mtxDestRender); + + BufferUpdateDesc spotlightUpdateDesc = { m_spotlightBuffer[frame.m_frameIndex].m_handle, + (spotlightCount++) * sizeof(resource::SceneSpotLight), + sizeof(resource::SceneSpotLight) }; + beginUpdateResource(&spotlightUpdateDesc); + auto spotLightData = reinterpret_cast(spotlightUpdateDesc.pMappedData); + //cVector3f forward = cMath::MatrixMul3x3(light->m_mtxViewSpaceTransform, cVector3f(0, 0, 1)); + spotLightData->m_radius = light->GetRadius(); + spotLightData->m_direction = normalize(float3(forward.x, forward.y, forward.z)); + spotLightData->m_lightColor = float4(color.r, color.g, color.b, color.a); + spotLightData->m_worldPos = v3ToF3(cMath::ToForgeVec3(light->GetWorldPosition())); + spotLightData->m_angle = pLightSpot->GetFOV(); + spotLightData->m_viewProjection = cMath::ToForgeMatrix4(mtxDestTransform); + endUpdateResource(&spotlightUpdateDesc); + break; + } default: break; } @@ -804,7 +874,16 @@ namespace hpl { cmdBindPipeline(cmd, m_pointLightClusterPipeline.m_handle); cmdBindDescriptorSet(cmd, 0, m_lightDescriptorPerFrameSet[frame.m_frameIndex].m_handle); cmdDispatch(cmd, pointlightCount, 1, 1); + // { + // std::array barriers = { BufferBarrier{ m_lightClusterCountBuffer[frame.m_frameIndex].m_handle, + // RESOURCE_STATE_UNORDERED_ACCESS, + // RESOURCE_STATE_UNORDERED_ACCESS } }; + // cmdResourceBarrier( + // cmd, barriers.size(), barriers.data(), 0, nullptr, 0, nullptr); + // } + cmdBindPipeline(cmd, m_spotLightClusterPipeline.m_handle); + cmdDispatch(cmd, spotlightCount, 1, 1); } {