Skip to content

Commit d573175

Browse files
authored
Fixed a vulkan and metal warning in the SSGI compute shader. (#715)
* - 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 mip was incorrectly copied.
1 parent 86fae39 commit d573175

File tree

4 files changed

+139
-129
lines changed

4 files changed

+139
-129
lines changed

com.unity.render-pipelines.high-definition/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
634634
- Fixed a bug where the light list is not cleared but still used when resizing the RT.
635635
- Fixed exposure debug shader with XR single-pass rendering.
636636
- Fixed issues with scene view and transparent motion vectors.
637+
- Fixed a vulkan and metal warning in the SSGI compute shader.
638+
- Fixed an exception due to the color pyramid not allocated when SSGI is enabled.
639+
- Fixed an issue with the first Depth history was incorrectly copied.
637640

638641
### Changed
639642
- Improve MIP selection for decals on Transparents

com.unity.render-pipelines.high-definition/Runtime/Lighting/ScreenSpaceLighting/ScreenSpaceGlobalIllumination.compute

Lines changed: 128 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -55,136 +55,137 @@ bool RayMarch(float3 positionWS, float3 sampleDir, float3 normalWS, float2 posit
5555
float3 sampledPosNDC = ComputeNormalizedDeviceCoordinatesWithZ(sampledPosWS, UNITY_MATRIX_VP); // Jittered
5656
float3 sampledPosSS = float3(sampledPosNDC.xy * _ScreenSize.xy, sampledPosNDC.z);
5757

58-
// If the point is behind the camera, this ray should not be cast
59-
killRay = killRay || (sampledPosSS.z <= 0);
58+
// Due to a warning on Vulkan and Metal if returning early, this is the only way we found to avoid it.
59+
bool status = false;
6060

61-
// If this ray
62-
if (killRay)
63-
return false;
64-
65-
// We start tracing from the center of the current pixel, and do so up to the far plane.
66-
float3 rayOrigin = float3(positionSS + 0.5, deviceDepth);
67-
68-
// Compute the ray direction in screen space
69-
float3 rayDir = (sampledPosSS - rayOrigin);
70-
71-
// Compute the reciprocal of the direction (not sure why tho ftm)
72-
float3 rcpRayDir = rcp(rayDir);
73-
74-
// Compute a ray step (added an abs here looks better, maybe its wrong need to check mmore)
75-
int2 rayStep = int2((rcpRayDir.x) >= 0 ? 1 : 0,
76-
(rcpRayDir.y) >= 0 ? 1 : 0);
77-
78-
float3 raySign = float3(rcpRayDir.x >= 0 ? 1 : -1,
79-
rcpRayDir.y >= 0 ? 1 : -1,
80-
rcpRayDir.z >= 0 ? 1 : -1);
81-
bool rayTowardsEye = rcpRayDir.z >= 0;
82-
83-
// Build the bounds that start at the center of the pixel and travel to the edge of the screen
84-
float tMax;
85-
{
86-
// Shrink the frustum by half a texel for efficiency reasons.
87-
const float halfTexel = 0.5;
88-
89-
float3 bounds;
90-
bounds.x = clamp(sampledPosSS.x, halfTexel, _ScreenSize.x - halfTexel);
91-
bounds.y = clamp(sampledPosSS.y, halfTexel, _ScreenSize.y - halfTexel);
92-
// If we do not want to intersect the skybox, it is more efficient to not trace too far.
93-
float maxDepth = -0.00000024; // 2^-22
94-
bounds.z = (rcpRayDir.z >= 0) ? 1 : maxDepth;
95-
96-
float3 dist = bounds * rcpRayDir - (rayOrigin * rcpRayDir);
97-
tMax = Min3(dist.x, dist.y, dist.z);
98-
}
99-
100-
// Start ray marching from the next texel to avoid self-intersections.
101-
float t;
61+
// If the point is behind the camera or the ray is invalid, this ray should not be cast
62+
if (!killRay || (sampledPosSS.z <= 0))
10263
{
103-
// 'rayOrigin' is the exact texel center.
104-
float2 dist = abs(0.5 * rcpRayDir.xy);
105-
t = min(dist.x, dist.y);
64+
// We start tracing from the center of the current pixel, and do so up to the far plane.
65+
float3 rayOrigin = float3(positionSS + 0.5, deviceDepth);
66+
67+
// Compute the ray direction in screen space
68+
float3 rayDir = (sampledPosSS - rayOrigin);
69+
70+
// Compute the reciprocal of the direction (not sure why tho ftm)
71+
float3 rcpRayDir = rcp(rayDir);
72+
73+
// Compute a ray step (added an abs here looks better, maybe its wrong need to check mmore)
74+
int2 rayStep = int2((rcpRayDir.x) >= 0 ? 1 : 0,
75+
(rcpRayDir.y) >= 0 ? 1 : 0);
76+
77+
float3 raySign = float3(rcpRayDir.x >= 0 ? 1 : -1,
78+
rcpRayDir.y >= 0 ? 1 : -1,
79+
rcpRayDir.z >= 0 ? 1 : -1);
80+
bool rayTowardsEye = rcpRayDir.z >= 0;
81+
82+
// Build the bounds that start at the center of the pixel and travel to the edge of the screen
83+
float tMax;
84+
{
85+
// Shrink the frustum by half a texel for efficiency reasons.
86+
const float halfTexel = 0.5;
87+
88+
float3 bounds;
89+
bounds.x = clamp(sampledPosSS.x, halfTexel, _ScreenSize.x - halfTexel);
90+
bounds.y = clamp(sampledPosSS.y, halfTexel, _ScreenSize.y - halfTexel);
91+
// If we do not want to intersect the skybox, it is more efficient to not trace too far.
92+
float maxDepth = -0.00000024; // 2^-22
93+
bounds.z = (rcpRayDir.z >= 0) ? 1 : maxDepth;
94+
95+
float3 dist = bounds * rcpRayDir - (rayOrigin * rcpRayDir);
96+
tMax = Min3(dist.x, dist.y, dist.z);
97+
}
98+
99+
// Start ray marching from the next texel to avoid self-intersections.
100+
float t;
101+
{
102+
// 'rayOrigin' is the exact texel center.
103+
float2 dist = abs(0.5 * rcpRayDir.xy);
104+
t = min(dist.x, dist.y);
105+
}
106+
107+
int mipLevel = 0;
108+
int2 mipOffset = _DepthPyramidMipLevelOffsets[mipLevel];
109+
int iterCount = 0;
110+
bool hit = false;
111+
bool miss = false;
112+
bool belowMip0 = false; // This value is set prior to entering the cell
113+
114+
while (!(hit || miss) && (t <= tMax) && (iterCount < _IndirectDiffuseSteps))
115+
{
116+
rayPos = rayOrigin + t * rayDir;
117+
118+
// Ray position often ends up on the edge. To determine (and look up) the right cell,
119+
// we need to bias the position by a small epsilon in the direction of the ray.
120+
float2 sgnEdgeDist = round(rayPos.xy) - rayPos.xy;
121+
float2 satEdgeDist = clamp(raySign.xy * sgnEdgeDist + GI_TRACE_EPS, 0, GI_TRACE_EPS);
122+
rayPos.xy += raySign.xy * satEdgeDist;
123+
124+
int2 mipCoord = (int2)rayPos.xy >> mipLevel;
125+
// Bounds define 4 faces of a cube:
126+
// 2 walls in front of the ray, and a floor and a base below it.
127+
float4 bounds;
128+
129+
bounds.z = LOAD_TEXTURE2D_X(_CameraDepthTexture, mipOffset + mipCoord).r;
130+
bounds.xy = (mipCoord + rayStep) << mipLevel;
131+
132+
// We define the depth of the base as the depth value as:
133+
// b = DeviceDepth((1 + thickness) * LinearDepth(d))
134+
// b = ((f - n) * d + n * (1 - (1 + thickness))) / ((f - n) * (1 + thickness))
135+
// b = ((f - n) * d - n * thickness) / ((f - n) * (1 + thickness))
136+
// b = d / (1 + thickness) - n / (f - n) * (thickness / (1 + thickness))
137+
// b = d * k_s + k_b
138+
bounds.w = bounds.z * _IndirectDiffuseThicknessScale + _IndirectDiffuseThicknessBias;
139+
140+
float4 dist = bounds * rcpRayDir.xyzz - (rayOrigin.xyzz * rcpRayDir.xyzz);
141+
float distWall = min(dist.x, dist.y);
142+
float distFloor = dist.z;
143+
float distBase = dist.w;
144+
145+
// Note: 'rayPos' given by 't' can correspond to one of several depth values:
146+
// - above or exactly on the floor
147+
// - inside the floor (between the floor and the base)
148+
// - below the base
149+
bool belowFloor = rayPos.z < bounds.z;
150+
bool aboveBase = rayPos.z >= bounds.w;
151+
152+
bool insideFloor = belowFloor && aboveBase;
153+
bool hitFloor = (t <= distFloor) && (distFloor <= distWall);
154+
155+
// Game rules:
156+
// * if the closest intersection is with the wall of the cell, switch to the coarser MIP, and advance the ray.
157+
// * if the closest intersection is with the heightmap below, switch to the finer MIP, and advance the ray.
158+
// * if the closest intersection is with the heightmap above, switch to the finer MIP, and do NOT advance the ray.
159+
// Victory conditions:
160+
// * See below. Do NOT reorder the statements!
161+
162+
miss = belowMip0 && insideFloor;
163+
hit = (mipLevel == 0) && (hitFloor || insideFloor);
164+
belowMip0 = (mipLevel == 0) && belowFloor;
165+
166+
// 'distFloor' can be smaller than the current distance 't'.
167+
// We can also safely ignore 'distBase'.
168+
// If we hit the floor, it's always safe to jump there.
169+
// If we are at (mipLevel != 0) and we are below the floor, we should not move.
170+
t = hitFloor ? distFloor : (((mipLevel != 0) && belowFloor) ? t : distWall);
171+
rayPos.z = bounds.z; // Retain the depth of the potential intersection
172+
173+
// Warning: both rays towards the eye, and tracing behind objects has linear
174+
// rather than logarithmic complexity! This is due to the fact that we only store
175+
// the maximum value of depth, and not the min-max.
176+
mipLevel += (hitFloor || belowFloor || rayTowardsEye) ? -1 : 1;
177+
mipLevel = clamp(mipLevel, 0, 6);
178+
mipOffset = _DepthPyramidMipLevelOffsets[mipLevel];
179+
// mipLevel = 0;
180+
181+
iterCount++;
182+
}
183+
184+
// Treat intersections with the sky as misses.
185+
miss = miss || ((rayPos.z == 0));
186+
status = hit && !miss;
106187
}
107-
108-
int mipLevel = 0;
109-
int2 mipOffset = _DepthPyramidMipLevelOffsets[mipLevel];
110-
int iterCount = 0;
111-
bool hit = false;
112-
bool miss = false;
113-
bool belowMip0 = false; // This value is set prior to entering the cell
114-
115-
while (!(hit || miss) && (t <= tMax) && (iterCount < _IndirectDiffuseSteps))
116-
{
117-
rayPos = rayOrigin + t * rayDir;
118-
119-
// Ray position often ends up on the edge. To determine (and look up) the right cell,
120-
// we need to bias the position by a small epsilon in the direction of the ray.
121-
float2 sgnEdgeDist = round(rayPos.xy) - rayPos.xy;
122-
float2 satEdgeDist = clamp(raySign.xy * sgnEdgeDist + GI_TRACE_EPS, 0, GI_TRACE_EPS);
123-
rayPos.xy += raySign.xy * satEdgeDist;
124-
125-
int2 mipCoord = (int2)rayPos.xy >> mipLevel;
126-
// Bounds define 4 faces of a cube:
127-
// 2 walls in front of the ray, and a floor and a base below it.
128-
float4 bounds;
129-
130-
bounds.z = LOAD_TEXTURE2D_X(_CameraDepthTexture, mipOffset + mipCoord).r;
131-
bounds.xy = (mipCoord + rayStep) << mipLevel;
132-
133-
// We define the depth of the base as the depth value as:
134-
// b = DeviceDepth((1 + thickness) * LinearDepth(d))
135-
// b = ((f - n) * d + n * (1 - (1 + thickness))) / ((f - n) * (1 + thickness))
136-
// b = ((f - n) * d - n * thickness) / ((f - n) * (1 + thickness))
137-
// b = d / (1 + thickness) - n / (f - n) * (thickness / (1 + thickness))
138-
// b = d * k_s + k_b
139-
bounds.w = bounds.z * _IndirectDiffuseThicknessScale + _IndirectDiffuseThicknessBias;
140-
141-
float4 dist = bounds * rcpRayDir.xyzz - (rayOrigin.xyzz * rcpRayDir.xyzz);
142-
float distWall = min(dist.x, dist.y);
143-
float distFloor = dist.z;
144-
float distBase = dist.w;
145-
146-
// Note: 'rayPos' given by 't' can correspond to one of several depth values:
147-
// - above or exactly on the floor
148-
// - inside the floor (between the floor and the base)
149-
// - below the base
150-
bool belowFloor = rayPos.z < bounds.z;
151-
bool aboveBase = rayPos.z >= bounds.w;
152-
153-
bool insideFloor = belowFloor && aboveBase;
154-
bool hitFloor = (t <= distFloor) && (distFloor <= distWall);
155-
156-
// Game rules:
157-
// * if the closest intersection is with the wall of the cell, switch to the coarser MIP, and advance the ray.
158-
// * if the closest intersection is with the heightmap below, switch to the finer MIP, and advance the ray.
159-
// * if the closest intersection is with the heightmap above, switch to the finer MIP, and do NOT advance the ray.
160-
// Victory conditions:
161-
// * See below. Do NOT reorder the statements!
162-
163-
miss = belowMip0 && insideFloor;
164-
hit = (mipLevel == 0) && (hitFloor || insideFloor);
165-
belowMip0 = (mipLevel == 0) && belowFloor;
166-
167-
// 'distFloor' can be smaller than the current distance 't'.
168-
// We can also safely ignore 'distBase'.
169-
// If we hit the floor, it's always safe to jump there.
170-
// If we are at (mipLevel != 0) and we are below the floor, we should not move.
171-
t = hitFloor ? distFloor : (((mipLevel != 0) && belowFloor) ? t : distWall);
172-
rayPos.z = bounds.z; // Retain the depth of the potential intersection
173-
174-
// Warning: both rays towards the eye, and tracing behind objects has linear
175-
// rather than logarithmic complexity! This is due to the fact that we only store
176-
// the maximum value of depth, and not the min-max.
177-
mipLevel += (hitFloor || belowFloor || rayTowardsEye) ? -1 : 1;
178-
mipLevel = clamp(mipLevel, 0, 6);
179-
mipOffset = _DepthPyramidMipLevelOffsets[mipLevel];
180-
// mipLevel = 0;
181-
182-
iterCount++;
183-
}
184-
185-
// Treat intersections with the sky as misses.
186-
miss = miss || ((rayPos.z == 0));
187-
return hit && !miss;
188+
return status;
188189
}
189190

190191
[numthreads(INDIRECT_DIFFUSE_TILE_SIZE, INDIRECT_DIFFUSE_TILE_SIZE, 1)]

com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,12 @@ internal bool IsSSREnabled()
363363
return frameSettings.IsEnabled(FrameSettingsField.SSR) && ssr.enabled.value;
364364
}
365365

