From f0b0ed7fa32427bdf1cdab3662bfdc51e5d279d1 Mon Sep 17 00:00:00 2001 From: Anis Date: Wed, 3 Jun 2020 14:13:04 +0200 Subject: [PATCH 1/2] - Fixed a vulkan and metal warning in the SSGI compute shader. --- .../CHANGELOG.md | 1 + .../ScreenSpaceGlobalIllumination.compute | 255 +++++++++--------- 2 files changed, 129 insertions(+), 127 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index 69b3a8bb1cd..001548cd370 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -629,6 +629,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed a bug where the light list is not cleared but still used when resizing the RT. - Fixed exposure debug shader with XR single-pass rendering. - Fixed issues with scene view and transparent motion vectors. +- Fixed a vulkan and metal warning in the SSGI compute shader. ### Changed - Improve MIP selection for decals on Transparents diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/ScreenSpaceGlobalIllumination.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/ScreenSpaceGlobalIllumination.compute index 0f241b56bc7..638143883b3 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/ScreenSpaceGlobalIllumination.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/ScreenSpaceGlobalIllumination.compute @@ -55,136 +55,137 @@ bool RayMarch(float3 positionWS, float3 sampleDir, float3 normalWS, float2 posit float3 sampledPosNDC = ComputeNormalizedDeviceCoordinatesWithZ(sampledPosWS, UNITY_MATRIX_VP); // Jittered float3 sampledPosSS = float3(sampledPosNDC.xy * _ScreenSize.xy, sampledPosNDC.z); - // If the point is behind the camera, this ray should not be cast - killRay = killRay || (sampledPosSS.z <= 0); + // Due to a warning on Vulkan and Metal if returning early, this is the only way we found to avoid it. + bool status = false; - // If this ray - if (killRay) - return false; - - // We start tracing from the center of the current pixel, and do so up to the far plane. - float3 rayOrigin = float3(positionSS + 0.5, deviceDepth); - - // Compute the ray direction in screen space - float3 rayDir = (sampledPosSS - rayOrigin); - - // Compute the reciprocal of the direction (not sure why tho ftm) - float3 rcpRayDir = rcp(rayDir); - - // Compute a ray step (added an abs here looks better, maybe its wrong need to check mmore) - int2 rayStep = int2((rcpRayDir.x) >= 0 ? 1 : 0, - (rcpRayDir.y) >= 0 ? 1 : 0); - - float3 raySign = float3(rcpRayDir.x >= 0 ? 1 : -1, - rcpRayDir.y >= 0 ? 1 : -1, - rcpRayDir.z >= 0 ? 1 : -1); - bool rayTowardsEye = rcpRayDir.z >= 0; - - // Build the bounds that start at the center of the pixel and travel to the edge of the screen - float tMax; - { - // Shrink the frustum by half a texel for efficiency reasons. - const float halfTexel = 0.5; - - float3 bounds; - bounds.x = clamp(sampledPosSS.x, halfTexel, _ScreenSize.x - halfTexel); - bounds.y = clamp(sampledPosSS.y, halfTexel, _ScreenSize.y - halfTexel); - // If we do not want to intersect the skybox, it is more efficient to not trace too far. - float maxDepth = -0.00000024; // 2^-22 - bounds.z = (rcpRayDir.z >= 0) ? 1 : maxDepth; - - float3 dist = bounds * rcpRayDir - (rayOrigin * rcpRayDir); - tMax = Min3(dist.x, dist.y, dist.z); - } - - // Start ray marching from the next texel to avoid self-intersections. - float t; + // If the point is behind the camera or the ray is invalid, this ray should not be cast + if (!killRay || (sampledPosSS.z <= 0)) { - // 'rayOrigin' is the exact texel center. - float2 dist = abs(0.5 * rcpRayDir.xy); - t = min(dist.x, dist.y); + // We start tracing from the center of the current pixel, and do so up to the far plane. + float3 rayOrigin = float3(positionSS + 0.5, deviceDepth); + + // Compute the ray direction in screen space + float3 rayDir = (sampledPosSS - rayOrigin); + + // Compute the reciprocal of the direction (not sure why tho ftm) + float3 rcpRayDir = rcp(rayDir); + + // Compute a ray step (added an abs here looks better, maybe its wrong need to check mmore) + int2 rayStep = int2((rcpRayDir.x) >= 0 ? 1 : 0, + (rcpRayDir.y) >= 0 ? 1 : 0); + + float3 raySign = float3(rcpRayDir.x >= 0 ? 1 : -1, + rcpRayDir.y >= 0 ? 1 : -1, + rcpRayDir.z >= 0 ? 1 : -1); + bool rayTowardsEye = rcpRayDir.z >= 0; + + // Build the bounds that start at the center of the pixel and travel to the edge of the screen + float tMax; + { + // Shrink the frustum by half a texel for efficiency reasons. + const float halfTexel = 0.5; + + float3 bounds; + bounds.x = clamp(sampledPosSS.x, halfTexel, _ScreenSize.x - halfTexel); + bounds.y = clamp(sampledPosSS.y, halfTexel, _ScreenSize.y - halfTexel); + // If we do not want to intersect the skybox, it is more efficient to not trace too far. + float maxDepth = -0.00000024; // 2^-22 + bounds.z = (rcpRayDir.z >= 0) ? 1 : maxDepth; + + float3 dist = bounds * rcpRayDir - (rayOrigin * rcpRayDir); + tMax = Min3(dist.x, dist.y, dist.z); + } + + // Start ray marching from the next texel to avoid self-intersections. + float t; + { + // 'rayOrigin' is the exact texel center. + float2 dist = abs(0.5 * rcpRayDir.xy); + t = min(dist.x, dist.y); + } + + int mipLevel = 0; + int2 mipOffset = _DepthPyramidMipLevelOffsets[mipLevel]; + int iterCount = 0; + bool hit = false; + bool miss = false; + bool belowMip0 = false; // This value is set prior to entering the cell + + while (!(hit || miss) && (t <= tMax) && (iterCount < _IndirectDiffuseSteps)) + { + rayPos = rayOrigin + t * rayDir; + + // Ray position often ends up on the edge. To determine (and look up) the right cell, + // we need to bias the position by a small epsilon in the direction of the ray. + float2 sgnEdgeDist = round(rayPos.xy) - rayPos.xy; + float2 satEdgeDist = clamp(raySign.xy * sgnEdgeDist + GI_TRACE_EPS, 0, GI_TRACE_EPS); + rayPos.xy += raySign.xy * satEdgeDist; + + int2 mipCoord = (int2)rayPos.xy >> mipLevel; + // Bounds define 4 faces of a cube: + // 2 walls in front of the ray, and a floor and a base below it. + float4 bounds; + + bounds.z = LOAD_TEXTURE2D_X(_CameraDepthTexture, mipOffset + mipCoord).r; + bounds.xy = (mipCoord + rayStep) << mipLevel; + + // We define the depth of the base as the depth value as: + // b = DeviceDepth((1 + thickness) * LinearDepth(d)) + // b = ((f - n) * d + n * (1 - (1 + thickness))) / ((f - n) * (1 + thickness)) + // b = ((f - n) * d - n * thickness) / ((f - n) * (1 + thickness)) + // b = d / (1 + thickness) - n / (f - n) * (thickness / (1 + thickness)) + // b = d * k_s + k_b + bounds.w = bounds.z * _IndirectDiffuseThicknessScale + _IndirectDiffuseThicknessBias; + + float4 dist = bounds * rcpRayDir.xyzz - (rayOrigin.xyzz * rcpRayDir.xyzz); + float distWall = min(dist.x, dist.y); + float distFloor = dist.z; + float distBase = dist.w; + + // Note: 'rayPos' given by 't' can correspond to one of several depth values: + // - above or exactly on the floor + // - inside the floor (between the floor and the base) + // - below the base + bool belowFloor = rayPos.z < bounds.z; + bool aboveBase = rayPos.z >= bounds.w; + + bool insideFloor = belowFloor && aboveBase; + bool hitFloor = (t <= distFloor) && (distFloor <= distWall); + + // Game rules: + // * if the closest intersection is with the wall of the cell, switch to the coarser MIP, and advance the ray. + // * if the closest intersection is with the heightmap below, switch to the finer MIP, and advance the ray. + // * if the closest intersection is with the heightmap above, switch to the finer MIP, and do NOT advance the ray. + // Victory conditions: + // * See below. Do NOT reorder the statements! + + miss = belowMip0 && insideFloor; + hit = (mipLevel == 0) && (hitFloor || insideFloor); + belowMip0 = (mipLevel == 0) && belowFloor; + + // 'distFloor' can be smaller than the current distance 't'. + // We can also safely ignore 'distBase'. + // If we hit the floor, it's always safe to jump there. + // If we are at (mipLevel != 0) and we are below the floor, we should not move. + t = hitFloor ? distFloor : (((mipLevel != 0) && belowFloor) ? t : distWall); + rayPos.z = bounds.z; // Retain the depth of the potential intersection + + // Warning: both rays towards the eye, and tracing behind objects has linear + // rather than logarithmic complexity! This is due to the fact that we only store + // the maximum value of depth, and not the min-max. + mipLevel += (hitFloor || belowFloor || rayTowardsEye) ? -1 : 1; + mipLevel = clamp(mipLevel, 0, 6); + mipOffset = _DepthPyramidMipLevelOffsets[mipLevel]; + // mipLevel = 0; + + iterCount++; + } + + // Treat intersections with the sky as misses. + miss = miss || ((rayPos.z == 0)); + status = hit && !miss; } - - int mipLevel = 0; - int2 mipOffset = _DepthPyramidMipLevelOffsets[mipLevel]; - int iterCount = 0; - bool hit = false; - bool miss = false; - bool belowMip0 = false; // This value is set prior to entering the cell - - while (!(hit || miss) && (t <= tMax) && (iterCount < _IndirectDiffuseSteps)) - { - rayPos = rayOrigin + t * rayDir; - - // Ray position often ends up on the edge. To determine (and look up) the right cell, - // we need to bias the position by a small epsilon in the direction of the ray. - float2 sgnEdgeDist = round(rayPos.xy) - rayPos.xy; - float2 satEdgeDist = clamp(raySign.xy * sgnEdgeDist + GI_TRACE_EPS, 0, GI_TRACE_EPS); - rayPos.xy += raySign.xy * satEdgeDist; - - int2 mipCoord = (int2)rayPos.xy >> mipLevel; - // Bounds define 4 faces of a cube: - // 2 walls in front of the ray, and a floor and a base below it. - float4 bounds; - - bounds.z = LOAD_TEXTURE2D_X(_CameraDepthTexture, mipOffset + mipCoord).r; - bounds.xy = (mipCoord + rayStep) << mipLevel; - - // We define the depth of the base as the depth value as: - // b = DeviceDepth((1 + thickness) * LinearDepth(d)) - // b = ((f - n) * d + n * (1 - (1 + thickness))) / ((f - n) * (1 + thickness)) - // b = ((f - n) * d - n * thickness) / ((f - n) * (1 + thickness)) - // b = d / (1 + thickness) - n / (f - n) * (thickness / (1 + thickness)) - // b = d * k_s + k_b - bounds.w = bounds.z * _IndirectDiffuseThicknessScale + _IndirectDiffuseThicknessBias; - - float4 dist = bounds * rcpRayDir.xyzz - (rayOrigin.xyzz * rcpRayDir.xyzz); - float distWall = min(dist.x, dist.y); - float distFloor = dist.z; - float distBase = dist.w; - - // Note: 'rayPos' given by 't' can correspond to one of several depth values: - // - above or exactly on the floor - // - inside the floor (between the floor and the base) - // - below the base - bool belowFloor = rayPos.z < bounds.z; - bool aboveBase = rayPos.z >= bounds.w; - - bool insideFloor = belowFloor && aboveBase; - bool hitFloor = (t <= distFloor) && (distFloor <= distWall); - - // Game rules: - // * if the closest intersection is with the wall of the cell, switch to the coarser MIP, and advance the ray. - // * if the closest intersection is with the heightmap below, switch to the finer MIP, and advance the ray. - // * if the closest intersection is with the heightmap above, switch to the finer MIP, and do NOT advance the ray. - // Victory conditions: - // * See below. Do NOT reorder the statements! - - miss = belowMip0 && insideFloor; - hit = (mipLevel == 0) && (hitFloor || insideFloor); - belowMip0 = (mipLevel == 0) && belowFloor; - - // 'distFloor' can be smaller than the current distance 't'. - // We can also safely ignore 'distBase'. - // If we hit the floor, it's always safe to jump there. - // If we are at (mipLevel != 0) and we are below the floor, we should not move. - t = hitFloor ? distFloor : (((mipLevel != 0) && belowFloor) ? t : distWall); - rayPos.z = bounds.z; // Retain the depth of the potential intersection - - // Warning: both rays towards the eye, and tracing behind objects has linear - // rather than logarithmic complexity! This is due to the fact that we only store - // the maximum value of depth, and not the min-max. - mipLevel += (hitFloor || belowFloor || rayTowardsEye) ? -1 : 1; - mipLevel = clamp(mipLevel, 0, 6); - mipOffset = _DepthPyramidMipLevelOffsets[mipLevel]; - // mipLevel = 0; - - iterCount++; - } - - // Treat intersections with the sky as misses. - miss = miss || ((rayPos.z == 0)); - return hit && !miss; + return status; } [numthreads(INDIRECT_DIFFUSE_TILE_SIZE, INDIRECT_DIFFUSE_TILE_SIZE, 1)] From 8f9bf15643cfa4cb206f69248f32e124c7d061b0 Mon Sep 17 00:00:00 2001 From: Anis Date: Thu, 4 Jun 2020 14:31:38 +0200 Subject: [PATCH 2/2] - Fixed an exception due to the color pyramid not allocated when SSGI is enabled. - Fixed an issue with the first depth history mip was incorrectly copied. --- com.unity.render-pipelines.high-definition/CHANGELOG.md | 2 ++ .../Runtime/RenderPipeline/Camera/HDCamera.cs | 8 +++++++- .../Runtime/RenderPipeline/HDRenderPipeline.cs | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index 001548cd370..31f76f3d92d 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -630,6 +630,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed exposure debug shader with XR single-pass rendering. - Fixed issues with scene view and transparent motion vectors. - Fixed a vulkan and metal warning in the SSGI compute shader. +- Fixed an exception due to the color pyramid not allocated when SSGI is enabled. +- Fixed an issue with the first Depth history was incorrectly copied. ### Changed - Improve MIP selection for decals on Transparents diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs index e77c0825e69..ed42a0554e2 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs @@ -363,6 +363,12 @@ internal bool IsSSREnabled() return frameSettings.IsEnabled(FrameSettingsField.SSR) && ssr.enabled.value; } + internal bool IsSSGIEnabled() + { + var ssgi = volumeStack.GetComponent(); + return frameSettings.IsEnabled(FrameSettingsField.SSGI) && ssgi.enable.value; + } + internal bool IsTransparentSSREnabled() { var ssr = volumeStack.GetComponent(); @@ -419,7 +425,7 @@ internal void Update(FrameSettings currentFrameSettings, HDRenderPipeline hdrp, HDRenderPipeline.ReinitializeVolumetricBufferParams(this); bool isCurrentColorPyramidRequired = frameSettings.IsEnabled(FrameSettingsField.Refraction) || frameSettings.IsEnabled(FrameSettingsField.Distortion); - bool isHistoryColorPyramidRequired = IsSSREnabled() || antialiasing == AntialiasingMode.TemporalAntialiasing; + bool isHistoryColorPyramidRequired = IsSSREnabled() || IsSSGIEnabled() || antialiasing == AntialiasingMode.TemporalAntialiasing; bool isVolumetricHistoryRequired = IsVolumetricReprojectionEnabled(); int numColorPyramidBuffersRequired = 0; diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs index a95a9afcc82..39e449f4006 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs @@ -5184,7 +5184,7 @@ RTHandle Allocator1(string id, int frameIndex, RTHandleSystem rtHandleSystem) var mipchainInfo = m_SharedRTManager.GetDepthBufferMipChainInfo(); depthBuffer1 = hdCamera.GetCurrentFrameRT((int)HDCameraFrameHistoryType.Depth1) ?? hdCamera.AllocHistoryFrameRT((int)HDCameraFrameHistoryType.Depth1, Allocator1, 1); for (int i = 0; i < hdCamera.viewCount; i++) - cmd.CopyTexture(mainDepthBuffer, i, 0, 0, 0, hdCamera.actualWidth / 2, hdCamera.actualHeight / 2, depthBuffer1, i, 0, 0, 0); + cmd.CopyTexture(mainDepthBuffer, i, 0, mipchainInfo.mipLevelOffsets[1].x, mipchainInfo.mipLevelOffsets[1].y, hdCamera.actualWidth / 2, hdCamera.actualHeight / 2, depthBuffer1, i, 0, 0, 0); } // Send buffers to client.