Skip to content

Fixed a vulkan and metal warning in the SSGI compute shader. #715

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions com.unity.render-pipelines.high-definition/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,9 @@ 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.
- 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,12 @@ internal bool IsSSREnabled()
return frameSettings.IsEnabled(FrameSettingsField.SSR) && ssr.enabled.value;
}

internal bool IsSSGIEnabled()
{
var ssgi = volumeStack.GetComponent<GlobalIllumination>();
return frameSettings.IsEnabled(FrameSettingsField.SSGI) && ssgi.enable.value;
}

internal bool IsTransparentSSREnabled()
{
var ssr = volumeStack.GetComponent<ScreenSpaceReflection>();
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down