366+
internal bool IsSSGIEnabled()
367+
{
368+
var ssgi = volumeStack.GetComponent<GlobalIllumination>();
369+
return frameSettings.IsEnabled(FrameSettingsField.SSGI) && ssgi.enable.value;
370+
}
371+
366372
internal bool IsTransparentSSREnabled()
367373
{
368374
var ssr = volumeStack.GetComponent<ScreenSpaceReflection>();
@@ -419,7 +425,7 @@ internal void Update(FrameSettings currentFrameSettings, HDRenderPipeline hdrp,
419425
HDRenderPipeline.ReinitializeVolumetricBufferParams(this);
420426

421427
bool isCurrentColorPyramidRequired = frameSettings.IsEnabled(FrameSettingsField.Refraction) || frameSettings.IsEnabled(FrameSettingsField.Distortion);
422-
bool isHistoryColorPyramidRequired = IsSSREnabled() || antialiasing == AntialiasingMode.TemporalAntialiasing;
428+
bool isHistoryColorPyramidRequired = IsSSREnabled() || IsSSGIEnabled() || antialiasing == AntialiasingMode.TemporalAntialiasing;
423429
bool isVolumetricHistoryRequired = IsVolumetricReprojectionEnabled();
424430

425431
int numColorPyramidBuffersRequired = 0;

com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5193,7 +5193,7 @@ RTHandle Allocator1(string id, int frameIndex, RTHandleSystem rtHandleSystem)
51935193
var mipchainInfo = m_SharedRTManager.GetDepthBufferMipChainInfo();
51945194
depthBuffer1 = hdCamera.GetCurrentFrameRT((int)HDCameraFrameHistoryType.Depth1) ?? hdCamera.AllocHistoryFrameRT((int)HDCameraFrameHistoryType.Depth1, Allocator1, 1);
51955195
for (int i = 0; i < hdCamera.viewCount; i++)
5196-
cmd.CopyTexture(mainDepthBuffer, i, 0, 0, 0, hdCamera.actualWidth / 2, hdCamera.actualHeight / 2, depthBuffer1, i, 0, 0, 0);
5196+
cmd.CopyTexture(mainDepthBuffer, i, 0, mipchainInfo.mipLevelOffsets[1].x, mipchainInfo.mipLevelOffsets[1].y, hdCamera.actualWidth / 2, hdCamera.actualHeight / 2, depthBuffer1, i, 0, 0, 0);
51975197
}
51985198

51995199
// Send buffers to client.

0 commit comments

Comments
 (0)