diff --git a/WickedEngine/cullingShaderHF.hlsli b/WickedEngine/cullingShaderHF.hlsli index 0cb9fa3d8..6d50fc6b0 100644 --- a/WickedEngine/cullingShaderHF.hlsli +++ b/WickedEngine/cullingShaderHF.hlsli @@ -52,7 +52,7 @@ struct Frustum float4 ClipToView(float4 clip) { // View space position. - float4 view = mul(g_xCamera_InvP, clip); + float4 view = mul(clip, g_xCamera_InvP); // Perspective projection. view = view / view.w; diff --git a/WickedEngine/globals.hlsli b/WickedEngine/globals.hlsli index 748d63908..73ceb22fd 100644 --- a/WickedEngine/globals.hlsli +++ b/WickedEngine/globals.hlsli @@ -133,6 +133,14 @@ inline float2 GetScreenResolution() { return g_xWorld_ScreenWidthHeight; } inline float GetScreenWidth() { return g_xWorld_ScreenWidthHeight.x; } inline float GetScreenHeight() { return g_xWorld_ScreenWidthHeight.y; } inline float GetTime() { return g_xFrame_WindTime; } -inline float GetEmissive(float emissive) { return emissive * 10.0f; }; +inline float GetEmissive(float emissive) { return emissive * 10.0f; } + +struct ComputeShaderInput +{ + uint3 groupID : SV_GroupID; // 3D index of the thread group in the dispatch. + uint3 groupThreadID : SV_GroupThreadID; // 3D index of local thread ID in a thread group. + uint3 dispatchThreadID : SV_DispatchThreadID; // 3D index of global thread ID in the dispatch. + uint groupIndex : SV_GroupIndex; // Flattened local index of the thread within a thread group. +}; #endif // _SHADER_GLOBALS_ \ No newline at end of file diff --git a/WickedEngine/lightCullingCS.hlsl b/WickedEngine/lightCullingCS.hlsl index 5eaff9ae0..ab7182df1 100644 --- a/WickedEngine/lightCullingCS.hlsl +++ b/WickedEngine/lightCullingCS.hlsl @@ -1,13 +1,9 @@ #include "globals.hlsli" #include "cullingShaderHF.hlsli" -groupshared uint minDepthInt; -groupshared uint maxDepthInt; - -RWTexture2D tex : register(u0); +RWTEXTURE2D(DebugTexture, float4, 0); STRUCTUREDBUFFER(in_Frustums, Frustum, SBSLOT_TILEFRUSTUMS); -groupshared Frustum frustum; struct LightArrayType { @@ -18,6 +14,12 @@ struct LightArrayType RWSTRUCTUREDBUFFER(lightArray, LightArrayType, SBSLOT_LIGHTARRAY); #define lightCount ((int)g_xColor.x) + +groupshared uint uMinDepth; +groupshared uint uMaxDepth; +groupshared Frustum GroupFrustum; + + bool intersects(LightArrayType light, Frustum frustum, float minZ, float maxZ) { Sphere sphere; @@ -31,70 +33,146 @@ groupshared uint visibleLightIndices[1024]; #define BLOCK_SIZE 16 [numthreads(BLOCK_SIZE, BLOCK_SIZE, 1)] -void main( - uint3 groupId : SV_GroupID, - uint3 groupThreadId : SV_GroupThreadID, - uint3 dispatchThreadId : SV_DispatchThreadID, // - uint groupIndex : SV_GroupIndex) +void main(ComputeShaderInput IN) { - float depth = texture_lineardepth.Load(uint3(dispatchThreadId.xy, 0)).r / g_xCamera_ZFarP; - uint depthInt = asuint(depth); + // Calculate min & max depth in threadgroup / tile. + int2 texCoord = IN.dispatchThreadID.xy; + float fDepth = texture_depth.Load(int3(texCoord, 0)).r; - if (groupIndex == 0) // Avoid contention by other threads in the group. + uint uDepth = asuint(fDepth); + + if (IN.groupIndex == 0) // Avoid contention by other threads in the group. { - minDepthInt = 0xffffffff; - maxDepthInt = 0; + uMinDepth = 0xffffffff; + uMaxDepth = 0; //o_LightCount = 0; //t_LightCount = 0; - frustum = in_Frustums[groupId.x + (groupId.y * numThreadGroups.x)]; - } - - //minDepthInt = 0xFFFFFFFF; - //maxDepthInt = 0; - - GroupMemoryBarrierWithGroupSync(); - - InterlockedMin(minDepthInt, depthInt); - InterlockedMax(maxDepthInt, depthInt); - - GroupMemoryBarrierWithGroupSync(); - - float minGroupDepth = asfloat(minDepthInt); - float maxGroupDepth = asfloat(maxDepthInt); - - //tex[dispatchThreadId.xy] = minGroupDepth; - - uint threadCount = BLOCK_SIZE*BLOCK_SIZE; - uint passCount = (lightCount + threadCount - 1) / threadCount; - - for (uint passIt = 0; passIt < passCount; ++passIt) - { - uint lightIndex = passIt*threadCount + groupIndex; - - // prevent overrun by clamping to last "null" light - lightIndex = min(lightIndex, lightCount); - - if (intersects(lightArray[lightIndex], frustum, minGroupDepth, maxGroupDepth)) - { - uint offset; - InterlockedAdd(visibleLightCount, 1, offset); - visibleLightIndices[offset] = lightIndex; - } + GroupFrustum = in_Frustums[IN.groupID.x + (IN.groupID.y * numThreadGroups.x)]; } GroupMemoryBarrierWithGroupSync(); - float4 color = 0; - for (uint lightIt = 0; lightIt < visibleLightCount; ++lightIt) - { - uint lightIndex = visibleLightIndices[lightIt]; - LightArrayType light = lightArray[lightIndex]; + InterlockedMin(uMinDepth, uDepth); + InterlockedMax(uMaxDepth, uDepth); - color += light.col; - } - color.a = 1; + GroupMemoryBarrierWithGroupSync(); - color.rgb = frustum.planes[0].N; + float fMinDepth = asfloat(uMinDepth); + float fMaxDepth = asfloat(uMaxDepth); - tex[dispatchThreadId.xy] = color; + // Convert depth values to view space. + float minDepthVS = ScreenToView(float4(0, 0, fMinDepth, 1)).z; + float maxDepthVS = ScreenToView(float4(0, 0, fMaxDepth, 1)).z; + float nearClipVS = ScreenToView(float4(0, 0, 0, 1)).z; + + // Clipping plane for minimum depth value + // (used for testing lights within the bounds of opaque geometry). + Plane minPlane = { float3(0, 0, -1), -minDepthVS }; + + //// Cull lights + //// Each thread in a group will cull 1 light until all lights have been culled. + //for (uint i = IN.groupIndex; i < NUM_LIGHTS; i += BLOCK_SIZE * BLOCK_SIZE) + //{ + // if (Lights[i].Enabled) + // { + // Light light = Lights[i]; + + // switch (light.Type) + // { + // case POINT_LIGHT: + // { + // Sphere sphere = { light.PositionVS.xyz, light.Range }; + // if (SphereInsideFrustum(sphere, GroupFrustum, nearClipVS, maxDepthVS)) + // { + // // Add light to light list for transparent geometry. + // t_AppendLight(i); + + // if (!SphereInsidePlane(sphere, minPlane)) + // { + // // Add light to light list for opaque geometry. + // o_AppendLight(i); + // } + // } + // } + // break; + // case SPOT_LIGHT: + // { + // float coneRadius = tan(radians(light.SpotlightAngle)) * light.Range; + // Cone cone = { light.PositionVS.xyz, light.Range, light.DirectionVS.xyz, coneRadius }; + // if (ConeInsideFrustum(cone, GroupFrustum, nearClipVS, maxDepthVS)) + // { + // // Add light to light list for transparent geometry. + // t_AppendLight(i); + + // if (!ConeInsidePlane(cone, minPlane)) + // { + // // Add light to light list for opaque geometry. + // o_AppendLight(i); + // } + // } + // } + // break; + // case DIRECTIONAL_LIGHT: + // { + // // Directional lights always get added to our light list. + // // (Hopefully there are not too many directional lights!) + // t_AppendLight(i); + // o_AppendLight(i); + // } + // break; + // } + // } + //} + + //// Wait till all threads in group have caught up. + //GroupMemoryBarrierWithGroupSync(); + + //// Update global memory with visible light buffer. + //// First update the light grid (only thread 0 in group needs to do this) + //if (IN.groupIndex == 0) + //{ + // // Update light grid for opaque geometry. + // InterlockedAdd(o_LightIndexCounter[0], o_LightCount, o_LightIndexStartOffset); + // o_LightGrid[IN.groupID.xy] = uint2(o_LightIndexStartOffset, o_LightCount); + + // // Update light grid for transparent geometry. + // InterlockedAdd(t_LightIndexCounter[0], t_LightCount, t_LightIndexStartOffset); + // t_LightGrid[IN.groupID.xy] = uint2(t_LightIndexStartOffset, t_LightCount); + //} + + //GroupMemoryBarrierWithGroupSync(); + + //// Now update the light index list (all threads). + //// For opaque goemetry. + //for (i = IN.groupIndex; i < o_LightCount; i += BLOCK_SIZE * BLOCK_SIZE) + //{ + // o_LightIndexList[o_LightIndexStartOffset + i] = o_LightList[i]; + //} + //// For transparent geometry. + //for (i = IN.groupIndex; i < t_LightCount; i += BLOCK_SIZE * BLOCK_SIZE) + //{ + // t_LightIndexList[t_LightIndexStartOffset + i] = t_LightList[i]; + //} + + //// Update the debug texture output. + //if (IN.groupThreadID.x == 0 || IN.groupThreadID.y == 0) + //{ + // DebugTexture[texCoord] = float4(0, 0, 0, 0.9f); + //} + //else if (IN.groupThreadID.x == 1 || IN.groupThreadID.y == 1) + //{ + // DebugTexture[texCoord] = float4(1, 1, 1, 0.5f); + //} + //else if (o_LightCount > 0) + //{ + // float normalizedLightCount = o_LightCount / 50.0f; + // float4 lightCountHeatMapColor = LightCountHeatMap.SampleLevel(LinearClampSampler, float2(normalizedLightCount, 0), 0); + // DebugTexture[texCoord] = lightCountHeatMapColor; + //} + //else + //{ + // DebugTexture[texCoord] = float4(0, 0, 0, 1); + //} + + //DebugTexture[texCoord] = float4(GroupFrustum.planes[0].N,1); } \ No newline at end of file diff --git a/WickedEngine/tileFrustumsCS.hlsl b/WickedEngine/tileFrustumsCS.hlsl index a3ba72a71..4cf254c7b 100644 --- a/WickedEngine/tileFrustumsCS.hlsl +++ b/WickedEngine/tileFrustumsCS.hlsl @@ -6,26 +6,22 @@ RWSTRUCTUREDBUFFER(out_Frustums, Frustum, SBSLOT_TILEFRUSTUMS); #define BLOCK_SIZE 16 [numthreads(BLOCK_SIZE, BLOCK_SIZE, 1)] -void main( - uint3 groupId : SV_GroupID, - uint3 groupThreadId : SV_GroupThreadID, - uint3 dispatchThreadId : SV_DispatchThreadID, // - uint groupIndex : SV_GroupIndex) +void main(ComputeShaderInput IN) { // View space eye position is always at the origin. const float3 eyePos = float3(0, 0, 0); - // Compute the 4 corner points on the far clipping plane to use as the + // Compute 4 points on the far clipping plane to use as the // frustum vertices. float4 screenSpace[4]; // Top left point - screenSpace[0] = float4(dispatchThreadId.xy * BLOCK_SIZE, 1.0f, 1.0f); + screenSpace[0] = float4(IN.dispatchThreadID.xy * BLOCK_SIZE, 1.0f, 1.0f); // Top right point - screenSpace[1] = float4(float2(dispatchThreadId.x + 1, dispatchThreadId.y) * BLOCK_SIZE, 1.0f, 1.0f); + screenSpace[1] = float4(float2(IN.dispatchThreadID.x + 1, IN.dispatchThreadID.y) * BLOCK_SIZE, 1.0f, 1.0f); // Bottom left point - screenSpace[2] = float4(float2(dispatchThreadId.x, dispatchThreadId.y + 1) * BLOCK_SIZE, 1.0f, 1.0f); + screenSpace[2] = float4(float2(IN.dispatchThreadID.x, IN.dispatchThreadID.y + 1) * BLOCK_SIZE, 1.0f, 1.0f); // Bottom right point - screenSpace[3] = float4(float2(dispatchThreadId.x + 1, dispatchThreadId.y + 1) * BLOCK_SIZE, 1.0f, 1.0f); + screenSpace[3] = float4(float2(IN.dispatchThreadID.x + 1, IN.dispatchThreadID.y + 1) * BLOCK_SIZE, 1.0f, 1.0f); float3 viewSpace[4]; // Now convert the screen space points to view space @@ -47,9 +43,10 @@ void main( frustum.planes[3] = ComputePlane(eyePos, viewSpace[3], viewSpace[2]); // Store the computed frustum in global memory (if our thread ID is in bounds of the grid). - if (dispatchThreadId.x < numThreads.x && dispatchThreadId.y < numThreads.y) + if (IN.dispatchThreadID.x < numThreads.x && IN.dispatchThreadID.y < numThreads.y) { - uint index = dispatchThreadId.x + (dispatchThreadId.y * numThreads.x); + uint index = IN.dispatchThreadID.x + (IN.dispatchThreadID.y * numThreads.x); out_Frustums[index] = frustum; } + //out_Frustums[0].planes[0].N = float3(1, 0, 1); } \ No newline at end of file diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index a13b8d7c2..ac4443f43 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -246,6 +246,7 @@ void wiRenderer::SetUpStaticComponents() GetDevice()->LOCK(); BindPersistentState(GRAPHICSTHREAD_IMMEDIATE); + UpdateWorldCB(GRAPHICSTHREAD_IMMEDIATE); GetDevice()->UNLOCK(); //t1.join(); @@ -1426,13 +1427,16 @@ void wiRenderer::UpdateRenderData(GRAPHICSTHREAD threadID) static ShaderBoneType *bonebuf[GRAPHICSTHREAD_COUNT] = { 0 }; if (bonebuf[threadID] == nullptr) { - bonebuf[threadID] = new ShaderBoneType[maxBoneCount]; + bonebuf[threadID] = (ShaderBoneType*)_mm_malloc(sizeof(ShaderBoneType)*maxBoneCount, 16); } if (mesh->armature->boneCollection.size() > maxBoneCount) { maxBoneCount = (int)mesh->armature->boneCollection.size() * 2; - SAFE_DELETE_ARRAY(bonebuf[threadID]); - bonebuf[threadID] = new ShaderBoneType[maxBoneCount]; + if (bonebuf[threadID] != nullptr) + { + _mm_free(bonebuf[threadID]); + } + bonebuf[threadID] = (ShaderBoneType*)_mm_malloc(sizeof(ShaderBoneType)*maxBoneCount, 16); } for (unsigned int k = 0; k < mesh->armature->boneCollection.size(); k++) { bonebuf[threadID][k].pose = mesh->armature->boneCollection[k]->boneRelativity; @@ -3309,10 +3313,22 @@ Texture2D* wiRenderer::ComputeTiledLightCulling(GRAPHICSTHREAD threadID) int _width = device->GetScreenWidth(); int _height = device->GetScreenHeight(); - int tileCount = (int)(ceilf((float)_width / 16.f) * ceilf((float)_height / 16.f)); + + + // Calc dispatchparams + int _B = 16; + DispatchParamsCB dispatchParams; + dispatchParams.numThreads[0] = (UINT)ceilf((float)_width / _B); + dispatchParams.numThreads[1] = (UINT)ceilf((float)_height / _B); + dispatchParams.numThreads[2] = 1; + dispatchParams.numThreadGroups[0] = (UINT)ceilf((float)dispatchParams.numThreads[0] / _B); + dispatchParams.numThreadGroups[1] = (UINT)ceilf((float)dispatchParams.numThreads[1] / _B); + dispatchParams.numThreadGroups[2] = 1; + device->UpdateBuffer(constantBuffers[CBTYPE_DISPATCHPARAMS], &dispatchParams, threadID); + device->BindConstantBufferCS(constantBuffers[CBTYPE_DISPATCHPARAMS], CB_GETBINDSLOT(DispatchParamsCB), threadID); static GPUBuffer* frustumBuffer = nullptr; - if(frustumBuffer == nullptr) + if (frustumBuffer == nullptr) { frustumBuffer = new GPUBuffer; @@ -3320,7 +3336,7 @@ Texture2D* wiRenderer::ComputeTiledLightCulling(GRAPHICSTHREAD threadID) GPUBufferDesc bd; ZeroMemory(&bd, sizeof(bd)); - bd.ByteWidth = _stride * tileCount; // storing 4 planes for every tile + bd.ByteWidth = _stride * dispatchParams.numThreads[0] * dispatchParams.numThreads[1] * dispatchParams.numThreads[2]; // storing 4 planes for every tile bd.BindFlags = BIND_SHADER_RESOURCE | BIND_UNORDERED_ACCESS; bd.MiscFlags = RESOURCE_MISC_BUFFER_STRUCTURED; bd.Usage = USAGE_DEFAULT; @@ -3330,18 +3346,6 @@ Texture2D* wiRenderer::ComputeTiledLightCulling(GRAPHICSTHREAD threadID) } - // Calc dispatchparams - int _B = 16; - DispatchParamsCB dispatchParams; - dispatchParams.numThreads[0] = (uint32_t)ceilf((float)_width / _B); - dispatchParams.numThreads[1] = (uint32_t)ceilf((float)_height / _B); - dispatchParams.numThreads[2] = 1; - dispatchParams.numThreadGroups[0] = (uint32_t)ceilf((float)dispatchParams.numThreads[0] / _B); - dispatchParams.numThreadGroups[1] = (uint32_t)ceilf((float)dispatchParams.numThreads[1] / _B); - dispatchParams.numThreadGroups[0] = 1; - device->UpdateBuffer(constantBuffers[CBTYPE_DISPATCHPARAMS], &dispatchParams, threadID); - device->BindConstantBufferCS(constantBuffers[CBTYPE_DISPATCHPARAMS], CB_GETBINDSLOT(DispatchParamsCB), threadID); - // calculate the per-tile frustums once: static bool frustumsComplete = false; if(!frustumsComplete) @@ -3349,7 +3353,7 @@ Texture2D* wiRenderer::ComputeTiledLightCulling(GRAPHICSTHREAD threadID) frustumsComplete = true; device->BindUnorderedAccessResourceCS(frustumBuffer, SBSLOT_TILEFRUSTUMS, threadID); device->BindCS(computeShaders[CSTYPE_TILEFRUSTUMS], threadID); - device->Dispatch(dispatchParams.numThreads[0], dispatchParams.numThreads[1], dispatchParams.numThreads[2], threadID); + device->Dispatch(dispatchParams.numThreadGroups[0], dispatchParams.numThreadGroups[1], dispatchParams.numThreadGroups[2], threadID); device->UnBindUnorderedAccessResources(SBSLOT_TILEFRUSTUMS, 1, threadID); } diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h index cc14e5958..f739e0b40 100644 --- a/WickedEngine/wiRenderer.h +++ b/WickedEngine/wiRenderer.h @@ -257,8 +257,8 @@ public: }; GFX_STRUCT DispatchParamsCB { - uint32_t numThreadGroups[3]; uint32_t pad0; - uint32_t numThreads[3]; uint32_t pad1; + UINT numThreadGroups[3]; UINT pad0; + UINT numThreads[3]; UINT pad1; CB_SETBINDSLOT(CBSLOT_RENDERER_DISPATCHPARAMS) @@ -271,6 +271,8 @@ public: XMFLOAT4X4 pose, prev; STRUCTUREDBUFFER_SETBINDSLOT(SBSLOT_BONE) + + ALIGN_16 }; GFX_STRUCT LightArrayType @@ -280,6 +282,8 @@ public: XMFLOAT4 col; STRUCTUREDBUFFER_SETBINDSLOT(SBSLOT_LIGHTARRAY) + + ALIGN_16 }; protected: