From dd03169ae8a72057b31aee98d04f2f831aa39815 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Mon, 20 Jul 2020 16:35:07 -0700 Subject: [PATCH 01/24] Implement clipping and culling (does not consider view frustum corners) --- .../ShaderLibrary/Macros.hlsl | 1 + .../Runtime/Lighting/LightLoop/LightLoop.cs | 9 +- .../Lighting/LightLoop/scrbound.compute | 529 +++++++++++++++++- 3 files changed, 533 insertions(+), 6 deletions(-) diff --git a/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl b/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl index c318581e16b..3981ef50a88 100644 --- a/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl +++ b/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl @@ -46,6 +46,7 @@ #define HALF_MIN_SQRT 0.0078125 // 2^-7 == sqrt(HALF_MIN), useful for ensuring HALF_MIN after x^2 #define HALF_MAX 65504.0 #define UINT_MAX 0xFFFFFFFFu +#define INT_MAX 0x7FFFFFFF #ifdef SHADER_API_GLES diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs index eb8ef22ab93..f171399ed1c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs @@ -1780,9 +1780,12 @@ void GetLightVolumeDataAndBound(LightCategory lightCategory, GPULightType gpuLig } else if (gpuLightType == GPULightType.Point) { - Vector3 vx = xAxisVS; - Vector3 vy = yAxisVS; - Vector3 vz = zAxisVS; + // Construct a view-space axis-aligned bounding cube around the bounding sphere. + // This allows us to utilize the same polygon clipping technique for all lights. + // Non-axis-aligned vectors may result in a larger screen-space AABB. + Vector3 vx = new Vector3(1, 0, 0); + Vector3 vy = new Vector3(0, 1, 0); + Vector3 vz = new Vector3(0, 0, 1); bound.center = positionVS; bound.boxAxisX = vx * range; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index be07511307d..355de58e942 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -11,6 +11,7 @@ #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightCullUtils.hlsl" +// #pragma enable_d3d11_debug_symbols #pragma only_renderers d3d11 playstation xboxone vulkan metal switch StructuredBuffer g_data : register( t0 ); @@ -20,6 +21,288 @@ StructuredBuffer g_data : register( t0 ); // output buffer RWStructuredBuffer g_vBoundsBuffer : register( u0 ); +#define DUMB_COMPILER +// #define USE_WAVE_INTRINSICS // We use TGSM and atomic operations if wave intrinsics are not supported + +#ifdef Z_BINNING + +// Computes r=(n/d) and rounds the result towards the largest adjacent integer. +uint DivRoundUp(uint n, uint d) +{ + return (n + d - 1) / d; // No division by 0 checks +} + +// Returns the location of the N-th set bit starting from the lowest order bit and working upward. +// Slow implementation - do not use for large bit sets. +// Could be optimized - see https://graphics.stanford.edu/~seander/bithacks.html +uint NthBitLow(uint value, uint n) +{ + uint b = -1; // Consistent with the behavior of firstbitlow() + uint c = countbits(value); + + if (n < c) // Validate inputs + { + uint r = n + 1; // Compute the number of remaining bits + + do + { + uint f = firstbitlow(value >> (b + 1)); // Find the next set bit + b += f + r; // Make a guess (assume all [b+f+1,b+f+r] bits are set) + c = countbits(value << (32 - (b + 1))); // Count the number of bits actually set + r = (n + 1) - c; // Compute the number of remaining bits + } while (r > 0); + } + + return b; +} + +// Clipping a plane by a cube may produce a hexagon (6-gon). +// Clipping a hexagon by 4 planes may produce a decagon (10-gon). +#define MAX_CLIP_VERTS (10) +#define NUM_EDGES (12) +#define NUM_VERTS (8) +#define NUM_FACES (6) +#define NUM_PLANES (6) +#define THREADS_PER_LIGHT (4) +#define THREADS_PER_GROUP (64) +#define LIGHTS_PER_GROUP (THREADS_PER_GROUP / THREADS_PER_LIGHT) +#define VERTS_PER_GROUP (NUM_VERTS * LIGHTS_PER_GROUP) +#define VERTS_PER_THREAD (NUM_VERTS / THREADS_PER_LIGHT) +#define FACES_PER_THREAD DivRoundUp(NUM_FACES, THREADS_PER_LIGHT) + +// All planes and faces are always in the standard order (see below). +#define FACE_LEFT (1 << 0) // x = -1 +#define FACE_RIGHT (1 << 1) // x = +1 +#define FACE_FRONT (1 << 2) // y = -1 +#define FACE_BACK (1 << 3) // y = +1 +#define FACE_TOP (1 << 4) // z = -1 +#define FACE_BOTTOM (1 << 5) // z = +1 +#define FACE_MASK ((1 << NUM_FACES) - 1) + +// TODO: the compiler generates 'tbuffer_load_format_x' instructions +// when we access the look-up tables. Can we avoid this? + +// All vertices are always in the standard order (see below). +static const uint s_FaceMasksOfVerts[NUM_VERTS] = +{ + FACE_LEFT | FACE_FRONT | FACE_TOP, // 0: (-1, -1, -1) + FACE_RIGHT | FACE_FRONT | FACE_TOP, // 1: (+1, -1, -1) + FACE_RIGHT | FACE_BACK | FACE_TOP, // 2: (+1, +1, -1) + FACE_LEFT | FACE_BACK | FACE_TOP, // 3: (-1, +1, -1) + FACE_LEFT | FACE_FRONT | FACE_BOTTOM, // 4: (-1, -1, +1) + FACE_RIGHT | FACE_FRONT | FACE_BOTTOM, // 5: (+1, -1, +1) + FACE_RIGHT | FACE_BACK | FACE_BOTTOM, // 6: (+1, +1, +1) + FACE_LEFT | FACE_BACK | FACE_BOTTOM // 7: (-1, +1, +1) +}; + +// CCW order (starting with the LSB) of vertices for each face (w.r.t. its normal), +// with normals pointing in the interior of the volume. +static const uint s_VertMasksOfFaces[NUM_FACES] = +{ + 3 << 9 | 7 << 6 | 4 << 3 | 0 << 0, // 0: FACE_LEFT + 5 << 9 | 6 << 6 | 2 << 3 | 1 << 0, // 1: FACE_RIGHT + 4 << 9 | 5 << 6 | 1 << 3 | 0 << 0, // 2: FACE_FRONT + 2 << 9 | 6 << 6 | 7 << 3 | 3 << 0, // 3: FACE_BACK + 1 << 9 | 2 << 6 | 3 << 3 | 0 << 0, // 4: FACE_TOP + 7 << 9 | 6 << 6 | 5 << 3 | 4 << 0 // 5: FACE_BOTTOM +}; + +// 5 arrays * 128 elements * 4 bytes each = 2560 bytes. +groupshared float gs_HapVertsX[VERTS_PER_GROUP]; +groupshared float gs_HapVertsY[VERTS_PER_GROUP]; +groupshared float gs_HapVertsZ[VERTS_PER_GROUP]; +groupshared float gs_HapVertsW[VERTS_PER_GROUP]; +groupshared uint gs_BehindMasksOfVerts[VERTS_PER_GROUP]; // 6 planes each (HLSL does not support small data types) + +#ifndef USE_WAVE_INTRINSICS +// 1 array * 16 elements * 4 bytes each = 64 bytes. +groupshared uint gs_CullClipFaceMasks[LIGHTS_PER_GROUP]; // 6 faces each (HLSL does not support small data types) + +// 6 arrays * 16 elements * 4 bytes each = 384 bytes. +// Note that these are actually floats reinterpreted as uints. +// The reason is because floating-point atomic operations are not supported. +groupshared uint gs_RapAaBbMinPtX[LIGHTS_PER_GROUP]; +groupshared uint gs_RapAaBbMaxPtX[LIGHTS_PER_GROUP]; +groupshared uint gs_RapAaBbMinPtY[LIGHTS_PER_GROUP]; +groupshared uint gs_RapAaBbMaxPtY[LIGHTS_PER_GROUP]; +groupshared uint gs_RapAaBbMinPtZ[LIGHTS_PER_GROUP]; +groupshared uint gs_RapAaBbMaxPtZ[LIGHTS_PER_GROUP]; +#endif // USE_WAVE_INTRINSICS + +// Returns 'true' if it manages to cull the face. +bool TryCullFace(uint f, uint behindMasksOfVerts[NUM_VERTS]) +{ + uint cullMaskOfFace = FACE_MASK; // Initially behind + uint vertMaskOfFace = s_VertMasksOfFaces[f]; + + for (int j = 0; j < 4; j++) + { + uint v = BitFieldExtract(vertMaskOfFace, 3 * j, 3); + // Non-zero if ALL the vertices are behind any of the planes. + cullMaskOfFace &= behindMasksOfVerts[v]; + } + + return (cullMaskOfFace != 0); +} + +struct ClipVertex +{ + float4 pt; // Homogeneous coordinate after perspective + float bc; // Boundary coordinate with respect to the plane 'p' +}; + +ClipVertex CreateClipVertex(uint p, float4 v) +{ + bool evenPlane = (p % 2) == 0; + + float c = v[p / 2]; + float w = v.w; + + ClipVertex cv; + + cv.pt = v; + cv.bc = evenPlane ? c : w - c; // dot(PlaneEquation, HapVertex); + + return cv; +} + +float4 IntersectEdgeAgainstPlane(ClipVertex v0, ClipVertex v1) +{ + float alpha = saturate(v0.bc * rcp(v0.bc - v1.bc)); // Guaranteed to lie between 0 and 1 + + return lerp(v0.pt, v1.pt, alpha); +} + +void ClipPolygonAgainstPlane(uint p, uint srcBegin, uint srcSize, + inout float4 vertRingBuffer[MAX_CLIP_VERTS], + out uint dstBegin, out uint dstSize) +{ + dstBegin = srcBegin + srcSize; // Start at the end; we don't use modular arithmetic here + dstSize = 0; + + ClipVertex tailVert = CreateClipVertex(p, vertRingBuffer[(srcBegin + srcSize - 1) % MAX_CLIP_VERTS]); + +#ifdef DUMB_COMPILER + uint modSrcIdx = srcBegin % MAX_CLIP_VERTS; + uint modDstIdx = dstBegin % MAX_CLIP_VERTS; +#endif + + for (uint k = srcBegin; k < (srcBegin + srcSize); k++) + { + #ifndef DUMB_COMPILER + uint modSrcIdx = k % MAX_CLIP_VERTS; + #endif + ClipVertex leadVert = CreateClipVertex(p, vertRingBuffer[modSrcIdx]); + + // Execute Blinn's line clipping algorithm. + // Classify the line segment. 4 cases: + // 0. v0 out, v1 out -> add nothing + // 1. v0 in, v1 out -> add intersection + // 2. v0 out, v1 in -> add intersection, add v1 + // 3. v0 in, v1 in -> add v1 + // (bc >= 0) <-> in, (bc < 0) <-> out. Beware of the signed zero. + + if ((tailVert.bc >= 0) != (leadVert.bc >= 0)) + { + // The line segment is guaranteed to cross the plane. + float4 clipVert = IntersectEdgeAgainstPlane(tailVert, leadVert); + #ifndef DUMB_COMPILER + uint modDstIdx = (dstBegin + dstSize++) % MAX_CLIP_VERTS; + #endif + vertRingBuffer[modDstIdx] = clipVert; + #ifdef DUMB_COMPILER + dstSize++; + modDstIdx++; + modDstIdx = (modDstIdx == MAX_CLIP_VERTS) ? 0 : modDstIdx; + #endif + } + + if (leadVert.bc >= 0) + { + #ifndef DUMB_COMPILER + uint modDstIdx = (dstBegin + dstSize++) % MAX_CLIP_VERTS; + #endif + vertRingBuffer[modDstIdx] = leadVert.pt; + #ifdef DUMB_COMPILER + dstSize++; + modDstIdx++; + modDstIdx = (modDstIdx == MAX_CLIP_VERTS) ? 0 : modDstIdx; + #endif + } + + #ifdef DUMB_COMPILER + modSrcIdx++; + modSrcIdx = (modSrcIdx == MAX_CLIP_VERTS) ? 0 : modSrcIdx; + #endif + tailVert = leadVert; // Avoid recomputation and overwriting the vertex in the ring buffer + } +} + +void ClipFaceAgainstViewVolumeAndUpdateAaBb(uint f, uint behindMasksOfVerts[NUM_VERTS], uint firstVertexOffset, + inout float3 rapAaBbMinPt, inout float3 rapAaBbMaxPt) +{ + float4 vertRingBuffer[MAX_CLIP_VERTS]; + uint srcBegin = 0, srcSize = 4; + + uint clipMaskOfFace = 0; // Initially in front + uint vertMaskOfFace = s_VertMasksOfFaces[f]; + + for (int j = 0; j < 4; j++) + { + uint v = BitFieldExtract(vertMaskOfFace, 3 * j, 3); + // Non-zero if ANY of the vertices are behind any of the planes. + clipMaskOfFace |= behindMasksOfVerts[v]; + + // Note that not all edges may require clipping. However, + // filtering the vertex list is somewhat expensive, so we currently don't do it. + vertRingBuffer[j].x = gs_HapVertsX[firstVertexOffset + v]; + vertRingBuffer[j].y = gs_HapVertsY[firstVertexOffset + v]; + vertRingBuffer[j].z = gs_HapVertsZ[firstVertexOffset + v]; + vertRingBuffer[j].w = gs_HapVertsW[firstVertexOffset + v]; + } + + const uint numPlanesToClipAgainst = countbits(clipMaskOfFace); // [1, 6] + + // Sutherland-Hodgeman polygon clipping algorithm. + // It works by clipping the entire polygon against one clipping plane at a time. + for (uint j = 0; j < numPlanesToClipAgainst; j++) + { + uint p = firstbitlow(clipMaskOfFace); + + uint dstBegin, dstSize; + ClipPolygonAgainstPlane(p, srcBegin, srcSize, vertRingBuffer, dstBegin, dstSize); + + srcBegin = dstBegin; + srcSize = dstSize; + + clipMaskOfFace ^= 1 << p; // Clear the bit to continue using firstbitlow() + } + +#ifdef DUMB_COMPILER + uint modSrcIdx = srcBegin % MAX_CLIP_VERTS; +#endif + + for (int j = srcBegin; j < (srcBegin + srcSize); j++) + { + #ifndef DUMB_COMPILER + uint modSrcIdx = j % MAX_CLIP_VERTS; + #endif + + float4 hapVert = vertRingBuffer[modSrcIdx]; + float3 rapVert = hapVert.xyz * rcp(hapVert.w); + + rapAaBbMinPt = min(rapAaBbMinPt, rapVert); + rapAaBbMaxPt = max(rapAaBbMaxPt, rapVert); + + #ifdef DUMB_COMPILER + modSrcIdx++; + modSrcIdx = (modSrcIdx == MAX_CLIP_VERTS) ? 0 : modSrcIdx; + #endif + } +} + +#else // !Z_BINNING + #define MAX_PNTS 9 // strictly this should be 10=6+4 but we get more wavefronts and 10 seems to never hit (fingers crossed) // However, worst case the plane that would be skipped if such an extreme case ever happened would be backplane // clipping gets skipped which doesn't cause any errors. @@ -39,6 +322,7 @@ void CalcBound(out bool2 bIsMinValid, out bool2 bIsMaxValid, out float2 vMin, ou #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightingConvexHullUtils.hlsl" +#endif // Z_BINNING [numthreads(NR_THREADS, 1, 1)] void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID) @@ -54,13 +338,248 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI unsigned int g = groupID; unsigned int t = threadID; - const int subLigt = (int) (t/8); - const int lgtIndex = subLigt+(int) g*8; - const int sideIndex = (int) (t%8); + const int subLigt = (uint) (t/8); + const int lgtIndex = subLigt+(uint) g*8; + const int sideIndex = (uint) (t%8); const int eyeAdjustedLgtIndex = GenerateLightCullDataIndex(lgtIndex, g_iNrVisibLights, eyeIndex); SFiniteLightBound lgtDat = g_data[eyeAdjustedLgtIndex]; +#ifdef Z_BINNING + //********************************************************************************************** + // The goal of this program is to compute the AABB of the light in the NDC space ([0, 1] range). + // The light is represented by a convex volume (a cuboid) with 6 faces (planar quads) and 8 vertices. + // + // Since a light volume may be partially off-screen, we must clip it before computing the AABB. + // Clipping the resulting AABB (rather than the light volume itself) may result in a loose AABB. + // + // To avoid having to deal with toroidal properties of the perspective transform, + // we perform clipping using the homogeneous (projective) post-perspective coordinates. + // This clipping method in described in Blinn's paper titled "Line Clipping". + // + // The algorithm processes a light on 4 threads. While all 6 faces may require clipping in the + // worst case, clipping more than 4 faces is very uncommon (typically, we clip 0, 3 or 4). + // Note that some faces may require culling rather than clipping (the former is simpler). + // + // It's important to realize that face culling may end up culling 5 (or even all 6) faces. + // This means that the clipped light volume may be reduced to a single polygon, or nothing at all. + // (Imagine a view volume completely or partially inside a light volume). + // Therefore, we must perform view-volume-corner-inside-light-volume tests. + // + // + // Notation: + // rbp - real (3D) coordinates before perspective + // hbp - hom. (4D) coordinates before perspective + // hap - hom. (4D) coordinates after perspective + // rap - real (3D) coordinates after perspective (after division by w) + // ********************************************************************************************* + + const uint groupLocalLightIndex = t / THREADS_PER_LIGHT; + const uint firstVertexOffset = NUM_VERTS * groupLocalLightIndex; + + const float2 scale = lgtDat.scaleXY.xy; + const float3 rbpC = lgtDat.center.xyz; + const float3 rbpX = lgtDat.boxAxisX.xyz; + const float3 rbpY = lgtDat.boxAxisY.xyz; + const float3 rbpZ = lgtDat.boxAxisZ.xyz; + +#ifndef USE_WAVE_INTRINSICS + // Initialize the TGSM. All threads write the same value -> no data races. + // The hardware will coalesce the writes. + gs_CullClipFaceMasks[groupLocalLightIndex] = 0; // Initially inside + gs_RapAaBbMinPtX[groupLocalLightIndex] = asuint(1.0f); + gs_RapAaBbMaxPtX[groupLocalLightIndex] = asuint(0.0f); + gs_RapAaBbMinPtY[groupLocalLightIndex] = asuint(1.0f); + gs_RapAaBbMaxPtY[groupLocalLightIndex] = asuint(0.0f); + gs_RapAaBbMinPtZ[groupLocalLightIndex] = asuint(1.0f); + gs_RapAaBbMaxPtZ[groupLocalLightIndex] = asuint(0.0f); +#endif // USE_WAVE_INTRINSICS + + float3 rapAaBbMinPt = 1; + float3 rapAaBbMaxPt = 0; + + // We must determine whether we have to clip or cull any of the faces. + // If all vertices of a face are inside with respect to all the culling planes, + // we can trivially accept that face. If all vertices of a face are behind + // any single plane, we can trivially reject (cull) that face. + uint cullClipFaceMask = 0; // Initially inside + + // (1) Compute the vertices of the light volume. + for (uint i = 0; i < VERTS_PER_THREAD; i++) + { + uint v = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; + + // rbpVerts[0] = rbpC - rbpX * scale.x - rbpY * scale.y - rbpZ; // (-1, -1, -1) + // rbpVerts[1] = rbpC + rbpX * scale.x - rbpY * scale.y - rbpZ; // (+1, -1, -1) + // rbpVerts[2] = rbpC + rbpX * scale.x + rbpY * scale.y - rbpZ; // (+1, +1, -1) + // rbpVerts[3] = rbpC - rbpX * scale.x + rbpY * scale.y - rbpZ; // (-1, +1, -1) + // rbpVerts[4] = rbpC - rbpX - rbpY + rbpZ; // (-1, -1, +1) + // rbpVerts[5] = rbpC + rbpX - rbpY + rbpZ; // (+1, -1, +1) + // rbpVerts[6] = rbpC + rbpX + rbpY + rbpZ; // (+1, +1, +1) + // rbpVerts[7] = rbpC - rbpX + rbpY + rbpZ; // (-1, +1, +1) + + float3 m; // See the comment above + + m.x = (countbits(v % 4) == 1) ? 1 : -1; + m.y = (v & 2 != 0) ? 1 : -1; + m.z = (v >= 4) ? 1 : -1; + + m.xy *= (v >= 4) ? 1 : scale; + + float3 rbpVert = rbpC + m.x * rbpX + m.y * rbpY + m.z * rbpZ; + float4 hapVert = mul(g_mProjection, float4(rbpVert, 1)); + + // Make sure the W component is strictly positive. + // It is helpful in order to simplify clipping and to avoid perspective division by 0. + float w = hapVert.w; + float s = (w >= 0) ? 1 : -1; + + // Transform the X and Y components: [-w, w] -> [0, w]. + hapVert.x = (0.5 * s) * hapVert.x + ((0.5 * s) * w); + hapVert.y = (0.5 * s) * hapVert.y + ((0.5 * s) * w); + hapVert.z = s * hapVert.z; + hapVert.w = max(abs(w), FLT_MIN); + + // For each vertex, we must determine whether it is within the bounds. + // For culling and clipping, we must know, per culling plane, whether the vertex + // is in the positive or the negative half-space. + uint behindMask = 0; // Initially in front + + // Consider the vertex to be inside the view volume if: + // 0 <= x <= w + // 0 <= y <= w + // 0 <= z <= w + w = hapVert.w; + + for (uint j = 0; j < (NUM_PLANES / 2); j++) + { + behindMask |= (hapVert[j] < 0 ? 1 : 0) << (2 * j + 0); // Planes crossing '0' + behindMask |= (hapVert[j] > w ? 1 : 0) << (2 * j + 1); // Planes crossing 'w' + } + + if (behindMask == 0) // Inside? + { + float3 rapVert = hapVert.xyz * rcp(hapVert.w); + + rapAaBbMinPt = min(rapAaBbMinPt, rapVert); + rapAaBbMaxPt = max(rapAaBbMaxPt, rapVert); + } + else // Outside + { + cullClipFaceMask |= s_FaceMasksOfVerts[v]; + } + + gs_HapVertsX[firstVertexOffset + v] = hapVert.x; + gs_HapVertsY[firstVertexOffset + v] = hapVert.y; + gs_HapVertsZ[firstVertexOffset + v] = hapVert.z; + gs_HapVertsW[firstVertexOffset + v] = hapVert.w; + gs_BehindMasksOfVerts[firstVertexOffset + v] = behindMask; + } + +#ifdef USE_WAVE_INTRINSICS + // ... +#else + InterlockedOr(gs_CullClipFaceMasks[groupLocalLightIndex], cullClipFaceMask); + + GroupMemoryBarrierWithGroupSync(); + + cullClipFaceMask = gs_CullClipFaceMasks[groupLocalLightIndex]; +#endif + + if (cullClipFaceMask != 0) + { + // The light may be partially outside the view volume. + } + + uint behindMasksOfVerts[NUM_VERTS]; + + for (uint i = 0; i < NUM_VERTS; i++) + { + behindMasksOfVerts[i] = gs_BehindMasksOfVerts[firstVertexOffset + i]; + } + + // (2) Cull the faces. + const uint cullFaceMask = cullClipFaceMask; + const uint numFacesToCull = countbits(cullFaceMask); // [0, 6] + + for (uint i = 0; i < FACES_PER_THREAD; i++) + { + uint n = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; + + if (n < numFacesToCull) + { + uint f = NthBitLow(cullFaceMask, n); + + if (TryCullFace(f, behindMasksOfVerts)) + { + cullClipFaceMask ^= 1 << f; // Clear the bit + } + } + } + +#ifdef USE_WAVE_INTRINSICS + // ... +#else + InterlockedAnd(gs_CullClipFaceMasks[groupLocalLightIndex], cullClipFaceMask); + + GroupMemoryBarrierWithGroupSync(); + + cullClipFaceMask = gs_CullClipFaceMasks[groupLocalLightIndex]; +#endif + + // (3) Clip the faces. + const uint clipFaceMask = cullClipFaceMask; + const uint numFacesToClip = countbits(clipFaceMask); // [0, 6] + + for (uint i = 0; i < FACES_PER_THREAD; i++) + { + uint n = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; + + if (n < numFacesToCull) + { + uint f = NthBitLow(clipFaceMask, n); + + ClipFaceAgainstViewVolumeAndUpdateAaBb(f, behindMasksOfVerts, firstVertexOffset, + rapAaBbMinPt, rapAaBbMaxPt); + } + } + +#ifdef USE_WAVE_INTRINSICS + // ... +#else + // Integer comparison works for floating-point numbers as long as the sign bit is 0. + // We must take care of the signed zero ourselves. + InterlockedMin(gs_RapAaBbMinPtX[groupLocalLightIndex], asuint(rapAaBbMinPt.x) & INT_MAX); + InterlockedMax(gs_RapAaBbMaxPtX[groupLocalLightIndex], asuint(rapAaBbMaxPt.x) & INT_MAX); + InterlockedMin(gs_RapAaBbMinPtY[groupLocalLightIndex], asuint(rapAaBbMinPt.y) & INT_MAX); + InterlockedMax(gs_RapAaBbMaxPtY[groupLocalLightIndex], asuint(rapAaBbMaxPt.y) & INT_MAX); + InterlockedMin(gs_RapAaBbMinPtZ[groupLocalLightIndex], asuint(rapAaBbMinPt.z) & INT_MAX); + InterlockedMax(gs_RapAaBbMaxPtZ[groupLocalLightIndex], asuint(rapAaBbMaxPt.z) & INT_MAX); + + GroupMemoryBarrierWithGroupSync(); + + rapAaBbMinPt.x = asfloat(gs_RapAaBbMinPtX[groupLocalLightIndex]); + rapAaBbMaxPt.x = asfloat(gs_RapAaBbMaxPtX[groupLocalLightIndex]); + rapAaBbMinPt.y = asfloat(gs_RapAaBbMinPtY[groupLocalLightIndex]); + rapAaBbMaxPt.y = asfloat(gs_RapAaBbMaxPtY[groupLocalLightIndex]); + rapAaBbMinPt.z = asfloat(gs_RapAaBbMinPtZ[groupLocalLightIndex]); + rapAaBbMaxPt.z = asfloat(gs_RapAaBbMaxPtZ[groupLocalLightIndex]); +#endif // USE_WAVE_INTRINSICS + + if (t % THREADS_PER_LIGHT == 0) + { + // Each light's AABB is represented by two float3s, the min and max of the box. + // And for stereo, we have two sets of lights. Therefore, each eye has a set of mins, followed by + // a set of maxs, and each set is equal to g_iNrVisibLights. + const ScreenSpaceBoundsIndices boundsIndices = GenerateScreenSpaceBoundsIndices(lgtIndex, g_iNrVisibLights, eyeIndex); + + float minLinearDepth = -1, maxLinearDepth = -1; // TODO + + g_vBoundsBuffer[boundsIndices.min] = float4(rapAaBbMinPt, minLinearDepth); + g_vBoundsBuffer[boundsIndices.max] = float4(rapAaBbMaxPt, maxLinearDepth); + } + +#else // !Z_BINNING const float3 boxX = lgtDat.boxAxisX.xyz; const float3 boxY = lgtDat.boxAxisY.xyz; const float3 boxZ = -lgtDat.boxAxisZ.xyz; // flip axis (so it points away from the light direction for a spot-light) @@ -385,8 +904,10 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI g_vBoundsBuffer[boundsIndices.max] = float4(0.5*vMax.x + 0.5, 0.5*vMax.y + 0.5, vMax.z*VIEWPORT_SCALE_Z, linMaZ); } } +#endif // Z_BINNING } +#ifndef Z_BINNING float4 GenNewVert(const float4 vVisib, const float4 vInvisib, const int p); @@ -531,3 +1052,5 @@ void CalcBound(out bool2 bIsMinValid, out bool2 bIsMaxValid, out float2 vMin, ou vMin = B; vMax = A; } + +#endif // !Z_BINNING \ No newline at end of file From d3b4b95d67bf81a61faa1267f6613bb4d352c7f1 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Wed, 5 Aug 2020 14:04:30 -0700 Subject: [PATCH 02/24] Support orthographic projection --- .../Runtime/Lighting/LightLoop/scrbound.compute | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 355de58e942..7ac90c4d52c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -431,7 +431,8 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // Make sure the W component is strictly positive. // It is helpful in order to simplify clipping and to avoid perspective division by 0. - float w = hapVert.w; + // For the orthographic projection, we only consider (w = 1) + float w = g_isOrthographic ? 1 : hapVert.w; float s = (w >= 0) ? 1 : -1; // Transform the X and Y components: [-w, w] -> [0, w]. From 10a585e7fa5212076384a5c875c703a24ed813f9 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Wed, 5 Aug 2020 15:52:27 -0700 Subject: [PATCH 03/24] Turn 'scaleXY' into a scalar --- .../Runtime/Lighting/LightLoop/LightLoop.cs | 30 +++++++++---------- .../Lighting/LightLoop/LightLoop.cs.hlsl | 2 +- .../Lighting/LightLoop/scrbound.compute | 8 ++--- .../Runtime/Material/Decal/DecalSystem.cs | 6 ++-- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs index f171399ed1c..57f186dbb82 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs @@ -130,8 +130,8 @@ struct SFiniteLightBound public Vector3 boxAxisY; // Scaled by the extents (half-size) public Vector3 boxAxisZ; // Scaled by the extents (half-size) public Vector3 center; // Center of the bounds (box) in camera space - public Vector2 scaleXY; // Scale applied to the top of the box to turn it into a truncated pyramid - public float radius; // Circumscribed sphere for the bounds (box) + public float scaleXY; // Scale applied to the top of the box to turn it into a truncated pyramid (X = Y) + public float radius; // Circumscribed sphere for the bounds (box) }; [GenerateHLSL] @@ -1766,9 +1766,9 @@ void GetLightVolumeDataAndBound(LightCategory lightCategory, GPULightType gpuLig fAltDx *= range; fAltDy *= range; // Handle case of pyramid with this select (currently unused) - var altDist = Mathf.Sqrt(fAltDy * fAltDy + (true ? 1.0f : 2.0f) * fAltDx * fAltDx); - bound.radius = altDist > (0.5f * range) ? altDist : (0.5f * range); // will always pick fAltDist - bound.scaleXY = squeeze ? new Vector2(0.01f, 0.01f) : new Vector2(1.0f, 1.0f); + var altDist = Mathf.Sqrt(fAltDy * fAltDy + (true ? 1.0f : 2.0f) * fAltDx * fAltDx); + bound.radius = altDist > (0.5f * range) ? altDist : (0.5f * range); // will always pick fAltDist + bound.scaleXY = squeeze ? 0.01f : 1.0f; lightVolumeData.lightAxisX = vx; lightVolumeData.lightAxisY = vy; @@ -1791,8 +1791,8 @@ void GetLightVolumeDataAndBound(LightCategory lightCategory, GPULightType gpuLig bound.boxAxisX = vx * range; bound.boxAxisY = vy * range; bound.boxAxisZ = vz * range; - bound.scaleXY.Set(1.0f, 1.0f); - bound.radius = range; + bound.scaleXY = 1.0f; + bound.radius = range; // fill up ldata lightVolumeData.lightAxisX = vx; @@ -1813,7 +1813,7 @@ void GetLightVolumeDataAndBound(LightCategory lightCategory, GPULightType gpuLig bound.boxAxisY = extents.y * yAxisVS; bound.boxAxisZ = extents.z * zAxisVS; bound.radius = extents.magnitude; - bound.scaleXY.Set(1.0f, 1.0f); + bound.scaleXY = 1.0f; lightVolumeData.lightPos = centerVS; lightVolumeData.lightAxisX = xAxisVS; @@ -1833,7 +1833,7 @@ void GetLightVolumeDataAndBound(LightCategory lightCategory, GPULightType gpuLig bound.boxAxisY = extents.y * yAxisVS; bound.boxAxisZ = extents.z * zAxisVS; bound.radius = extents.magnitude; - bound.scaleXY.Set(1.0f, 1.0f); + bound.scaleXY = 1.0f; lightVolumeData.lightPos = centerVS; lightVolumeData.lightAxisX = xAxisVS; @@ -1853,7 +1853,7 @@ void GetLightVolumeDataAndBound(LightCategory lightCategory, GPULightType gpuLig bound.boxAxisY = extents.y * yAxisVS; bound.boxAxisZ = extents.z * zAxisVS; bound.radius = extents.magnitude; - bound.scaleXY.Set(1.0f, 1.0f); + bound.scaleXY = 1.0f; lightVolumeData.lightPos = centerVS; lightVolumeData.lightAxisX = xAxisVS; @@ -2055,8 +2055,8 @@ void GetEnvLightVolumeDataAndBound(HDProbe probe, LightVolumeType lightVolumeTyp bound.boxAxisX = influenceRightVS * influenceExtents.x; bound.boxAxisY = influenceUpVS * influenceExtents.x; bound.boxAxisZ = influenceForwardVS * influenceExtents.x; - bound.scaleXY.Set(1.0f, 1.0f); - bound.radius = influenceExtents.x; + bound.scaleXY = 1.0f; + bound.radius = influenceExtents.x; break; } case LightVolumeType.Box: @@ -2065,8 +2065,8 @@ void GetEnvLightVolumeDataAndBound(HDProbe probe, LightVolumeType lightVolumeTyp bound.boxAxisX = influenceExtents.x * influenceRightVS; bound.boxAxisY = influenceExtents.y * influenceUpVS; bound.boxAxisZ = influenceExtents.z * influenceForwardVS; - bound.scaleXY.Set(1.0f, 1.0f); - bound.radius = influenceExtents.magnitude; + bound.scaleXY = 1.0f; + bound.radius = influenceExtents.magnitude; // The culling system culls pixels that are further // than a threshold to the box influence extents. @@ -2114,7 +2114,7 @@ void CreateBoxVolumeDataAndBound(OrientedBBox obb, LightCategory category, Light bound.boxAxisY = extentConservativeY * upVS; bound.boxAxisZ = extentConservativeZ * forwardVS; bound.radius = extentConservativeMagnitude; - bound.scaleXY.Set(1.0f, 1.0f); + bound.scaleXY = 1.0f; // The culling system culls pixels that are further // than a threshold to the box influence extents. diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs.hlsl index f4a18fa8f36..dc8f41cf636 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs.hlsl @@ -74,7 +74,7 @@ struct SFiniteLightBound float3 boxAxisY; float3 boxAxisZ; float3 center; - float2 scaleXY; + float scaleXY; float radius; }; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 7ac90c4d52c..44984045787 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -377,11 +377,11 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI const uint groupLocalLightIndex = t / THREADS_PER_LIGHT; const uint firstVertexOffset = NUM_VERTS * groupLocalLightIndex; - const float2 scale = lgtDat.scaleXY.xy; + const float scale = lgtDat.scaleXY.x; // scale.x = scale.y const float3 rbpC = lgtDat.center.xyz; - const float3 rbpX = lgtDat.boxAxisX.xyz; - const float3 rbpY = lgtDat.boxAxisY.xyz; - const float3 rbpZ = lgtDat.boxAxisZ.xyz; + const float3 rbpX = lgtDat.boxAxisX.xyz; // Pre-scaled + const float3 rbpY = lgtDat.boxAxisY.xyz; // Pre-scaled + const float3 rbpZ = lgtDat.boxAxisZ.xyz; // Pre-scaled #ifndef USE_WAVE_INTRINSICS // Initialize the TGSM. All threads write the same value -> no data races. diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DecalSystem.cs b/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DecalSystem.cs index 509ae94928a..ec262a398f7 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DecalSystem.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DecalSystem.cs @@ -629,12 +629,12 @@ private void GetDecalVolumeDataAndBound(Matrix4x4 decalToWorld, Matrix4x4 worldT var influenceForwardVS = worldToView.MultiplyVector(influenceZ / influenceExtents.z); var influencePositionVS = worldToView.MultiplyPoint(pos); // place the mesh pivot in the center - m_Bounds[m_DecalDatasCount].center = influencePositionVS; + m_Bounds[m_DecalDatasCount].center = influencePositionVS; m_Bounds[m_DecalDatasCount].boxAxisX = influenceRightVS * influenceExtents.x; m_Bounds[m_DecalDatasCount].boxAxisY = influenceUpVS * influenceExtents.y; m_Bounds[m_DecalDatasCount].boxAxisZ = influenceForwardVS * influenceExtents.z; - m_Bounds[m_DecalDatasCount].scaleXY.Set(1.0f, 1.0f); - m_Bounds[m_DecalDatasCount].radius = influenceExtents.magnitude; + m_Bounds[m_DecalDatasCount].scaleXY = 1.0f; + m_Bounds[m_DecalDatasCount].radius = influenceExtents.magnitude; // The culling system culls pixels that are further // than a threshold to the box influence extents. From 7a7d8f38f1cb41adaef4fa04fff76d58d9675efd Mon Sep 17 00:00:00 2001 From: Evgenii Date: Wed, 5 Aug 2020 18:53:33 -0700 Subject: [PATCH 04/24] Test corners of the view volume --- .../Lighting/LightLoop/scrbound.compute | 138 +++++++++++++++++- 1 file changed, 130 insertions(+), 8 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 44984045787..8cde9454319 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -56,6 +56,55 @@ uint NthBitLow(uint value, uint n) return b; } +float4x4 Translation4x4(float3 d) +{ + float4x4 M = k_Identity4x4; + + M._14_24_34 = d; // Last column + + return M; +} + +float3x3 Rotation3x3(float3 xAxis, float3 yAxis, float3 zAxis) +{ + float3x3 R = float3x3(xAxis, yAxis, zAxis); + float3x3 C = transpose(R); // Row to column + + return C; +} + +float3x3 Invert3x3(float3x3 R) +{ + float3x3 C = transpose(R); // Row to column + float det = dot(C[0], cross(C[1], C[2])); + float3x3 adj = float3x3(cross(C[1], C[2]), + cross(C[2], C[0]), + cross(C[0], C[1])); + + return rcp(det) * adj; +} + +float4x4 Homogenize3x3(float3x3 R) +{ + float4x4 M = float4x4(float4(R[0], 0), + float4(R[1], 0), + float4(R[2], 0), + float4(0,0,0,1)); + + return M; +} + +float4x4 PerspectiveProjection4x4(float s, float g, float n, float f) +{ + float a = (f + n) * rcp(f - n); + float b = -2 * f * n * rcp(f - n); + + return float4x4(g/s, 0, 0, 0, + 0, g, 0, 0, + 0, 0, a, b, + 0, 0, 1, 0); +} + // Clipping a plane by a cube may produce a hexagon (6-gon). // Clipping a hexagon by 4 planes may produce a decagon (10-gon). #define MAX_CLIP_VERTS (10) @@ -377,7 +426,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI const uint groupLocalLightIndex = t / THREADS_PER_LIGHT; const uint firstVertexOffset = NUM_VERTS * groupLocalLightIndex; - const float scale = lgtDat.scaleXY.x; // scale.x = scale.y + const float scale = lgtDat.scaleXY; // scale.x = scale.y const float3 rbpC = lgtDat.center.xyz; const float3 rbpX = lgtDat.boxAxisX.xyz; // Pre-scaled const float3 rbpY = lgtDat.boxAxisY.xyz; // Pre-scaled @@ -427,19 +476,21 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI m.xy *= (v >= 4) ? 1 : scale; float3 rbpVert = rbpC + m.x * rbpX + m.y * rbpY + m.z * rbpZ; + // Avoid generating (w = 0). + rbpVert.z = (abs(rbpVert.z) >= FLT_EPS) ? rbpVert.z : FLT_EPS; + float4 hapVert = mul(g_mProjection, float4(rbpVert, 1)); // Make sure the W component is strictly positive. // It is helpful in order to simplify clipping and to avoid perspective division by 0. - // For the orthographic projection, we only consider (w = 1) - float w = g_isOrthographic ? 1 : hapVert.w; + float w = hapVert.w; float s = (w >= 0) ? 1 : -1; // Transform the X and Y components: [-w, w] -> [0, w]. hapVert.x = (0.5 * s) * hapVert.x + ((0.5 * s) * w); hapVert.y = (0.5 * s) * hapVert.y + ((0.5 * s) * w); hapVert.z = s * hapVert.z; - hapVert.w = max(abs(w), FLT_MIN); + hapVert.w = s * hapVert.w; // For each vertex, we must determine whether it is within the bounds. // For culling and clipping, we must know, per culling plane, whether the vertex @@ -448,8 +499,11 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // Consider the vertex to be inside the view volume if: // 0 <= x <= w - // 0 <= y <= w + // 0 <= y <= w <-- include boundary points, to avoid clipping them later // 0 <= z <= w + // w is always valid + // For the orthographic projection, (w = 1), so no modifications are necessary. + // TODO: epsilon for numerical robustness? w = hapVert.w; for (uint j = 0; j < (NUM_PLANES / 2); j++) @@ -487,9 +541,77 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI cullClipFaceMask = gs_CullClipFaceMasks[groupLocalLightIndex]; #endif + // (2) Test the corners of the view volume. if (cullClipFaceMask != 0) { - // The light may be partially outside the view volume. + // The light is partially outside the view volume. + // Therefore, some of the corners of the view volume may be inside the light volume. + // We perform aggressive culling, so we must make sure they are accounted for. + // The light volume is a special type of cuboid - a right frustum. + // We can exploit this fact by building a light-space projection matrix. + float4x4 invTranslateToLightSpace = Translation4x4(-rbpC); + float4x4 invRotateAndScaleInLightSpace = Homogenize3x3(Invert3x3(Rotation3x3(rbpX, rbpY, rbpZ))); + // TODO: avoid full inversion by using unit vectors and passing magnitudes explicitly. + + // This (orhographic) projection matrix maps a view-space point to a light-space [-1, 1]^3 cube. + float4x4 lightSpaceMatrix = mul(invRotateAndScaleInLightSpace, invTranslateToLightSpace); + + if (scale != 1) // Perspective light space? + { + // Compute the parameters of the perspective projection. + float s = scale; + float e = -1 - 2 * (s * rcp(1 - s)); // Signed distance from the origin to the eye + float n = -e - 1; // Distance from the eye to the near plane + float f = -e + 1; // Distance from the eye to the far plane + float g = f; // Distance from the eye to the projection plane + + float4x4 invTranslateEye = Translation4x4(float3(0, 0, -e)); + float4x4 perspProjMatrix = PerspectiveProjection4x4(s, g, n, f); + + lightSpaceMatrix = mul(perspProjMatrix, mul(invTranslateEye, lightSpaceMatrix)); + } + + for (uint i = 0; i < VERTS_PER_THREAD; i++) + { + uint v = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; + + // rapVertsCS[0] = (-1, -1, 0) + // rapVertsCS[1] = (+1, -1, 0) + // rapVertsCS[2] = (+1, +1, 0) + // rapVertsCS[3] = (-1, +1, 0) + // rapVertsCS[4] = (-1, -1, 1) + // rapVertsCS[5] = (+1, -1, 1) + // rapVertsCS[6] = (+1, +1, 1) + // rapVertsCS[7] = (-1, +1, 1) + + float3 rapVertCS; // See the comment above + + rapVertCS.x = (countbits(v % 4) == 1) ? 1 : -1; + rapVertCS.y = (v & 2 != 0) ? 1 : -1; + rapVertCS.z = (v >= 4) ? 1 : 0; + + float4 hbpVertVS = mul(g_mInvProjection, float4(rapVertCS, 1)); // Clip to view space + float4 hapVertLS = mul(lightSpaceMatrix, hbpVertVS); // View to light space + + // Consider the vertex to be inside the light volume if: + // -w < x < w + // -w < y < w <-- exclude boundary points, as we will not clip using these vertices + // -w < z < w + // 0 < w + // For the orthographic projection, (w = 1), so no modifications are necessary. + // TODO: epsilon for numerical robustness? + + bool inside = Max3(abs(hapVertLS.x), abs(hapVertLS.y), abs(hapVertLS.z)) < hapVertLS.w; + + if (inside) + { + float3 rapVertNDC = float3(rapVertCS.xy * 0.5 + 0.5, rapVertCS.z); + + // Update the AABB. + rapAaBbMinPt = min(rapAaBbMinPt, rapVertNDC); + rapAaBbMaxPt = max(rapAaBbMaxPt, rapVertNDC); + } + } } uint behindMasksOfVerts[NUM_VERTS]; @@ -499,7 +621,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI behindMasksOfVerts[i] = gs_BehindMasksOfVerts[firstVertexOffset + i]; } - // (2) Cull the faces. + // (3) Cull the faces. const uint cullFaceMask = cullClipFaceMask; const uint numFacesToCull = countbits(cullFaceMask); // [0, 6] @@ -528,7 +650,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI cullClipFaceMask = gs_CullClipFaceMasks[groupLocalLightIndex]; #endif - // (3) Clip the faces. + // (4) Clip the faces. const uint clipFaceMask = cullClipFaceMask; const uint numFacesToClip = countbits(clipFaceMask); // [0, 6] From a8d04ba5c5d9a5d376d1b50b29986367f3a19fa0 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Wed, 5 Aug 2020 18:58:56 -0700 Subject: [PATCH 05/24] Improve the placeholder for the linear depth --- .../Runtime/Lighting/LightLoop/scrbound.compute | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 8cde9454319..2c483b79058 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -696,7 +696,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // a set of maxs, and each set is equal to g_iNrVisibLights. const ScreenSpaceBoundsIndices boundsIndices = GenerateScreenSpaceBoundsIndices(lgtIndex, g_iNrVisibLights, eyeIndex); - float minLinearDepth = -1, maxLinearDepth = -1; // TODO + float minLinearDepth = 0, maxLinearDepth = FLT_MAX; // TODO g_vBoundsBuffer[boundsIndices.min] = float4(rapAaBbMinPt, minLinearDepth); g_vBoundsBuffer[boundsIndices.max] = float4(rapAaBbMaxPt, maxLinearDepth); From 2ea9f8b7d33bc06e77c768fc60f7407acadef4f5 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Wed, 5 Aug 2020 20:08:03 -0700 Subject: [PATCH 06/24] Fix aspect --- .../Runtime/Lighting/LightLoop/scrbound.compute | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 2c483b79058..1a1b42f609b 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -94,14 +94,14 @@ float4x4 Homogenize3x3(float3x3 R) return M; } -float4x4 PerspectiveProjection4x4(float s, float g, float n, float f) +float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) { - float a = (f + n) * rcp(f - n); - float b = -2 * f * n * rcp(f - n); + float b = (f + n) * rcp(f - n); + float c = -2 * f * n * rcp(f - n); - return float4x4(g/s, 0, 0, 0, + return float4x4(g/a, 0, 0, 0, 0, g, 0, 0, - 0, 0, a, b, + 0, 0, b, c, 0, 0, 1, 0); } @@ -566,7 +566,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI float g = f; // Distance from the eye to the projection plane float4x4 invTranslateEye = Translation4x4(float3(0, 0, -e)); - float4x4 perspProjMatrix = PerspectiveProjection4x4(s, g, n, f); + float4x4 perspProjMatrix = PerspectiveProjection4x4(1, g, n, f); lightSpaceMatrix = mul(perspProjMatrix, mul(invTranslateEye, lightSpaceMatrix)); } From 0b870dbc07eff4ebda06c93e75f5e6467f6b9ca1 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Wed, 5 Aug 2020 20:41:09 -0700 Subject: [PATCH 07/24] Bugfix --- .../Lighting/LightLoop/scrbound.compute | 155 +++++++++--------- 1 file changed, 81 insertions(+), 74 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 1a1b42f609b..6a7d315ad24 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -11,7 +11,7 @@ #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightCullUtils.hlsl" -// #pragma enable_d3d11_debug_symbols +#pragma enable_d3d11_debug_symbols #pragma only_renderers d3d11 playstation xboxone vulkan metal switch StructuredBuffer g_data : register( t0 ); @@ -21,17 +21,12 @@ StructuredBuffer g_data : register( t0 ); // output buffer RWStructuredBuffer g_vBoundsBuffer : register( u0 ); -#define DUMB_COMPILER +#define Z_BINNING +// #define DUMB_COMPILER // #define USE_WAVE_INTRINSICS // We use TGSM and atomic operations if wave intrinsics are not supported #ifdef Z_BINNING -// Computes r=(n/d) and rounds the result towards the largest adjacent integer. -uint DivRoundUp(uint n, uint d) -{ - return (n + d - 1) / d; // No division by 0 checks -} - // Returns the location of the N-th set bit starting from the lowest order bit and working upward. // Slow implementation - do not use for large bit sets. // Could be optimized - see https://graphics.stanford.edu/~seander/bithacks.html @@ -105,21 +100,24 @@ float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) 0, 0, 1, 0); } +#define CLEAR_SIGN_BIT(X) (asuint(X) & INT_MAX) +#define DIV_ROUND_UP(N, D) (((N) + (D) - 1) / (D)) // No division by 0 checks + // Clipping a plane by a cube may produce a hexagon (6-gon). // Clipping a hexagon by 4 planes may produce a decagon (10-gon). #define MAX_CLIP_VERTS (10) -#define NUM_EDGES (12) #define NUM_VERTS (8) #define NUM_FACES (6) #define NUM_PLANES (6) -#define THREADS_PER_LIGHT (4) #define THREADS_PER_GROUP (64) +#define THREADS_PER_LIGHT (1) // Set to 1 for debugging #define LIGHTS_PER_GROUP (THREADS_PER_GROUP / THREADS_PER_LIGHT) #define VERTS_PER_GROUP (NUM_VERTS * LIGHTS_PER_GROUP) #define VERTS_PER_THREAD (NUM_VERTS / THREADS_PER_LIGHT) -#define FACES_PER_THREAD DivRoundUp(NUM_FACES, THREADS_PER_LIGHT) +#define FACES_PER_THREAD DIV_ROUND_UP(NUM_FACES, THREADS_PER_LIGHT) // All planes and faces are always in the standard order (see below). +// Near and far planes may be swapped for Reverse Z-Buffering, but it does not change the algorithm. #define FACE_LEFT (1 << 0) // x = -1 #define FACE_RIGHT (1 << 1) // x = +1 #define FACE_FRONT (1 << 2) // y = -1 @@ -131,6 +129,8 @@ float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) // TODO: the compiler generates 'tbuffer_load_format_x' instructions // when we access the look-up tables. Can we avoid this? +// TODO: try vert order (0 0 0), (1 0 0), (0 1 0), (1 1 0), (0 0 1), (1 0 1), (0 1 1), (1 1 1) + // All vertices are always in the standard order (see below). static const uint s_FaceMasksOfVerts[NUM_VERTS] = { @@ -148,12 +148,12 @@ static const uint s_FaceMasksOfVerts[NUM_VERTS] = // with normals pointing in the interior of the volume. static const uint s_VertMasksOfFaces[NUM_FACES] = { - 3 << 9 | 7 << 6 | 4 << 3 | 0 << 0, // 0: FACE_LEFT - 5 << 9 | 6 << 6 | 2 << 3 | 1 << 0, // 1: FACE_RIGHT - 4 << 9 | 5 << 6 | 1 << 3 | 0 << 0, // 2: FACE_FRONT - 2 << 9 | 6 << 6 | 7 << 3 | 3 << 0, // 3: FACE_BACK - 1 << 9 | 2 << 6 | 3 << 3 | 0 << 0, // 4: FACE_TOP - 7 << 9 | 6 << 6 | 5 << 3 | 4 << 0 // 5: FACE_BOTTOM + (3) << 9 | (7) << 6 | (4) << 3 | (0) << 0, // 0: FACE_LEFT + (5) << 9 | (6) << 6 | (2) << 3 | (1) << 0, // 1: FACE_RIGHT + (4) << 9 | (5) << 6 | (1) << 3 | (0) << 0, // 2: FACE_FRONT + (6) << 9 | (7) << 6 | (3) << 3 | (2) << 0, // 3: FACE_BACK + (1) << 9 | (2) << 6 | (3) << 3 | (0) << 0, // 4: FACE_TOP + (7) << 9 | (6) << 6 | (5) << 3 | (4) << 0 // 5: FACE_BOTTOM }; // 5 arrays * 128 elements * 4 bytes each = 2560 bytes. @@ -168,7 +168,7 @@ groupshared uint gs_BehindMasksOfVerts[VERTS_PER_GROUP]; // 6 planes each (HLSL groupshared uint gs_CullClipFaceMasks[LIGHTS_PER_GROUP]; // 6 faces each (HLSL does not support small data types) // 6 arrays * 16 elements * 4 bytes each = 384 bytes. -// Note that these are actually floats reinterpreted as uints. +// These are actually floats reinterpreted as uints. // The reason is because floating-point atomic operations are not supported. groupshared uint gs_RapAaBbMinPtX[LIGHTS_PER_GROUP]; groupshared uint gs_RapAaBbMaxPtX[LIGHTS_PER_GROUP]; @@ -302,8 +302,8 @@ void ClipFaceAgainstViewVolumeAndUpdateAaBb(uint f, uint behindMasksOfVerts[NUM_ // Non-zero if ANY of the vertices are behind any of the planes. clipMaskOfFace |= behindMasksOfVerts[v]; - // Note that not all edges may require clipping. However, - // filtering the vertex list is somewhat expensive, so we currently don't do it. + // Not all edges may require clipping. However, filtering the vertex list + // is somewhat expensive, so we currently don't do it. vertRingBuffer[j].x = gs_HapVertsX[firstVertexOffset + v]; vertRingBuffer[j].y = gs_HapVertsY[firstVertexOffset + v]; vertRingBuffer[j].z = gs_HapVertsZ[firstVertexOffset + v]; @@ -331,14 +331,14 @@ void ClipFaceAgainstViewVolumeAndUpdateAaBb(uint f, uint behindMasksOfVerts[NUM_ uint modSrcIdx = srcBegin % MAX_CLIP_VERTS; #endif - for (int j = srcBegin; j < (srcBegin + srcSize); j++) + for (uint j = srcBegin; j < (srcBegin + srcSize); j++) { #ifndef DUMB_COMPILER uint modSrcIdx = j % MAX_CLIP_VERTS; #endif float4 hapVert = vertRingBuffer[modSrcIdx]; - float3 rapVert = hapVert.xyz * rcp(hapVert.w); + float3 rapVert = saturate(hapVert.xyz * rcp(hapVert.w)); // Must not generate negative values rapAaBbMinPt = min(rapAaBbMinPt, rapVert); rapAaBbMaxPt = max(rapAaBbMaxPt, rapVert); @@ -352,7 +352,10 @@ void ClipFaceAgainstViewVolumeAndUpdateAaBb(uint f, uint behindMasksOfVerts[NUM_ #else // !Z_BINNING -#define MAX_PNTS 9 // strictly this should be 10=6+4 but we get more wavefronts and 10 seems to never hit (fingers crossed) +#define THREADS_PER_LIGHT (8) +#define THREADS_PER_GROUP (64) +#define LIGHTS_PER_GROUP (THREADS_PER_GROUP / THREADS_PER_LIGHT) +#define MAX_PNTS (9) // strictly this should be 10=6+4 but we get more wavefronts and 10 seems to never hit (fingers crossed) // However, worst case the plane that would be skipped if such an extreme case ever happened would be backplane // clipping gets skipped which doesn't cause any errors. @@ -387,8 +390,8 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI unsigned int g = groupID; unsigned int t = threadID; - const int subLigt = (uint) (t/8); - const int lgtIndex = subLigt+(uint) g*8; + const int subLigt = (uint) (t/THREADS_PER_LIGHT); + const int lgtIndex = subLigt+(uint) g*LIGHTS_PER_GROUP; const int sideIndex = (uint) (t%8); const int eyeAdjustedLgtIndex = GenerateLightCullDataIndex(lgtIndex, g_iNrVisibLights, eyeIndex); @@ -402,13 +405,13 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // Since a light volume may be partially off-screen, we must clip it before computing the AABB. // Clipping the resulting AABB (rather than the light volume itself) may result in a loose AABB. // - // To avoid having to deal with toroidal properties of the perspective transform, + // To avoid having to deal with the "Moebius twist" property of the perspective transform, // we perform clipping using the homogeneous (projective) post-perspective coordinates. // This clipping method in described in Blinn's paper titled "Line Clipping". // // The algorithm processes a light on 4 threads. While all 6 faces may require clipping in the // worst case, clipping more than 4 faces is very uncommon (typically, we clip 0, 3 or 4). - // Note that some faces may require culling rather than clipping (the former is simpler). + // Some faces may require culling rather than clipping (the former is simpler). // // It's important to realize that face culling may end up culling 5 (or even all 6) faces. // This means that the clipped light volume may be reduced to a single polygon, or nothing at all. @@ -428,20 +431,23 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI const float scale = lgtDat.scaleXY; // scale.x = scale.y const float3 rbpC = lgtDat.center.xyz; + // TODO: store X, Y, Scale const float3 rbpX = lgtDat.boxAxisX.xyz; // Pre-scaled const float3 rbpY = lgtDat.boxAxisY.xyz; // Pre-scaled const float3 rbpZ = lgtDat.boxAxisZ.xyz; // Pre-scaled #ifndef USE_WAVE_INTRINSICS - // Initialize the TGSM. All threads write the same value -> no data races. - // The hardware will coalesce the writes. - gs_CullClipFaceMasks[groupLocalLightIndex] = 0; // Initially inside - gs_RapAaBbMinPtX[groupLocalLightIndex] = asuint(1.0f); - gs_RapAaBbMaxPtX[groupLocalLightIndex] = asuint(0.0f); - gs_RapAaBbMinPtY[groupLocalLightIndex] = asuint(1.0f); - gs_RapAaBbMaxPtY[groupLocalLightIndex] = asuint(0.0f); - gs_RapAaBbMinPtZ[groupLocalLightIndex] = asuint(1.0f); - gs_RapAaBbMaxPtZ[groupLocalLightIndex] = asuint(0.0f); + // (0) Initialize the TGSM. + if (t % THREADS_PER_LIGHT == 0) // Avoid bank conflicts + { + gs_CullClipFaceMasks[groupLocalLightIndex] = 0; // Initially inside + gs_RapAaBbMinPtX[groupLocalLightIndex] = asuint(1.0f); + gs_RapAaBbMaxPtX[groupLocalLightIndex] = asuint(0.0f); + gs_RapAaBbMinPtY[groupLocalLightIndex] = asuint(1.0f); + gs_RapAaBbMaxPtY[groupLocalLightIndex] = asuint(0.0f); + gs_RapAaBbMinPtZ[groupLocalLightIndex] = asuint(1.0f); + gs_RapAaBbMaxPtZ[groupLocalLightIndex] = asuint(0.0f); + } #endif // USE_WAVE_INTRINSICS float3 rapAaBbMinPt = 1; @@ -477,20 +483,16 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI float3 rbpVert = rbpC + m.x * rbpX + m.y * rbpY + m.z * rbpZ; // Avoid generating (w = 0). - rbpVert.z = (abs(rbpVert.z) >= FLT_EPS) ? rbpVert.z : FLT_EPS; + rbpVert.z = (abs(rbpVert.z) > FLT_MIN) ? rbpVert.z : FLT_MIN; float4 hapVert = mul(g_mProjection, float4(rbpVert, 1)); - // Make sure the W component is strictly positive. - // It is helpful in order to simplify clipping and to avoid perspective division by 0. - float w = hapVert.w; - float s = (w >= 0) ? 1 : -1; + // Warning: the W component may be negative. + // Flipping the -W pyramid by negating all coordinates is incorrect + // and will break both classification and clipping. // Transform the X and Y components: [-w, w] -> [0, w]. - hapVert.x = (0.5 * s) * hapVert.x + ((0.5 * s) * w); - hapVert.y = (0.5 * s) * hapVert.y + ((0.5 * s) * w); - hapVert.z = s * hapVert.z; - hapVert.w = s * hapVert.w; + hapVert.xy = 0.5 * hapVert.xy + (0.5 * hapVert.w); // For each vertex, we must determine whether it is within the bounds. // For culling and clipping, we must know, per culling plane, whether the vertex @@ -504,17 +506,18 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // w is always valid // For the orthographic projection, (w = 1), so no modifications are necessary. // TODO: epsilon for numerical robustness? - w = hapVert.w; for (uint j = 0; j < (NUM_PLANES / 2); j++) { + float w = hapVert.w; + behindMask |= (hapVert[j] < 0 ? 1 : 0) << (2 * j + 0); // Planes crossing '0' behindMask |= (hapVert[j] > w ? 1 : 0) << (2 * j + 1); // Planes crossing 'w' } if (behindMask == 0) // Inside? { - float3 rapVert = hapVert.xyz * rcp(hapVert.w); + float3 rapVert = saturate(hapVert.xyz * rcp(hapVert.w)); // Must not generate negative values rapAaBbMinPt = min(rapAaBbMinPt, rapVert); rapAaBbMaxPt = max(rapAaBbMaxPt, rapVert); @@ -568,7 +571,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI float4x4 invTranslateEye = Translation4x4(float3(0, 0, -e)); float4x4 perspProjMatrix = PerspectiveProjection4x4(1, g, n, f); - lightSpaceMatrix = mul(perspProjMatrix, mul(invTranslateEye, lightSpaceMatrix)); + lightSpaceMatrix = mul(mul(perspProjMatrix, invTranslateEye), lightSpaceMatrix); } for (uint i = 0; i < VERTS_PER_THREAD; i++) @@ -596,7 +599,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // Consider the vertex to be inside the light volume if: // -w < x < w // -w < y < w <-- exclude boundary points, as we will not clip using these vertices - // -w < z < w + // -w < z < w <-- assume that Z-precision is not very important here // 0 < w // For the orthographic projection, (w = 1), so no modifications are necessary. // TODO: epsilon for numerical robustness? @@ -622,20 +625,22 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI } // (3) Cull the faces. - const uint cullFaceMask = cullClipFaceMask; - const uint numFacesToCull = countbits(cullFaceMask); // [0, 6] - - for (uint i = 0; i < FACES_PER_THREAD; i++) { - uint n = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; + const uint cullFaceMask = cullClipFaceMask; + const uint numFacesToCull = countbits(cullFaceMask); // [0, 6] - if (n < numFacesToCull) + for (uint i = 0; i < FACES_PER_THREAD; i++) { - uint f = NthBitLow(cullFaceMask, n); + uint n = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; - if (TryCullFace(f, behindMasksOfVerts)) + if (n < numFacesToCull) { - cullClipFaceMask ^= 1 << f; // Clear the bit + uint f = NthBitLow(cullFaceMask, n); + + if (TryCullFace(f, behindMasksOfVerts)) + { + cullClipFaceMask ^= 1 << f; // Clear the bit + } } } } @@ -651,19 +656,21 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI #endif // (4) Clip the faces. - const uint clipFaceMask = cullClipFaceMask; - const uint numFacesToClip = countbits(clipFaceMask); // [0, 6] - - for (uint i = 0; i < FACES_PER_THREAD; i++) { - uint n = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; + const uint clipFaceMask = cullClipFaceMask; + const uint numFacesToClip = countbits(clipFaceMask); // [0, 6] - if (n < numFacesToCull) + for (uint i = 0; i < FACES_PER_THREAD; i++) { - uint f = NthBitLow(clipFaceMask, n); + uint n = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; + + if (n < numFacesToClip) + { + uint f = NthBitLow(clipFaceMask, n); - ClipFaceAgainstViewVolumeAndUpdateAaBb(f, behindMasksOfVerts, firstVertexOffset, - rapAaBbMinPt, rapAaBbMaxPt); + ClipFaceAgainstViewVolumeAndUpdateAaBb(f, behindMasksOfVerts, firstVertexOffset, + rapAaBbMinPt, rapAaBbMaxPt); + } } } @@ -672,12 +679,12 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI #else // Integer comparison works for floating-point numbers as long as the sign bit is 0. // We must take care of the signed zero ourselves. - InterlockedMin(gs_RapAaBbMinPtX[groupLocalLightIndex], asuint(rapAaBbMinPt.x) & INT_MAX); - InterlockedMax(gs_RapAaBbMaxPtX[groupLocalLightIndex], asuint(rapAaBbMaxPt.x) & INT_MAX); - InterlockedMin(gs_RapAaBbMinPtY[groupLocalLightIndex], asuint(rapAaBbMinPt.y) & INT_MAX); - InterlockedMax(gs_RapAaBbMaxPtY[groupLocalLightIndex], asuint(rapAaBbMaxPt.y) & INT_MAX); - InterlockedMin(gs_RapAaBbMinPtZ[groupLocalLightIndex], asuint(rapAaBbMinPt.z) & INT_MAX); - InterlockedMax(gs_RapAaBbMaxPtZ[groupLocalLightIndex], asuint(rapAaBbMaxPt.z) & INT_MAX); + InterlockedMin(gs_RapAaBbMinPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMinPt.x))); + InterlockedMax(gs_RapAaBbMaxPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMaxPt.x))); + InterlockedMin(gs_RapAaBbMinPtY[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMinPt.y))); + InterlockedMax(gs_RapAaBbMaxPtY[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMaxPt.y))); + InterlockedMin(gs_RapAaBbMinPtZ[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMinPt.z))); + InterlockedMax(gs_RapAaBbMaxPtZ[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMaxPt.z))); GroupMemoryBarrierWithGroupSync(); @@ -689,7 +696,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI rapAaBbMaxPt.z = asfloat(gs_RapAaBbMaxPtZ[groupLocalLightIndex]); #endif // USE_WAVE_INTRINSICS - if (t % THREADS_PER_LIGHT == 0) + if (t % THREADS_PER_LIGHT == 0) // Avoid bank conflicts { // Each light's AABB is represented by two float3s, the min and max of the box. // And for stereo, we have two sets of lights. Therefore, each eye has a set of mins, followed by From 83d73e935b84ddae99438bb432cb4673f7c2775e Mon Sep 17 00:00:00 2001 From: Evgenii Date: Thu, 6 Aug 2020 21:38:10 -0700 Subject: [PATCH 08/24] Optimize --- .../Lighting/LightLoop/scrbound.compute | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 6a7d315ad24..197589bf12c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -11,7 +11,7 @@ #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightCullUtils.hlsl" -#pragma enable_d3d11_debug_symbols +// #pragma enable_d3d11_debug_symbols #pragma only_renderers d3d11 playstation xboxone vulkan metal switch StructuredBuffer g_data : register( t0 ); @@ -22,7 +22,7 @@ StructuredBuffer g_data : register( t0 ); RWStructuredBuffer g_vBoundsBuffer : register( u0 ); #define Z_BINNING -// #define DUMB_COMPILER +#define DUMB_COMPILER // #define USE_WAVE_INTRINSICS // We use TGSM and atomic operations if wave intrinsics are not supported #ifdef Z_BINNING @@ -75,7 +75,6 @@ float3x3 Invert3x3(float3x3 R) float3x3 adj = float3x3(cross(C[1], C[2]), cross(C[2], C[0]), cross(C[0], C[1])); - return rcp(det) * adj; } @@ -85,14 +84,13 @@ float4x4 Homogenize3x3(float3x3 R) float4(R[1], 0), float4(R[2], 0), float4(0,0,0,1)); - return M; } float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) { - float b = (f + n) * rcp(f - n); - float c = -2 * f * n * rcp(f - n); + float b = (f + n) * rcp(f - n); // z: [-1, 1] + float c = -2 * f * n * rcp(f - n); // No Z-reversal return float4x4(g/a, 0, 0, 0, 0, g, 0, 0, @@ -110,14 +108,14 @@ float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) #define NUM_FACES (6) #define NUM_PLANES (6) #define THREADS_PER_GROUP (64) -#define THREADS_PER_LIGHT (1) // Set to 1 for debugging +#define THREADS_PER_LIGHT (4) // Set to 1 for debugging #define LIGHTS_PER_GROUP (THREADS_PER_GROUP / THREADS_PER_LIGHT) #define VERTS_PER_GROUP (NUM_VERTS * LIGHTS_PER_GROUP) #define VERTS_PER_THREAD (NUM_VERTS / THREADS_PER_LIGHT) #define FACES_PER_THREAD DIV_ROUND_UP(NUM_FACES, THREADS_PER_LIGHT) // All planes and faces are always in the standard order (see below). -// Near and far planes may be swapped for Reverse Z-Buffering, but it does not change the algorithm. +// Near and far planes are swapped in the case of Z-reversal, but it does not affect the algorithm. #define FACE_LEFT (1 << 0) // x = -1 #define FACE_RIGHT (1 << 1) // x = +1 #define FACE_FRONT (1 << 2) // y = -1 @@ -476,7 +474,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI float3 m; // See the comment above m.x = (countbits(v % 4) == 1) ? 1 : -1; - m.y = (v & 2 != 0) ? 1 : -1; + m.y = ((v & 2) != 0) ? 1 : -1; m.z = (v >= 4) ? 1 : -1; m.xy *= (v >= 4) ? 1 : scale; @@ -501,7 +499,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // Consider the vertex to be inside the view volume if: // 0 <= x <= w - // 0 <= y <= w <-- include boundary points, to avoid clipping them later + // 0 <= y <= w <-- include boundary points to avoid clipping them later // 0 <= z <= w // w is always valid // For the orthographic projection, (w = 1), so no modifications are necessary. @@ -556,7 +554,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI float4x4 invRotateAndScaleInLightSpace = Homogenize3x3(Invert3x3(Rotation3x3(rbpX, rbpY, rbpZ))); // TODO: avoid full inversion by using unit vectors and passing magnitudes explicitly. - // This (orhographic) projection matrix maps a view-space point to a light-space [-1, 1]^3 cube. + // This (orthographic) projection matrix maps a view-space point to a light-space [-1, 1]^3 cube. float4x4 lightSpaceMatrix = mul(invRotateAndScaleInLightSpace, invTranslateToLightSpace); if (scale != 1) // Perspective light space? @@ -590,7 +588,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI float3 rapVertCS; // See the comment above rapVertCS.x = (countbits(v % 4) == 1) ? 1 : -1; - rapVertCS.y = (v & 2 != 0) ? 1 : -1; + rapVertCS.y = ((v & 2) != 0) ? 1 : -1; rapVertCS.z = (v >= 4) ? 1 : 0; float4 hbpVertVS = mul(g_mInvProjection, float4(rapVertCS, 1)); // Clip to view space @@ -703,10 +701,8 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // a set of maxs, and each set is equal to g_iNrVisibLights. const ScreenSpaceBoundsIndices boundsIndices = GenerateScreenSpaceBoundsIndices(lgtIndex, g_iNrVisibLights, eyeIndex); - float minLinearDepth = 0, maxLinearDepth = FLT_MAX; // TODO - - g_vBoundsBuffer[boundsIndices.min] = float4(rapAaBbMinPt, minLinearDepth); - g_vBoundsBuffer[boundsIndices.max] = float4(rapAaBbMaxPt, maxLinearDepth); + g_vBoundsBuffer[boundsIndices.min] = float4(rapAaBbMinPt, 0); // TODO: add me - lin depth + g_vBoundsBuffer[boundsIndices.max] = float4(rapAaBbMaxPt, 100000); // } #else // !Z_BINNING From 3f137abbe1c24ef55d71f0083f0cae6a622fbfbc Mon Sep 17 00:00:00 2001 From: Evgenii Date: Fri, 7 Aug 2020 13:15:40 -0700 Subject: [PATCH 09/24] Also store view space Z --- .../Lighting/LightLoop/scrbound.compute | 112 ++++++++++-------- 1 file changed, 65 insertions(+), 47 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 197589bf12c..118ec9c2374 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -165,15 +165,17 @@ groupshared uint gs_BehindMasksOfVerts[VERTS_PER_GROUP]; // 6 planes each (HLSL // 1 array * 16 elements * 4 bytes each = 64 bytes. groupshared uint gs_CullClipFaceMasks[LIGHTS_PER_GROUP]; // 6 faces each (HLSL does not support small data types) -// 6 arrays * 16 elements * 4 bytes each = 384 bytes. +// 8 arrays * 16 elements * 4 bytes each = 512 bytes. // These are actually floats reinterpreted as uints. // The reason is because floating-point atomic operations are not supported. -groupshared uint gs_RapAaBbMinPtX[LIGHTS_PER_GROUP]; -groupshared uint gs_RapAaBbMaxPtX[LIGHTS_PER_GROUP]; -groupshared uint gs_RapAaBbMinPtY[LIGHTS_PER_GROUP]; -groupshared uint gs_RapAaBbMaxPtY[LIGHTS_PER_GROUP]; -groupshared uint gs_RapAaBbMinPtZ[LIGHTS_PER_GROUP]; -groupshared uint gs_RapAaBbMaxPtZ[LIGHTS_PER_GROUP]; +groupshared uint gs_NdcAaBbMinPtX[LIGHTS_PER_GROUP]; +groupshared uint gs_NdcAaBbMaxPtX[LIGHTS_PER_GROUP]; +groupshared uint gs_NdcAaBbMinPtY[LIGHTS_PER_GROUP]; +groupshared uint gs_NdcAaBbMaxPtY[LIGHTS_PER_GROUP]; +groupshared uint gs_NdcAaBbMinPtZ[LIGHTS_PER_GROUP]; // Note that min-max Z cannot be trivially reconstructed +groupshared uint gs_NdcAaBbMaxPtZ[LIGHTS_PER_GROUP]; // from min-max W if the projection is oblique. +groupshared uint gs_NdcAaBbMinPtW[LIGHTS_PER_GROUP]; // View-space Z coordinate +groupshared uint gs_NdcAaBbMaxPtW[LIGHTS_PER_GROUP]; // View-space Z coordinate #endif // USE_WAVE_INTRINSICS // Returns 'true' if it manages to cull the face. @@ -285,8 +287,8 @@ void ClipPolygonAgainstPlane(uint p, uint srcBegin, uint srcSize, } } -void ClipFaceAgainstViewVolumeAndUpdateAaBb(uint f, uint behindMasksOfVerts[NUM_VERTS], uint firstVertexOffset, - inout float3 rapAaBbMinPt, inout float3 rapAaBbMaxPt) +void ClipFaceAgainstViewVolumeAndUpdateAaBb(uint f, uint behindMasksOfVerts[NUM_VERTS], uint firstVertexOffset, float4x4 g_mInvProjection, + inout float4 ndcAaBbMinPt, inout float4 ndcAaBbMaxPt) { float4 vertRingBuffer[MAX_CLIP_VERTS]; uint srcBegin = 0, srcSize = 4; @@ -335,11 +337,15 @@ void ClipFaceAgainstViewVolumeAndUpdateAaBb(uint f, uint behindMasksOfVerts[NUM_ uint modSrcIdx = j % MAX_CLIP_VERTS; #endif - float4 hapVert = vertRingBuffer[modSrcIdx]; - float3 rapVert = saturate(hapVert.xyz * rcp(hapVert.w)); // Must not generate negative values + float4 hapVert = vertRingBuffer[modSrcIdx]; + float4 hbpVertVS = mul(g_mInvProjection, hapVert); // Just to support orthographic projection + float3 rapVertNDC = saturate(hapVert.xyz * rcp(hapVert.w)); // Must not generate negative values + float rbpVertVSz = hbpVertVS.z * rcp(hbpVertVS.w); - rapAaBbMinPt = min(rapAaBbMinPt, rapVert); - rapAaBbMaxPt = max(rapAaBbMaxPt, rapVert); + ndcAaBbMinPt.xyz = min(ndcAaBbMinPt.xyz, rapVertNDC); + ndcAaBbMaxPt.xyz = max(ndcAaBbMaxPt.xyz, rapVertNDC); + ndcAaBbMinPt.w = min(ndcAaBbMinPt.w, rbpVertVSz); + ndcAaBbMaxPt.w = max(ndcAaBbMaxPt.w, rbpVertVSz); #ifdef DUMB_COMPILER modSrcIdx++; @@ -439,17 +445,19 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI if (t % THREADS_PER_LIGHT == 0) // Avoid bank conflicts { gs_CullClipFaceMasks[groupLocalLightIndex] = 0; // Initially inside - gs_RapAaBbMinPtX[groupLocalLightIndex] = asuint(1.0f); - gs_RapAaBbMaxPtX[groupLocalLightIndex] = asuint(0.0f); - gs_RapAaBbMinPtY[groupLocalLightIndex] = asuint(1.0f); - gs_RapAaBbMaxPtY[groupLocalLightIndex] = asuint(0.0f); - gs_RapAaBbMinPtZ[groupLocalLightIndex] = asuint(1.0f); - gs_RapAaBbMaxPtZ[groupLocalLightIndex] = asuint(0.0f); + gs_NdcAaBbMinPtX[groupLocalLightIndex] = asuint(1.0f); + gs_NdcAaBbMaxPtX[groupLocalLightIndex] = asuint(0.0f); + gs_NdcAaBbMinPtY[groupLocalLightIndex] = asuint(1.0f); + gs_NdcAaBbMaxPtY[groupLocalLightIndex] = asuint(0.0f); + gs_NdcAaBbMinPtZ[groupLocalLightIndex] = asuint(1.0f); + gs_NdcAaBbMaxPtZ[groupLocalLightIndex] = asuint(0.0f); + gs_NdcAaBbMinPtW[groupLocalLightIndex] = asuint(FLT_INF); + gs_NdcAaBbMaxPtW[groupLocalLightIndex] = asuint(0.0f); } #endif // USE_WAVE_INTRINSICS - float3 rapAaBbMinPt = 1; - float3 rapAaBbMaxPt = 0; + float4 ndcAaBbMinPt = float4(1, 1, 1, FLT_INF); + float4 ndcAaBbMaxPt = 0; // We must determine whether we have to clip or cull any of the faces. // If all vertices of a face are inside with respect to all the culling planes, @@ -479,11 +487,11 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI m.xy *= (v >= 4) ? 1 : scale; - float3 rbpVert = rbpC + m.x * rbpX + m.y * rbpY + m.z * rbpZ; + float3 rbpVertVS = rbpC + m.x * rbpX + m.y * rbpY + m.z * rbpZ; // Avoid generating (w = 0). - rbpVert.z = (abs(rbpVert.z) > FLT_MIN) ? rbpVert.z : FLT_MIN; + rbpVertVS.z = (abs(rbpVertVS.z) > FLT_MIN) ? rbpVertVS.z : FLT_MIN; - float4 hapVert = mul(g_mProjection, float4(rbpVert, 1)); + float4 hapVert = mul(g_mProjection, float4(rbpVertVS, 1)); // Warning: the W component may be negative. // Flipping the -W pyramid by negating all coordinates is incorrect @@ -492,6 +500,8 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // Transform the X and Y components: [-w, w] -> [0, w]. hapVert.xy = 0.5 * hapVert.xy + (0.5 * hapVert.w); + // TODO: multiply vertex by ViewZ if orthographic for unified processing! + // For each vertex, we must determine whether it is within the bounds. // For culling and clipping, we must know, per culling plane, whether the vertex // is in the positive or the negative half-space. @@ -515,10 +525,12 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI if (behindMask == 0) // Inside? { - float3 rapVert = saturate(hapVert.xyz * rcp(hapVert.w)); // Must not generate negative values + float3 rapVertNDC = saturate(hapVert.xyz * rcp(hapVert.w)); // Must not generate negative values - rapAaBbMinPt = min(rapAaBbMinPt, rapVert); - rapAaBbMaxPt = max(rapAaBbMaxPt, rapVert); + ndcAaBbMinPt.xyz = min(ndcAaBbMinPt.xyz, rapVertNDC); + ndcAaBbMaxPt.xyz = max(ndcAaBbMaxPt.xyz, rapVertNDC); + ndcAaBbMinPt.w = min(ndcAaBbMinPt.w, rbpVertVS.z); + ndcAaBbMaxPt.w = max(ndcAaBbMaxPt.w, rbpVertVS.z); } else // Outside { @@ -607,10 +619,12 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI if (inside) { float3 rapVertNDC = float3(rapVertCS.xy * 0.5 + 0.5, rapVertCS.z); + float rbpVertVSz = hbpVertVS.z * rcp(hbpVertVS.w); - // Update the AABB. - rapAaBbMinPt = min(rapAaBbMinPt, rapVertNDC); - rapAaBbMaxPt = max(rapAaBbMaxPt, rapVertNDC); + ndcAaBbMinPt.xyz = min(ndcAaBbMinPt.xyz, rapVertNDC); + ndcAaBbMaxPt.xyz = max(ndcAaBbMaxPt.xyz, rapVertNDC); + ndcAaBbMinPt.w = min(ndcAaBbMinPt.w, rbpVertVSz); + ndcAaBbMaxPt.w = max(ndcAaBbMaxPt.w, rbpVertVSz); } } } @@ -666,8 +680,8 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI { uint f = NthBitLow(clipFaceMask, n); - ClipFaceAgainstViewVolumeAndUpdateAaBb(f, behindMasksOfVerts, firstVertexOffset, - rapAaBbMinPt, rapAaBbMaxPt); + ClipFaceAgainstViewVolumeAndUpdateAaBb(f, behindMasksOfVerts, firstVertexOffset, g_mInvProjection, + ndcAaBbMinPt, ndcAaBbMaxPt); } } } @@ -676,22 +690,26 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // ... #else // Integer comparison works for floating-point numbers as long as the sign bit is 0. - // We must take care of the signed zero ourselves. - InterlockedMin(gs_RapAaBbMinPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMinPt.x))); - InterlockedMax(gs_RapAaBbMaxPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMaxPt.x))); - InterlockedMin(gs_RapAaBbMinPtY[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMinPt.y))); - InterlockedMax(gs_RapAaBbMaxPtY[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMaxPt.y))); - InterlockedMin(gs_RapAaBbMinPtZ[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMinPt.z))); - InterlockedMax(gs_RapAaBbMaxPtZ[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(rapAaBbMaxPt.z))); + // We must take care of the signed zero ourselves. saturate() does not help here. + InterlockedMin(gs_NdcAaBbMinPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.x))); + InterlockedMax(gs_NdcAaBbMaxPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.x))); + InterlockedMin(gs_NdcAaBbMinPtY[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.y))); + InterlockedMax(gs_NdcAaBbMaxPtY[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.y))); + InterlockedMin(gs_NdcAaBbMinPtZ[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.z))); + InterlockedMax(gs_NdcAaBbMaxPtZ[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.z))); + InterlockedMin(gs_NdcAaBbMinPtW[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.w))); + InterlockedMax(gs_NdcAaBbMaxPtW[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.w))); GroupMemoryBarrierWithGroupSync(); - rapAaBbMinPt.x = asfloat(gs_RapAaBbMinPtX[groupLocalLightIndex]); - rapAaBbMaxPt.x = asfloat(gs_RapAaBbMaxPtX[groupLocalLightIndex]); - rapAaBbMinPt.y = asfloat(gs_RapAaBbMinPtY[groupLocalLightIndex]); - rapAaBbMaxPt.y = asfloat(gs_RapAaBbMaxPtY[groupLocalLightIndex]); - rapAaBbMinPt.z = asfloat(gs_RapAaBbMinPtZ[groupLocalLightIndex]); - rapAaBbMaxPt.z = asfloat(gs_RapAaBbMaxPtZ[groupLocalLightIndex]); + ndcAaBbMinPt.x = asfloat(gs_NdcAaBbMinPtX[groupLocalLightIndex]); + ndcAaBbMaxPt.x = asfloat(gs_NdcAaBbMaxPtX[groupLocalLightIndex]); + ndcAaBbMinPt.y = asfloat(gs_NdcAaBbMinPtY[groupLocalLightIndex]); + ndcAaBbMaxPt.y = asfloat(gs_NdcAaBbMaxPtY[groupLocalLightIndex]); + ndcAaBbMinPt.z = asfloat(gs_NdcAaBbMinPtZ[groupLocalLightIndex]); + ndcAaBbMaxPt.z = asfloat(gs_NdcAaBbMaxPtZ[groupLocalLightIndex]); + ndcAaBbMinPt.w = asfloat(gs_NdcAaBbMinPtW[groupLocalLightIndex]); + ndcAaBbMaxPt.w = asfloat(gs_NdcAaBbMaxPtW[groupLocalLightIndex]); #endif // USE_WAVE_INTRINSICS if (t % THREADS_PER_LIGHT == 0) // Avoid bank conflicts @@ -701,8 +719,8 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // a set of maxs, and each set is equal to g_iNrVisibLights. const ScreenSpaceBoundsIndices boundsIndices = GenerateScreenSpaceBoundsIndices(lgtIndex, g_iNrVisibLights, eyeIndex); - g_vBoundsBuffer[boundsIndices.min] = float4(rapAaBbMinPt, 0); // TODO: add me - lin depth - g_vBoundsBuffer[boundsIndices.max] = float4(rapAaBbMaxPt, 100000); // + g_vBoundsBuffer[boundsIndices.min] = ndcAaBbMinPt; + g_vBoundsBuffer[boundsIndices.max] = ndcAaBbMaxPt; } #else // !Z_BINNING From be9e040608676823de4727d59534afefd71804ce Mon Sep 17 00:00:00 2001 From: Evgenii Date: Sat, 8 Aug 2020 14:05:14 -0700 Subject: [PATCH 10/24] Optimize orthographic --- .../Lighting/LightLoop/scrbound.compute | 65 ++++++++++--------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 118ec9c2374..ee373ec6081 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -115,7 +115,7 @@ float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) #define FACES_PER_THREAD DIV_ROUND_UP(NUM_FACES, THREADS_PER_LIGHT) // All planes and faces are always in the standard order (see below). -// Near and far planes are swapped in the case of Z-reversal, but it does not affect the algorithm. +// Near and far planes are swapped in the case of Z-reversal, but it does not change the algorithm. #define FACE_LEFT (1 << 0) // x = -1 #define FACE_RIGHT (1 << 1) // x = +1 #define FACE_FRONT (1 << 2) // y = -1 @@ -249,7 +249,7 @@ void ClipPolygonAgainstPlane(uint p, uint srcBegin, uint srcSize, // 1. v0 in, v1 out -> add intersection // 2. v0 out, v1 in -> add intersection, add v1 // 3. v0 in, v1 in -> add v1 - // (bc >= 0) <-> in, (bc < 0) <-> out. Beware of the signed zero. + // (bc >= 0) <-> in, (bc < 0) <-> out. Beware of -0. if ((tailVert.bc >= 0) != (leadVert.bc >= 0)) { @@ -287,11 +287,12 @@ void ClipPolygonAgainstPlane(uint p, uint srcBegin, uint srcSize, } } -void ClipFaceAgainstViewVolumeAndUpdateAaBb(uint f, uint behindMasksOfVerts[NUM_VERTS], uint firstVertexOffset, float4x4 g_mInvProjection, - inout float4 ndcAaBbMinPt, inout float4 ndcAaBbMaxPt) +void ClipFaceAgainstViewVolume(uint f, uint behindMasksOfVerts[NUM_VERTS], uint firstVertexOffset, + out uint srcBegin, out uint srcSize, + out float4 vertRingBuffer[MAX_CLIP_VERTS]) { - float4 vertRingBuffer[MAX_CLIP_VERTS]; - uint srcBegin = 0, srcSize = 4; + srcBegin = 0; + srcSize = 4; uint clipMaskOfFace = 0; // Initially in front uint vertMaskOfFace = s_VertMasksOfFaces[f]; @@ -326,7 +327,12 @@ void ClipFaceAgainstViewVolumeAndUpdateAaBb(uint f, uint behindMasksOfVerts[NUM_ clipMaskOfFace ^= 1 << p; // Clear the bit to continue using firstbitlow() } +} +void UpdateAaBb(uint srcBegin, uint srcSize, float4 vertRingBuffer[MAX_CLIP_VERTS], + bool isOrthoProj, float4x4 invProj, + inout float4 ndcAaBbMinPt, inout float4 ndcAaBbMaxPt) +{ #ifdef DUMB_COMPILER uint modSrcIdx = srcBegin % MAX_CLIP_VERTS; #endif @@ -336,17 +342,18 @@ void ClipFaceAgainstViewVolumeAndUpdateAaBb(uint f, uint behindMasksOfVerts[NUM_ #ifndef DUMB_COMPILER uint modSrcIdx = j % MAX_CLIP_VERTS; #endif - float4 hapVert = vertRingBuffer[modSrcIdx]; - float4 hbpVertVS = mul(g_mInvProjection, hapVert); // Just to support orthographic projection - float3 rapVertNDC = saturate(hapVert.xyz * rcp(hapVert.w)); // Must not generate negative values - float rbpVertVSz = hbpVertVS.z * rcp(hbpVertVS.w); + // Clamp to the bounds in case of numerical errors (may still generate -0). + float3 rapVertNDC = saturate(hapVert.xyz * rcp(hapVert.w)); + float rbpVertVSz = hapVert.w; - ndcAaBbMinPt.xyz = min(ndcAaBbMinPt.xyz, rapVertNDC); - ndcAaBbMaxPt.xyz = max(ndcAaBbMaxPt.xyz, rapVertNDC); - ndcAaBbMinPt.w = min(ndcAaBbMinPt.w, rbpVertVSz); - ndcAaBbMaxPt.w = max(ndcAaBbMaxPt.w, rbpVertVSz); + if (isOrthoProj) // Must replace (w = 1) + { + rbpVertVSz = dot(invProj[2], hapVert); + } + ndcAaBbMinPt = min(ndcAaBbMinPt, float4(rapVertNDC, rbpVertVSz)); + ndcAaBbMaxPt = max(ndcAaBbMaxPt, float4(rapVertNDC, rbpVertVSz)); #ifdef DUMB_COMPILER modSrcIdx++; modSrcIdx = (modSrcIdx == MAX_CLIP_VERTS) ? 0 : modSrcIdx; @@ -496,12 +503,11 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // Warning: the W component may be negative. // Flipping the -W pyramid by negating all coordinates is incorrect // and will break both classification and clipping. + // For the orthographic projection, (w = 1). // Transform the X and Y components: [-w, w] -> [0, w]. hapVert.xy = 0.5 * hapVert.xy + (0.5 * hapVert.w); - // TODO: multiply vertex by ViewZ if orthographic for unified processing! - // For each vertex, we must determine whether it is within the bounds. // For culling and clipping, we must know, per culling plane, whether the vertex // is in the positive or the negative half-space. @@ -512,7 +518,6 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // 0 <= y <= w <-- include boundary points to avoid clipping them later // 0 <= z <= w // w is always valid - // For the orthographic projection, (w = 1), so no modifications are necessary. // TODO: epsilon for numerical robustness? for (uint j = 0; j < (NUM_PLANES / 2); j++) @@ -525,12 +530,11 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI if (behindMask == 0) // Inside? { - float3 rapVertNDC = saturate(hapVert.xyz * rcp(hapVert.w)); // Must not generate negative values + // Clamp to the bounds in case of numerical errors (may still generate -0). + float3 rapVertNDC = saturate(hapVert.xyz * rcp(hapVert.w)); - ndcAaBbMinPt.xyz = min(ndcAaBbMinPt.xyz, rapVertNDC); - ndcAaBbMaxPt.xyz = max(ndcAaBbMaxPt.xyz, rapVertNDC); - ndcAaBbMinPt.w = min(ndcAaBbMinPt.w, rbpVertVS.z); - ndcAaBbMaxPt.w = max(ndcAaBbMaxPt.w, rbpVertVS.z); + ndcAaBbMinPt = min(ndcAaBbMinPt, float4(rapVertNDC, rbpVertVS.z)); + ndcAaBbMaxPt = max(ndcAaBbMaxPt, float4(rapVertNDC, rbpVertVS.z)); } else // Outside { @@ -611,7 +615,6 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // -w < y < w <-- exclude boundary points, as we will not clip using these vertices // -w < z < w <-- assume that Z-precision is not very important here // 0 < w - // For the orthographic projection, (w = 1), so no modifications are necessary. // TODO: epsilon for numerical robustness? bool inside = Max3(abs(hapVertLS.x), abs(hapVertLS.y), abs(hapVertLS.z)) < hapVertLS.w; @@ -621,10 +624,8 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI float3 rapVertNDC = float3(rapVertCS.xy * 0.5 + 0.5, rapVertCS.z); float rbpVertVSz = hbpVertVS.z * rcp(hbpVertVS.w); - ndcAaBbMinPt.xyz = min(ndcAaBbMinPt.xyz, rapVertNDC); - ndcAaBbMaxPt.xyz = max(ndcAaBbMaxPt.xyz, rapVertNDC); - ndcAaBbMinPt.w = min(ndcAaBbMinPt.w, rbpVertVSz); - ndcAaBbMaxPt.w = max(ndcAaBbMaxPt.w, rbpVertVSz); + ndcAaBbMinPt = min(ndcAaBbMinPt, float4(rapVertNDC, rbpVertVSz)); + ndcAaBbMaxPt = max(ndcAaBbMaxPt, float4(rapVertNDC, rbpVertVSz)); } } } @@ -680,8 +681,12 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI { uint f = NthBitLow(clipFaceMask, n); - ClipFaceAgainstViewVolumeAndUpdateAaBb(f, behindMasksOfVerts, firstVertexOffset, g_mInvProjection, - ndcAaBbMinPt, ndcAaBbMaxPt); + uint srcBegin, srcSize; + float4 vertRingBuffer[MAX_CLIP_VERTS]; + ClipFaceAgainstViewVolume(f, behindMasksOfVerts, firstVertexOffset, + srcBegin, srcSize, vertRingBuffer); + UpdateAaBb(srcBegin, srcSize, vertRingBuffer, g_isOrthographic != 0, g_mInvProjection, + ndcAaBbMinPt, ndcAaBbMaxPt); } } } @@ -690,7 +695,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // ... #else // Integer comparison works for floating-point numbers as long as the sign bit is 0. - // We must take care of the signed zero ourselves. saturate() does not help here. + // We must take care of -0 ourselves. saturate() does not help here. InterlockedMin(gs_NdcAaBbMinPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.x))); InterlockedMax(gs_NdcAaBbMaxPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.x))); InterlockedMin(gs_NdcAaBbMinPtY[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.y))); From e4d275c7ea890b6d6689b514e1482f04bcaf7dff Mon Sep 17 00:00:00 2001 From: Evgenii Date: Mon, 10 Aug 2020 13:10:23 -0700 Subject: [PATCH 11/24] Optimize LUT --- .../Lighting/LightLoop/scrbound.compute | 144 +++++++++--------- 1 file changed, 71 insertions(+), 73 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index ee373ec6081..8d20322bccf 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -60,7 +60,8 @@ float4x4 Translation4x4(float3 d) return M; } -float3x3 Rotation3x3(float3 xAxis, float3 yAxis, float3 zAxis) +// Scale followed by rotation (scaled axes). +float3x3 ScaledRotation3x3(float3 xAxis, float3 yAxis, float3 zAxis) { float3x3 R = float3x3(xAxis, yAxis, zAxis); float3x3 C = transpose(R); // Row to column @@ -89,7 +90,7 @@ float4x4 Homogenize3x3(float3x3 R) float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) { - float b = (f + n) * rcp(f - n); // z: [-1, 1] + float b = (f + n) * rcp(f - n); // Z in [-1, 1] float c = -2 * f * n * rcp(f - n); // No Z-reversal return float4x4(g/a, 0, 0, 0, @@ -98,7 +99,7 @@ float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) 0, 0, 1, 0); } -#define CLEAR_SIGN_BIT(X) (asuint(X) & INT_MAX) +#define CLEAR_SIGN_BIT(X) (asint(X) & INT_MAX) #define DIV_ROUND_UP(N, D) (((N) + (D) - 1) / (D)) // No division by 0 checks // Clipping a plane by a cube may produce a hexagon (6-gon). @@ -116,43 +117,60 @@ float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) // All planes and faces are always in the standard order (see below). // Near and far planes are swapped in the case of Z-reversal, but it does not change the algorithm. -#define FACE_LEFT (1 << 0) // x = -1 -#define FACE_RIGHT (1 << 1) // x = +1 -#define FACE_FRONT (1 << 2) // y = -1 -#define FACE_BACK (1 << 3) // y = +1 -#define FACE_TOP (1 << 4) // z = -1 -#define FACE_BOTTOM (1 << 5) // z = +1 +#define FACE_LEFT (1 << 0) // -X z +#define FACE_RIGHT (1 << 1) // +X / +#define FACE_TOP (1 << 2) // -Y 0 -- x +#define FACE_BOTTOM (1 << 3) // +Y | +#define FACE_FRONT (1 << 4) // -Z y +#define FACE_BACK (1 << 5) // +Z #define FACE_MASK ((1 << NUM_FACES) - 1) -// TODO: the compiler generates 'tbuffer_load_format_x' instructions -// when we access the look-up tables. Can we avoid this? - -// TODO: try vert order (0 0 0), (1 0 0), (0 1 0), (1 1 0), (0 0 1), (1 0 1), (0 1 1), (1 1 1) +// A list of vertices for each face (CCW order w.r.t. its normal, starting from the LSB). +#define VERT_LIST_LEFT ((2) << 9 | (6) << 6 | (4) << 3 | (0) << 0) +#define VERT_LIST_RIGHT ((5) << 9 | (7) << 6 | (3) << 3 | (1) << 0) +#define VERT_LIST_TOP ((1) << 9 | (3) << 6 | (2) << 3 | (0) << 0) +#define VERT_LIST_BOTTOM ((6) << 9 | (7) << 6 | (5) << 3 | (4) << 0) +#define VERT_LIST_FRONT ((4) << 9 | (5) << 6 | (1) << 3 | (0) << 0) +#define VERT_LIST_BACK ((3) << 9 | (7) << 6 | (6) << 3 | (2) << 0) // All vertices are always in the standard order (see below). -static const uint s_FaceMasksOfVerts[NUM_VERTS] = +uint GetFaceMaskOfVertex(uint v) { - FACE_LEFT | FACE_FRONT | FACE_TOP, // 0: (-1, -1, -1) - FACE_RIGHT | FACE_FRONT | FACE_TOP, // 1: (+1, -1, -1) - FACE_RIGHT | FACE_BACK | FACE_TOP, // 2: (+1, +1, -1) - FACE_LEFT | FACE_BACK | FACE_TOP, // 3: (-1, +1, -1) - FACE_LEFT | FACE_FRONT | FACE_BOTTOM, // 4: (-1, -1, +1) - FACE_RIGHT | FACE_FRONT | FACE_BOTTOM, // 5: (+1, -1, +1) - FACE_RIGHT | FACE_BACK | FACE_BOTTOM, // 6: (+1, +1, +1) - FACE_LEFT | FACE_BACK | FACE_BOTTOM // 7: (-1, +1, +1) + // 0: (-1, -1, -1) -> { FACE_LEFT | FACE_TOP | FACE_FRONT } + // 1: (+1, -1, -1) -> { FACE_RIGHT | FACE_TOP | FACE_FRONT } + // 2: (-1, +1, -1) -> { FACE_LEFT | FACE_BOTTOM | FACE_FRONT } + // 3: (+1, +1, -1) -> { FACE_RIGHT | FACE_BOTTOM | FACE_FRONT } + // 4: (-1, -1, +1) -> { FACE_LEFT | FACE_TOP | FACE_BACK } + // 5: (+1, -1, +1) -> { FACE_RIGHT | FACE_TOP | FACE_BACK } + // 6: (-1, +1, +1) -> { FACE_LEFT | FACE_BOTTOM | FACE_BACK } + // 7: (+1, +1, +1) -> { FACE_RIGHT | FACE_BOTTOM | FACE_BACK } + // ((v & 1) == 0) ? 1 : 2) | ((v & 2) == 0) ? 4 : 8) | ((v & 4) == 0) ? 16 : 32) + uint f = (FACE_LEFT << BitFieldExtract(v, 0, 1)) + | (FACE_TOP << BitFieldExtract(v, 1, 1)) + | (FACE_FRONT << BitFieldExtract(v, 2, 1)); + + return f; }; -// CCW order (starting with the LSB) of vertices for each face (w.r.t. its normal), -// with normals pointing in the interior of the volume. -static const uint s_VertMasksOfFaces[NUM_FACES] = +float3 GenerateVertexOfStandardCube(uint v) { - (3) << 9 | (7) << 6 | (4) << 3 | (0) << 0, // 0: FACE_LEFT - (5) << 9 | (6) << 6 | (2) << 3 | (1) << 0, // 1: FACE_RIGHT - (4) << 9 | (5) << 6 | (1) << 3 | (0) << 0, // 2: FACE_FRONT - (6) << 9 | (7) << 6 | (3) << 3 | (2) << 0, // 3: FACE_BACK - (1) << 9 | (2) << 6 | (3) << 3 | (0) << 0, // 4: FACE_TOP - (7) << 9 | (6) << 6 | (5) << 3 | (4) << 0 // 5: FACE_BOTTOM -}; + float3 p; + + p.x = ((v & 1) == 0) ? -1 : 1; + p.y = ((v & 2) == 0) ? -1 : 1; + p.z = ((v & 4) == 0) ? -1 : 1; + + return p; +} + +uint GetVertexListOfFace(uint f) +{ + static const uint3 allVertLists = uint3((VERT_LIST_RIGHT << 12) | VERT_LIST_LEFT, + (VERT_LIST_BOTTOM << 12) | VERT_LIST_TOP, + (VERT_LIST_BACK << 12) | VERT_LIST_FRONT); + + return BitFieldExtract(allVertLists[f >> 1], 12 * (f & 1), 12); +} // 5 arrays * 128 elements * 4 bytes each = 2560 bytes. groupshared float gs_HapVertsX[VERTS_PER_GROUP]; @@ -182,11 +200,11 @@ groupshared uint gs_NdcAaBbMaxPtW[LIGHTS_PER_GROUP]; // View-space Z coordinate bool TryCullFace(uint f, uint behindMasksOfVerts[NUM_VERTS]) { uint cullMaskOfFace = FACE_MASK; // Initially behind - uint vertMaskOfFace = s_VertMasksOfFaces[f]; + uint vertListOfFace = GetVertexListOfFace(f); for (int j = 0; j < 4; j++) { - uint v = BitFieldExtract(vertMaskOfFace, 3 * j, 3); + uint v = BitFieldExtract(vertListOfFace, 3 * j, 3); // Non-zero if ALL the vertices are behind any of the planes. cullMaskOfFace &= behindMasksOfVerts[v]; } @@ -202,9 +220,9 @@ struct ClipVertex ClipVertex CreateClipVertex(uint p, float4 v) { - bool evenPlane = (p % 2) == 0; + bool evenPlane = (p & 1) == 0; - float c = v[p / 2]; + float c = v[p >> 1]; float w = v.w; ClipVertex cv; @@ -295,7 +313,7 @@ void ClipFaceAgainstViewVolume(uint f, uint behindMasksOfVerts[NUM_VERTS], uint srcSize = 4; uint clipMaskOfFace = 0; // Initially in front - uint vertMaskOfFace = s_VertMasksOfFaces[f]; + uint vertListOfFace = GetVertexListOfFace(f); for (int j = 0; j < 4; j++) { @@ -311,11 +329,9 @@ void ClipFaceAgainstViewVolume(uint f, uint behindMasksOfVerts[NUM_VERTS], uint vertRingBuffer[j].w = gs_HapVertsW[firstVertexOffset + v]; } - const uint numPlanesToClipAgainst = countbits(clipMaskOfFace); // [1, 6] - // Sutherland-Hodgeman polygon clipping algorithm. // It works by clipping the entire polygon against one clipping plane at a time. - for (uint j = 0; j < numPlanesToClipAgainst; j++) + while (clipMaskOfFace != 0) { uint p = firstbitlow(clipMaskOfFace); @@ -336,13 +352,12 @@ void UpdateAaBb(uint srcBegin, uint srcSize, float4 vertRingBuffer[MAX_CLIP_VERT #ifdef DUMB_COMPILER uint modSrcIdx = srcBegin % MAX_CLIP_VERTS; #endif - for (uint j = srcBegin; j < (srcBegin + srcSize); j++) { #ifndef DUMB_COMPILER uint modSrcIdx = j % MAX_CLIP_VERTS; #endif - float4 hapVert = vertRingBuffer[modSrcIdx]; + float4 hapVert = vertRingBuffer[modSrcIdx]; // Clamp to the bounds in case of numerical errors (may still generate -0). float3 rapVertNDC = saturate(hapVert.xyz * rcp(hapVert.w)); float rbpVertVSz = hapVert.w; @@ -477,22 +492,17 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI { uint v = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; - // rbpVerts[0] = rbpC - rbpX * scale.x - rbpY * scale.y - rbpZ; // (-1, -1, -1) - // rbpVerts[1] = rbpC + rbpX * scale.x - rbpY * scale.y - rbpZ; // (+1, -1, -1) - // rbpVerts[2] = rbpC + rbpX * scale.x + rbpY * scale.y - rbpZ; // (+1, +1, -1) - // rbpVerts[3] = rbpC - rbpX * scale.x + rbpY * scale.y - rbpZ; // (-1, +1, -1) - // rbpVerts[4] = rbpC - rbpX - rbpY + rbpZ; // (-1, -1, +1) - // rbpVerts[5] = rbpC + rbpX - rbpY + rbpZ; // (+1, -1, +1) - // rbpVerts[6] = rbpC + rbpX + rbpY + rbpZ; // (+1, +1, +1) - // rbpVerts[7] = rbpC - rbpX + rbpY + rbpZ; // (-1, +1, +1) + // rbpVerts[0] = rbpC - rbpX * scale - rbpY * scale - rbpZ; (-s, -s, -1) + // rbpVerts[1] = rbpC + rbpX * scale - rbpY * scale - rbpZ; (+s, -s, -1) + // rbpVerts[2] = rbpC - rbpX * scale + rbpY * scale - rbpZ; (-s, +s, -1) + // rbpVerts[3] = rbpC + rbpX * scale + rbpY * scale - rbpZ; (+s, +s, -1) + // rbpVerts[4] = rbpC - rbpX - rbpY + rbpZ; (-1, -1, +1) + // rbpVerts[5] = rbpC + rbpX - rbpY + rbpZ; (+1, -1, +1) + // rbpVerts[6] = rbpC - rbpX + rbpY + rbpZ; (-1, +1, +1) + // rbpVerts[7] = rbpC + rbpX + rbpY + rbpZ; (+1, +1, +1) - float3 m; // See the comment above - - m.x = (countbits(v % 4) == 1) ? 1 : -1; - m.y = ((v & 2) != 0) ? 1 : -1; - m.z = (v >= 4) ? 1 : -1; - - m.xy *= (v >= 4) ? 1 : scale; + float3 m = GenerateVertexOfStandardCube(v); + m.xy *= ((v & 4) == 0) ? scale : 1; // X, Y in [-scale, scale] float3 rbpVertVS = rbpC + m.x * rbpX + m.y * rbpY + m.z * rbpZ; // Avoid generating (w = 0). @@ -538,7 +548,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI } else // Outside { - cullClipFaceMask |= s_FaceMasksOfVerts[v]; + cullClipFaceMask |= GetFaceMaskOfVertex(v); } gs_HapVertsX[firstVertexOffset + v] = hapVert.x; @@ -567,7 +577,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI // The light volume is a special type of cuboid - a right frustum. // We can exploit this fact by building a light-space projection matrix. float4x4 invTranslateToLightSpace = Translation4x4(-rbpC); - float4x4 invRotateAndScaleInLightSpace = Homogenize3x3(Invert3x3(Rotation3x3(rbpX, rbpY, rbpZ))); + float4x4 invRotateAndScaleInLightSpace = Homogenize3x3(Invert3x3(ScaledRotation3x3(rbpX, rbpY, rbpZ))); // TODO: avoid full inversion by using unit vectors and passing magnitudes explicitly. // This (orthographic) projection matrix maps a view-space point to a light-space [-1, 1]^3 cube. @@ -592,20 +602,8 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI { uint v = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; - // rapVertsCS[0] = (-1, -1, 0) - // rapVertsCS[1] = (+1, -1, 0) - // rapVertsCS[2] = (+1, +1, 0) - // rapVertsCS[3] = (-1, +1, 0) - // rapVertsCS[4] = (-1, -1, 1) - // rapVertsCS[5] = (+1, -1, 1) - // rapVertsCS[6] = (+1, +1, 1) - // rapVertsCS[7] = (-1, +1, 1) - - float3 rapVertCS; // See the comment above - - rapVertCS.x = (countbits(v % 4) == 1) ? 1 : -1; - rapVertCS.y = ((v & 2) != 0) ? 1 : -1; - rapVertCS.z = (v >= 4) ? 1 : 0; + float3 rapVertCS = GenerateVertexOfStandardCube(v); + rapVertCS.z = rapVertCS.z * 0.5 + 0.5; // View's projection matrix MUST map Z to [0, 1] float4 hbpVertVS = mul(g_mInvProjection, float4(rapVertCS, 1)); // Clip to view space float4 hapVertLS = mul(lightSpaceMatrix, hbpVertVS); // View to light space From e1a5fccc9901689a4aa0b9e4742762a52d54885e Mon Sep 17 00:00:00 2001 From: Evgenii Date: Tue, 11 Aug 2020 12:54:05 -0700 Subject: [PATCH 12/24] Add wave intrinsic support --- .../Lighting/LightLoop/scrbound.compute | 66 ++++++++++++++----- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 8d20322bccf..76a53afb673 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -23,7 +23,6 @@ RWStructuredBuffer g_vBoundsBuffer : register( u0 ); #define Z_BINNING #define DUMB_COMPILER -// #define USE_WAVE_INTRINSICS // We use TGSM and atomic operations if wave intrinsics are not supported #ifdef Z_BINNING @@ -165,9 +164,10 @@ float3 GenerateVertexOfStandardCube(uint v) uint GetVertexListOfFace(uint f) { - static const uint3 allVertLists = uint3((VERT_LIST_RIGHT << 12) | VERT_LIST_LEFT, - (VERT_LIST_BOTTOM << 12) | VERT_LIST_TOP, - (VERT_LIST_BACK << 12) | VERT_LIST_FRONT); + // Warning: don't add 'static' here unless you want really bad code gen. + const uint3 allVertLists = uint3((VERT_LIST_RIGHT << 12) | VERT_LIST_LEFT, + (VERT_LIST_BOTTOM << 12) | VERT_LIST_TOP, + (VERT_LIST_BACK << 12) | VERT_LIST_FRONT); return BitFieldExtract(allVertLists[f >> 1], 12 * (f & 1), 12); } @@ -179,7 +179,7 @@ groupshared float gs_HapVertsZ[VERTS_PER_GROUP]; groupshared float gs_HapVertsW[VERTS_PER_GROUP]; groupshared uint gs_BehindMasksOfVerts[VERTS_PER_GROUP]; // 6 planes each (HLSL does not support small data types) -#ifndef USE_WAVE_INTRINSICS +#ifndef PLATFORM_SUPPORTS_WAVE_INTRINSICS // 1 array * 16 elements * 4 bytes each = 64 bytes. groupshared uint gs_CullClipFaceMasks[LIGHTS_PER_GROUP]; // 6 faces each (HLSL does not support small data types) @@ -194,7 +194,7 @@ groupshared uint gs_NdcAaBbMinPtZ[LIGHTS_PER_GROUP]; // Note that min-max Z can groupshared uint gs_NdcAaBbMaxPtZ[LIGHTS_PER_GROUP]; // from min-max W if the projection is oblique. groupshared uint gs_NdcAaBbMinPtW[LIGHTS_PER_GROUP]; // View-space Z coordinate groupshared uint gs_NdcAaBbMaxPtW[LIGHTS_PER_GROUP]; // View-space Z coordinate -#endif // USE_WAVE_INTRINSICS +#endif // PLATFORM_SUPPORTS_WAVE_INTRINSICS // Returns 'true' if it manages to cull the face. bool TryCullFace(uint f, uint behindMasksOfVerts[NUM_VERTS]) @@ -317,7 +317,7 @@ void ClipFaceAgainstViewVolume(uint f, uint behindMasksOfVerts[NUM_VERTS], uint for (int j = 0; j < 4; j++) { - uint v = BitFieldExtract(vertMaskOfFace, 3 * j, 3); + uint v = BitFieldExtract(vertListOfFace, 3 * j, 3); // Non-zero if ANY of the vertices are behind any of the planes. clipMaskOfFace |= behindMasksOfVerts[v]; @@ -462,7 +462,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI const float3 rbpY = lgtDat.boxAxisY.xyz; // Pre-scaled const float3 rbpZ = lgtDat.boxAxisZ.xyz; // Pre-scaled -#ifndef USE_WAVE_INTRINSICS +#ifndef PLATFORM_SUPPORTS_WAVE_INTRINSICS // (0) Initialize the TGSM. if (t % THREADS_PER_LIGHT == 0) // Avoid bank conflicts { @@ -476,7 +476,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI gs_NdcAaBbMinPtW[groupLocalLightIndex] = asuint(FLT_INF); gs_NdcAaBbMaxPtW[groupLocalLightIndex] = asuint(0.0f); } -#endif // USE_WAVE_INTRINSICS +#endif // PLATFORM_SUPPORTS_WAVE_INTRINSICS float4 ndcAaBbMinPt = float4(1, 1, 1, FLT_INF); float4 ndcAaBbMaxPt = 0; @@ -558,8 +558,15 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI gs_BehindMasksOfVerts[firstVertexOffset + v] = behindMask; } -#ifdef USE_WAVE_INTRINSICS - // ... +#ifdef PLATFORM_SUPPORTS_WAVE_INTRINSICS + for (uint i = 0; i < FastLog2(THREADS_PER_LIGHT); i++) + { + uint andMask = PLATFORM_LANE_COUNT - 1; // All lanes + uint orMask = 0; // Plays no role + uint xorMask = 1 << i; // Flip bits one by one starting from the LSB + // TODO: Francesco - expose the right intrinsic. + cullClipFaceMask |= LaneSwizzle(cullClipFaceMask, orMask, 0, xorMask); + } #else InterlockedOr(gs_CullClipFaceMasks[groupLocalLightIndex], cullClipFaceMask); @@ -628,6 +635,10 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI } } +#ifdef PLATFORM_SUPPORTS_WAVE_INTRINSICS + GroupMemoryBarrierWithGroupSync(); +#endif + uint behindMasksOfVerts[NUM_VERTS]; for (uint i = 0; i < NUM_VERTS; i++) @@ -656,8 +667,15 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI } } -#ifdef USE_WAVE_INTRINSICS - // ... +#ifdef PLATFORM_SUPPORTS_WAVE_INTRINSICS + for (uint i = 0; i < FastLog2(THREADS_PER_LIGHT); i++) + { + uint andMask = PLATFORM_LANE_COUNT - 1; // All lanes + uint orMask = 0; // Plays no role + uint xorMask = 1 << i; // Flip bits one by one starting from the LSB + // TODO: Francesco - expose the right intrinsic. + cullClipFaceMask &= LaneSwizzle(cullClipFaceMask, orMask, 0, xorMask); + } #else InterlockedAnd(gs_CullClipFaceMasks[groupLocalLightIndex], cullClipFaceMask); @@ -689,11 +707,25 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI } } -#ifdef USE_WAVE_INTRINSICS - // ... +#ifdef PLATFORM_SUPPORTS_WAVE_INTRINSICS + for (uint i = 0; i < FastLog2(THREADS_PER_LIGHT); i++) + { + uint andMask = PLATFORM_LANE_COUNT - 1; // All lanes + uint orMask = 0; // Plays no role + uint xorMask = 1 << i; // Flip bits one by one starting from the LSB + // TODO: Francesco - expose the right intrinsic. + ndcAaBbMinPt.x = min(ndcAaBbMinPt.x, LaneSwizzle(ndcAaBbMinPt.x, orMask, 0, xorMask)); + ndcAaBbMaxPt.x = max(ndcAaBbMaxPt.x, LaneSwizzle(ndcAaBbMaxPt.x, orMask, 0, xorMask)); + ndcAaBbMinPt.y = min(ndcAaBbMinPt.y, LaneSwizzle(ndcAaBbMinPt.y, orMask, 0, xorMask)); + ndcAaBbMaxPt.y = max(ndcAaBbMaxPt.y, LaneSwizzle(ndcAaBbMaxPt.y, orMask, 0, xorMask)); + ndcAaBbMinPt.z = min(ndcAaBbMinPt.z, LaneSwizzle(ndcAaBbMinPt.z, orMask, 0, xorMask)); + ndcAaBbMaxPt.z = max(ndcAaBbMaxPt.z, LaneSwizzle(ndcAaBbMaxPt.z, orMask, 0, xorMask)); + ndcAaBbMinPt.w = min(ndcAaBbMinPt.w, LaneSwizzle(ndcAaBbMinPt.w, orMask, 0, xorMask)); + ndcAaBbMaxPt.w = max(ndcAaBbMaxPt.w, LaneSwizzle(ndcAaBbMaxPt.w, orMask, 0, xorMask)); + } #else // Integer comparison works for floating-point numbers as long as the sign bit is 0. - // We must take care of -0 ourselves. saturate() does not help here. + // We must take care of -0 ourselves. saturate() does not help. InterlockedMin(gs_NdcAaBbMinPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.x))); InterlockedMax(gs_NdcAaBbMaxPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.x))); InterlockedMin(gs_NdcAaBbMinPtY[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.y))); @@ -713,7 +745,7 @@ void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupI ndcAaBbMaxPt.z = asfloat(gs_NdcAaBbMaxPtZ[groupLocalLightIndex]); ndcAaBbMinPt.w = asfloat(gs_NdcAaBbMinPtW[groupLocalLightIndex]); ndcAaBbMaxPt.w = asfloat(gs_NdcAaBbMaxPtW[groupLocalLightIndex]); -#endif // USE_WAVE_INTRINSICS +#endif // PLATFORM_SUPPORTS_WAVE_INTRINSICS if (t % THREADS_PER_LIGHT == 0) // Avoid bank conflicts { From c648e2ac35a6b18f122097e7a928ab233b1e86cd Mon Sep 17 00:00:00 2001 From: Evgenii Date: Tue, 11 Aug 2020 16:07:59 -0700 Subject: [PATCH 13/24] Fix group count --- .../Runtime/Lighting/LightLoop/LightLoop.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs index 57f186dbb82..897fa9c4d4b 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs @@ -3083,7 +3083,12 @@ static void GenerateLightsScreenSpaceAABBs(in BuildGPULightListParameters parame ConstantBuffer.Push(cmd, parameters.lightListCB, parameters.screenSpaceAABBShader, HDShaderIDs._ShaderVariablesLightList); - cmd.DispatchCompute(parameters.screenSpaceAABBShader, parameters.screenSpaceAABBKernel, (parameters.totalLightCount + 7) / 8, parameters.viewCount, 1); + const int threadsPerLight = 4; // Shader: THREADS_PER_LIGHT (4) + const int threadsPerGroup = 64; // Shader: THREADS_PER_GROUP (64) + + int groupCount = HDUtils.DivRoundUp(parameters.totalLightCount * threadsPerLight, threadsPerGroup); + + cmd.DispatchCompute(parameters.screenSpaceAABBShader, parameters.screenSpaceAABBKernel, groupCount, parameters.viewCount, 1); } } From d6cfa8fc130d3cf733be3a5c2ea9a37f862cf137 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Tue, 11 Aug 2020 16:14:11 -0700 Subject: [PATCH 14/24] Reduce the kernel count to 1 --- .../Runtime/Lighting/LightLoop/LightLoop.cs | 10 +--------- .../Runtime/Lighting/LightLoop/scrbound.compute | 17 ++++++++++------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs index 897fa9c4d4b..c1850bd6602 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs @@ -696,7 +696,6 @@ enum ClusterDepthSource : int { "TileLightListGen_NoDepthRT_SrcBigTile", "TileLightListGen_DepthRT_SrcBigTile_Oblique", "TileLightListGen_DepthRT_MSAA_SrcBigTile_Oblique" } }; - static int s_GenAABBKernel; static int s_GenListPerTileKernel; static int[,] s_ClusterKernels = new int[(int)ClusterPrepassSource.Count, (int)ClusterDepthSource.Count]; static int[,] s_ClusterObliqueKernels = new int[(int)ClusterPrepassSource.Count, (int)ClusterDepthSource.Count]; @@ -879,8 +878,6 @@ void InitializeLightLoop(IBLFilterBSDF[] iBLFilterBSDFArray) m_MaxLightsOnScreen = m_MaxDirectionalLightsOnScreen + m_MaxPunctualLightsOnScreen + m_MaxAreaLightsOnScreen + m_MaxEnvLightsOnScreen; m_MaxPlanarReflectionOnScreen = lightLoopSettings.maxPlanarReflectionOnScreen; - s_GenAABBKernel = buildScreenAABBShader.FindKernel("ScreenBoundsAABB"); - // Cluster { s_ClearVoxelAtomicKernel = clearClusterAtomicIndexShader.FindKernel("ClearAtomic"); @@ -3403,12 +3400,7 @@ unsafe BuildGPULightListParameters PrepareBuildGPULightListParameters( HDCamera // Screen space AABB parameters.screenSpaceAABBShader = buildScreenAABBShader; - parameters.screenSpaceAABBShader.shaderKeywords = null; - if (isProjectionOblique) - { - parameters.screenSpaceAABBShader.EnableKeyword("USE_OBLIQUE_MODE"); - } - parameters.screenSpaceAABBKernel = s_GenAABBKernel; + parameters.screenSpaceAABBKernel = 0; // Big tile prepass parameters.runBigTilePrepass = hdCamera.frameSettings.IsEnabled(FrameSettingsField.BigTilePrepass); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 76a53afb673..438e5fdc77f 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -1,11 +1,6 @@ // The implementation is based on the demo on "fine pruned tiled lighting" published in GPU Pro 7. // https://github.com/wolfgangfengel/GPU-Pro-7 -#pragma kernel ScreenBoundsAABB - -#pragma multi_compile _ USE_OBLIQUE_MODE - - #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs.hlsl" @@ -14,6 +9,14 @@ // #pragma enable_d3d11_debug_symbols #pragma only_renderers d3d11 playstation xboxone vulkan metal switch +#pragma kernel GenLightAABB + +uniform int g_isOrthographic; +uniform int g_iNrVisibLights; + +uniform float4x4 g_mInvProjectionArr[SHADEROPTIONS_XR_MAX_VIEWS]; +uniform float4x4 g_mProjectionArr[SHADEROPTIONS_XR_MAX_VIEWS]; + StructuredBuffer g_data : register( t0 ); #define NR_THREADS 64 @@ -403,7 +406,7 @@ void CalcBound(out bool2 bIsMinValid, out bool2 bIsMaxValid, out float2 vMin, ou #endif // Z_BINNING [numthreads(NR_THREADS, 1, 1)] -void ScreenBoundsAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID) +void GenLightAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID) { uint groupID = u3GroupID.x; uint eyeIndex = u3GroupID.y; // currently, can only be 0 or 1 @@ -1232,4 +1235,4 @@ void CalcBound(out bool2 bIsMinValid, out bool2 bIsMaxValid, out float2 vMin, ou vMax = A; } -#endif // !Z_BINNING \ No newline at end of file +#endif // !Z_BINNING From f3f540f08d92cf5d5a257f48be84b853deb8f4a1 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Tue, 11 Aug 2020 16:49:23 -0700 Subject: [PATCH 15/24] Remove old code --- .../Lighting/LightLoop/scrbound.compute | 701 +++--------------- 1 file changed, 96 insertions(+), 605 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 438e5fdc77f..86144d43973 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -11,23 +11,21 @@ #pragma kernel GenLightAABB +/* ------------------------------ Inputs ------------------------------------ */ + uniform int g_isOrthographic; uniform int g_iNrVisibLights; uniform float4x4 g_mInvProjectionArr[SHADEROPTIONS_XR_MAX_VIEWS]; uniform float4x4 g_mProjectionArr[SHADEROPTIONS_XR_MAX_VIEWS]; -StructuredBuffer g_data : register( t0 ); - -#define NR_THREADS 64 +StructuredBuffer g_data : register(t0); -// output buffer -RWStructuredBuffer g_vBoundsBuffer : register( u0 ); +/* ------------------------------ Outputs ----------------------------------- */ -#define Z_BINNING -#define DUMB_COMPILER +RWStructuredBuffer g_vBoundsBuffer : register(u0); -#ifdef Z_BINNING +/* ------------------------------ Utilities --------------------------------- */ // Returns the location of the N-th set bit starting from the lowest order bit and working upward. // Slow implementation - do not use for large bit sets. @@ -101,6 +99,10 @@ float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) 0, 0, 1, 0); } +/* ------------------------------ Implementation ---------------------------- */ + +#define DUMB_COMPILER // Improve the quality of generated code + #define CLEAR_SIGN_BIT(X) (asint(X) & INT_MAX) #define DIV_ROUND_UP(N, D) (((N) + (D) - 1) / (D)) // No division by 0 checks @@ -349,7 +351,7 @@ void ClipFaceAgainstViewVolume(uint f, uint behindMasksOfVerts[NUM_VERTS], uint } void UpdateAaBb(uint srcBegin, uint srcSize, float4 vertRingBuffer[MAX_CLIP_VERTS], - bool isOrthoProj, float4x4 invProj, + bool isOrthoProj, float4x4 invProjMat, inout float4 ndcAaBbMinPt, inout float4 ndcAaBbMaxPt) { #ifdef DUMB_COMPILER @@ -367,7 +369,7 @@ void UpdateAaBb(uint srcBegin, uint srcSize, float4 vertRingBuffer[MAX_CLIP_VERT if (isOrthoProj) // Must replace (w = 1) { - rbpVertVSz = dot(invProj[2], hapVert); + rbpVertVSz = dot(invProjMat[2], hapVert); } ndcAaBbMinPt = min(ndcAaBbMinPt, float4(rapVertNDC, rbpVertVSz)); @@ -379,105 +381,70 @@ void UpdateAaBb(uint srcBegin, uint srcSize, float4 vertRingBuffer[MAX_CLIP_VERT } } -#else // !Z_BINNING - -#define THREADS_PER_LIGHT (8) -#define THREADS_PER_GROUP (64) -#define LIGHTS_PER_GROUP (THREADS_PER_GROUP / THREADS_PER_LIGHT) -#define MAX_PNTS (9) // strictly this should be 10=6+4 but we get more wavefronts and 10 seems to never hit (fingers crossed) - // However, worst case the plane that would be skipped if such an extreme case ever happened would be backplane - // clipping gets skipped which doesn't cause any errors. - - -// LDS (2496 bytes) -groupshared float posX[MAX_PNTS*8*2]; -groupshared float posY[MAX_PNTS*8*2]; -groupshared float posZ[MAX_PNTS*8*2]; -groupshared float posW[MAX_PNTS*8*2]; -groupshared unsigned int clipFlags[48]; - +//********************************************************************************************** +// The goal of this program is to compute the AABB of the light in the NDC space ([0, 1] range). +// The light is represented by a convex volume (a cuboid) with 6 faces (planar quads) and 8 vertices. +// +// Since a light volume may be partially off-screen, we must clip it before computing the AABB. +// Clipping the resulting AABB (rather than the light volume itself) may result in a loose AABB. +// +// To avoid having to deal with the "Moebius twist" property of the perspective transform, +// we perform clipping using the homogeneous (projective) post-perspective coordinates. +// This clipping method in described in Blinn's paper titled "Line Clipping". +// +// The algorithm processes a light on 4 threads. While all 6 faces may require clipping in the +// worst case, clipping more than 4 faces is very uncommon (typically, we clip 0, 3 or 4). +// Some faces may require culling rather than clipping (the former is simpler). +// +// It's important to realize that face culling may end up culling 5 (or even all 6) faces. +// This means that the clipped light volume may be reduced to a single polygon, or nothing at all. +// (Imagine a view volume completely or partially inside a light volume). +// Therefore, we must perform view-volume-corner-inside-light-volume tests. +// +// +// Notation: +// rbp - real (3D) coordinates before perspective +// hbp - hom. (4D) coordinates before perspective +// hap - hom. (4D) coordinates after perspective +// rap - real (3D) coordinates after perspective (after division by w) +// ********************************************************************************************* + +[numthreads(THREADS_PER_GROUP, 1, 1)] +void GenLightAABB(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) +{ + const uint t = threadID; + const uint g = groupID.x; + const uint eyeIndex = groupID.y; // Currently, can only be 0 or 1 -unsigned int GetClip(const float4 P); -int ClipAgainstPlane(const int iSrcIndex, const int iNrSrcVerts, const int subLigt, const int p); -void CalcBound(out bool2 bIsMinValid, out bool2 bIsMaxValid, out float2 vMin, out float2 vMax, float4x4 InvProjection, float3 pos_view_space, float r); + const uint intraGroupLightIndex = t / THREADS_PER_LIGHT; + const uint globalLightIndex = g * LIGHTS_PER_GROUP + intraGroupLightIndex; + const uint firstVertexOffset = intraGroupLightIndex * NUM_VERTS; -#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightingConvexHullUtils.hlsl" + const int eyeAdjustedInputOffset = GenerateLightCullDataIndex(globalLightIndex, g_iNrVisibLights, eyeIndex); + const SFiniteLightBound cullData = g_data[eyeAdjustedInputOffset]; -#endif // Z_BINNING + const float4x4 projMat = g_mProjectionArr[eyeIndex]; + const float4x4 invProjMat = g_mInvProjectionArr[eyeIndex]; -[numthreads(NR_THREADS, 1, 1)] -void GenLightAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID) -{ - uint groupID = u3GroupID.x; - uint eyeIndex = u3GroupID.y; // currently, can only be 0 or 1 - - // The g_ is preserved in order to make cross-pipeline (FPTL) updates easier - float4x4 g_mInvProjection = g_mInvProjectionArr[eyeIndex]; - float4x4 g_mProjection = g_mProjectionArr[eyeIndex]; - - //uint vindex = groupID * NR_THREADS + threadID; - unsigned int g = groupID; - unsigned int t = threadID; - - const int subLigt = (uint) (t/THREADS_PER_LIGHT); - const int lgtIndex = subLigt+(uint) g*LIGHTS_PER_GROUP; - const int sideIndex = (uint) (t%8); - - const int eyeAdjustedLgtIndex = GenerateLightCullDataIndex(lgtIndex, g_iNrVisibLights, eyeIndex); - SFiniteLightBound lgtDat = g_data[eyeAdjustedLgtIndex]; - -#ifdef Z_BINNING - //********************************************************************************************** - // The goal of this program is to compute the AABB of the light in the NDC space ([0, 1] range). - // The light is represented by a convex volume (a cuboid) with 6 faces (planar quads) and 8 vertices. - // - // Since a light volume may be partially off-screen, we must clip it before computing the AABB. - // Clipping the resulting AABB (rather than the light volume itself) may result in a loose AABB. - // - // To avoid having to deal with the "Moebius twist" property of the perspective transform, - // we perform clipping using the homogeneous (projective) post-perspective coordinates. - // This clipping method in described in Blinn's paper titled "Line Clipping". - // - // The algorithm processes a light on 4 threads. While all 6 faces may require clipping in the - // worst case, clipping more than 4 faces is very uncommon (typically, we clip 0, 3 or 4). - // Some faces may require culling rather than clipping (the former is simpler). - // - // It's important to realize that face culling may end up culling 5 (or even all 6) faces. - // This means that the clipped light volume may be reduced to a single polygon, or nothing at all. - // (Imagine a view volume completely or partially inside a light volume). - // Therefore, we must perform view-volume-corner-inside-light-volume tests. - // - // - // Notation: - // rbp - real (3D) coordinates before perspective - // hbp - hom. (4D) coordinates before perspective - // hap - hom. (4D) coordinates after perspective - // rap - real (3D) coordinates after perspective (after division by w) - // ********************************************************************************************* - - const uint groupLocalLightIndex = t / THREADS_PER_LIGHT; - const uint firstVertexOffset = NUM_VERTS * groupLocalLightIndex; - - const float scale = lgtDat.scaleXY; // scale.x = scale.y - const float3 rbpC = lgtDat.center.xyz; - // TODO: store X, Y, Scale - const float3 rbpX = lgtDat.boxAxisX.xyz; // Pre-scaled - const float3 rbpY = lgtDat.boxAxisY.xyz; // Pre-scaled - const float3 rbpZ = lgtDat.boxAxisZ.xyz; // Pre-scaled + const float scale = cullData.scaleXY; // scale.x = scale.y + const float3 rbpC = cullData.center.xyz; // View-space + const float3 rbpX = cullData.boxAxisX.xyz; // Pre-scaled + const float3 rbpY = cullData.boxAxisY.xyz; // Pre-scaled + const float3 rbpZ = cullData.boxAxisZ.xyz; // Pre-scaled #ifndef PLATFORM_SUPPORTS_WAVE_INTRINSICS // (0) Initialize the TGSM. if (t % THREADS_PER_LIGHT == 0) // Avoid bank conflicts { - gs_CullClipFaceMasks[groupLocalLightIndex] = 0; // Initially inside - gs_NdcAaBbMinPtX[groupLocalLightIndex] = asuint(1.0f); - gs_NdcAaBbMaxPtX[groupLocalLightIndex] = asuint(0.0f); - gs_NdcAaBbMinPtY[groupLocalLightIndex] = asuint(1.0f); - gs_NdcAaBbMaxPtY[groupLocalLightIndex] = asuint(0.0f); - gs_NdcAaBbMinPtZ[groupLocalLightIndex] = asuint(1.0f); - gs_NdcAaBbMaxPtZ[groupLocalLightIndex] = asuint(0.0f); - gs_NdcAaBbMinPtW[groupLocalLightIndex] = asuint(FLT_INF); - gs_NdcAaBbMaxPtW[groupLocalLightIndex] = asuint(0.0f); + gs_CullClipFaceMasks[intraGroupLightIndex] = 0; // Initially inside + gs_NdcAaBbMinPtX[intraGroupLightIndex] = asuint(1.0f); + gs_NdcAaBbMaxPtX[intraGroupLightIndex] = asuint(0.0f); + gs_NdcAaBbMinPtY[intraGroupLightIndex] = asuint(1.0f); + gs_NdcAaBbMaxPtY[intraGroupLightIndex] = asuint(0.0f); + gs_NdcAaBbMinPtZ[intraGroupLightIndex] = asuint(1.0f); + gs_NdcAaBbMaxPtZ[intraGroupLightIndex] = asuint(0.0f); + gs_NdcAaBbMinPtW[intraGroupLightIndex] = asuint(FLT_INF); + gs_NdcAaBbMaxPtW[intraGroupLightIndex] = asuint(0.0f); } #endif // PLATFORM_SUPPORTS_WAVE_INTRINSICS @@ -511,7 +478,7 @@ void GenLightAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID) // Avoid generating (w = 0). rbpVertVS.z = (abs(rbpVertVS.z) > FLT_MIN) ? rbpVertVS.z : FLT_MIN; - float4 hapVert = mul(g_mProjection, float4(rbpVertVS, 1)); + float4 hapVert = mul(projMat, float4(rbpVertVS, 1)); // Warning: the W component may be negative. // Flipping the -W pyramid by negating all coordinates is incorrect @@ -571,11 +538,11 @@ void GenLightAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID) cullClipFaceMask |= LaneSwizzle(cullClipFaceMask, orMask, 0, xorMask); } #else - InterlockedOr(gs_CullClipFaceMasks[groupLocalLightIndex], cullClipFaceMask); + InterlockedOr(gs_CullClipFaceMasks[intraGroupLightIndex], cullClipFaceMask); GroupMemoryBarrierWithGroupSync(); - cullClipFaceMask = gs_CullClipFaceMasks[groupLocalLightIndex]; + cullClipFaceMask = gs_CullClipFaceMasks[intraGroupLightIndex]; #endif // (2) Test the corners of the view volume. @@ -615,8 +582,8 @@ void GenLightAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID) float3 rapVertCS = GenerateVertexOfStandardCube(v); rapVertCS.z = rapVertCS.z * 0.5 + 0.5; // View's projection matrix MUST map Z to [0, 1] - float4 hbpVertVS = mul(g_mInvProjection, float4(rapVertCS, 1)); // Clip to view space - float4 hapVertLS = mul(lightSpaceMatrix, hbpVertVS); // View to light space + float4 hbpVertVS = mul(invProjMat, float4(rapVertCS, 1)); // Clip to view space + float4 hapVertLS = mul(lightSpaceMatrix, hbpVertVS); // View to light space // Consider the vertex to be inside the light volume if: // -w < x < w @@ -680,11 +647,11 @@ void GenLightAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID) cullClipFaceMask &= LaneSwizzle(cullClipFaceMask, orMask, 0, xorMask); } #else - InterlockedAnd(gs_CullClipFaceMasks[groupLocalLightIndex], cullClipFaceMask); + InterlockedAnd(gs_CullClipFaceMasks[intraGroupLightIndex], cullClipFaceMask); GroupMemoryBarrierWithGroupSync(); - cullClipFaceMask = gs_CullClipFaceMasks[groupLocalLightIndex]; + cullClipFaceMask = gs_CullClipFaceMasks[intraGroupLightIndex]; #endif // (4) Clip the faces. @@ -704,7 +671,7 @@ void GenLightAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID) float4 vertRingBuffer[MAX_CLIP_VERTS]; ClipFaceAgainstViewVolume(f, behindMasksOfVerts, firstVertexOffset, srcBegin, srcSize, vertRingBuffer); - UpdateAaBb(srcBegin, srcSize, vertRingBuffer, g_isOrthographic != 0, g_mInvProjection, + UpdateAaBb(srcBegin, srcSize, vertRingBuffer, g_isOrthographic != 0, invProjMat, ndcAaBbMinPt, ndcAaBbMaxPt); } } @@ -729,510 +696,34 @@ void GenLightAABB(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID) #else // Integer comparison works for floating-point numbers as long as the sign bit is 0. // We must take care of -0 ourselves. saturate() does not help. - InterlockedMin(gs_NdcAaBbMinPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.x))); - InterlockedMax(gs_NdcAaBbMaxPtX[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.x))); - InterlockedMin(gs_NdcAaBbMinPtY[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.y))); - InterlockedMax(gs_NdcAaBbMaxPtY[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.y))); - InterlockedMin(gs_NdcAaBbMinPtZ[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.z))); - InterlockedMax(gs_NdcAaBbMaxPtZ[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.z))); - InterlockedMin(gs_NdcAaBbMinPtW[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.w))); - InterlockedMax(gs_NdcAaBbMaxPtW[groupLocalLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.w))); + InterlockedMin(gs_NdcAaBbMinPtX[intraGroupLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.x))); + InterlockedMax(gs_NdcAaBbMaxPtX[intraGroupLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.x))); + InterlockedMin(gs_NdcAaBbMinPtY[intraGroupLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.y))); + InterlockedMax(gs_NdcAaBbMaxPtY[intraGroupLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.y))); + InterlockedMin(gs_NdcAaBbMinPtZ[intraGroupLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.z))); + InterlockedMax(gs_NdcAaBbMaxPtZ[intraGroupLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.z))); + InterlockedMin(gs_NdcAaBbMinPtW[intraGroupLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMinPt.w))); + InterlockedMax(gs_NdcAaBbMaxPtW[intraGroupLightIndex], asuint(CLEAR_SIGN_BIT(ndcAaBbMaxPt.w))); GroupMemoryBarrierWithGroupSync(); - ndcAaBbMinPt.x = asfloat(gs_NdcAaBbMinPtX[groupLocalLightIndex]); - ndcAaBbMaxPt.x = asfloat(gs_NdcAaBbMaxPtX[groupLocalLightIndex]); - ndcAaBbMinPt.y = asfloat(gs_NdcAaBbMinPtY[groupLocalLightIndex]); - ndcAaBbMaxPt.y = asfloat(gs_NdcAaBbMaxPtY[groupLocalLightIndex]); - ndcAaBbMinPt.z = asfloat(gs_NdcAaBbMinPtZ[groupLocalLightIndex]); - ndcAaBbMaxPt.z = asfloat(gs_NdcAaBbMaxPtZ[groupLocalLightIndex]); - ndcAaBbMinPt.w = asfloat(gs_NdcAaBbMinPtW[groupLocalLightIndex]); - ndcAaBbMaxPt.w = asfloat(gs_NdcAaBbMaxPtW[groupLocalLightIndex]); + ndcAaBbMinPt.x = asfloat(gs_NdcAaBbMinPtX[intraGroupLightIndex]); + ndcAaBbMaxPt.x = asfloat(gs_NdcAaBbMaxPtX[intraGroupLightIndex]); + ndcAaBbMinPt.y = asfloat(gs_NdcAaBbMinPtY[intraGroupLightIndex]); + ndcAaBbMaxPt.y = asfloat(gs_NdcAaBbMaxPtY[intraGroupLightIndex]); + ndcAaBbMinPt.z = asfloat(gs_NdcAaBbMinPtZ[intraGroupLightIndex]); + ndcAaBbMaxPt.z = asfloat(gs_NdcAaBbMaxPtZ[intraGroupLightIndex]); + ndcAaBbMinPt.w = asfloat(gs_NdcAaBbMinPtW[intraGroupLightIndex]); + ndcAaBbMaxPt.w = asfloat(gs_NdcAaBbMaxPtW[intraGroupLightIndex]); #endif // PLATFORM_SUPPORTS_WAVE_INTRINSICS if (t % THREADS_PER_LIGHT == 0) // Avoid bank conflicts { - // Each light's AABB is represented by two float3s, the min and max of the box. - // And for stereo, we have two sets of lights. Therefore, each eye has a set of mins, followed by - // a set of maxs, and each set is equal to g_iNrVisibLights. - const ScreenSpaceBoundsIndices boundsIndices = GenerateScreenSpaceBoundsIndices(lgtIndex, g_iNrVisibLights, eyeIndex); - - g_vBoundsBuffer[boundsIndices.min] = ndcAaBbMinPt; - g_vBoundsBuffer[boundsIndices.max] = ndcAaBbMaxPt; - } - -#else // !Z_BINNING - const float3 boxX = lgtDat.boxAxisX.xyz; - const float3 boxY = lgtDat.boxAxisY.xyz; - const float3 boxZ = -lgtDat.boxAxisZ.xyz; // flip axis (so it points away from the light direction for a spot-light) - const float3 center = lgtDat.center.xyz; - const float radius = lgtDat.radius; - const float2 scaleXY = lgtDat.scaleXY; - - { - if(sideIndex<6 && lgtIndex<(int) g_iNrVisibLights) // mask 2 out of 8 threads - { - float3 q0, q1, q2, q3; - GetHullQuad(q0, q1, q2, q3, boxX, boxY, boxZ, center, scaleXY, sideIndex); - - - const float4 vP0 = mul(g_mProjection, float4(q0, 1)); - const float4 vP1 = mul(g_mProjection, float4(q1, 1)); - const float4 vP2 = mul(g_mProjection, float4(q2, 1)); - const float4 vP3 = mul(g_mProjection, float4(q3, 1)); - - // test vertices of one quad (of the convex hull) for intersection - const unsigned int uFlag0 = GetClip(vP0); - const unsigned int uFlag1 = GetClip(vP1); - const unsigned int uFlag2 = GetClip(vP2); - const unsigned int uFlag3 = GetClip(vP3); - - const float4 vPnts[] = {vP0, vP1, vP2, vP3}; - - // screen-space AABB of one quad (assuming no intersection) - float3 vMin, vMax; - for(int k=0; k<4; k++) - { - float fW = vPnts[k].w; - float fS = fW<0 ? -1 : 1; - float fWabs = fW<0 ? (-fW) : fW; - fW = fS * (fWabs>(i*6))&0x3f; - uFlagAnd &= uClipBits; - uFlagOr |= uClipBits; - } - - uCollectiveAnd &= uFlagAnd; - uCollectiveOr |= uFlagOr; - } - - bool bSetBoundYet = false; - float3 vMin=0.0, vMax=0.0; - if(uCollectiveAnd!=0 || uCollectiveOr==0) // all invisible or all visible (early out) - { - if(uCollectiveOr==0) // all visible - { - for(f=0; f<6; f++) - { - const int sideIndex = f; - - float3 vFaceMi = float3(posX[subLigt*MAX_PNTS*2 + sideIndex + 0], posY[subLigt*MAX_PNTS*2 + sideIndex + 0], posZ[subLigt*MAX_PNTS*2 + sideIndex + 0]); - float3 vFaceMa = float3(posX[subLigt*MAX_PNTS*2 + sideIndex + 6], posY[subLigt*MAX_PNTS*2 + sideIndex + 6], posZ[subLigt*MAX_PNTS*2 + sideIndex + 6]); - - for(int k=0; k<2; k++) - { - float3 vP = k==0 ? vFaceMi : vFaceMa; - if(f==0 && k==0) { vMin=vP; vMax=vP; } - - vMax = max(vMax, vP); vMin = min(vMin, vP); - } - } - bSetBoundYet=true; - } - } - else // :( need true clipping - { - - for(f=0; f<6; f++) - { - float3 q0, q1, q2, q3; - GetHullQuad(q0, q1, q2, q3, boxX, boxY, boxZ, center, scaleXY, f); - - // 4 vertices to a quad of the convex hull in post projection space - const float4 vP0 = mul(g_mProjection, float4(q0, 1)); - const float4 vP1 = mul(g_mProjection, float4(q1, 1)); - const float4 vP2 = mul(g_mProjection, float4(q2, 1)); - const float4 vP3 = mul(g_mProjection, float4(q3, 1)); - - - int iSrcIndex = 0; - - int offs = iSrcIndex*MAX_PNTS+subLigt*MAX_PNTS*2; - - // fill up source clip buffer with the quad - posX[offs+0]=vP0.x; posX[offs+1]=vP1.x; posX[offs+2]=vP2.x; posX[offs+3]=vP3.x; - posY[offs+0]=vP0.y; posY[offs+1]=vP1.y; posY[offs+2]=vP2.y; posY[offs+3]=vP3.y; - posZ[offs+0]=vP0.z; posZ[offs+1]=vP1.z; posZ[offs+2]=vP2.z; posZ[offs+3]=vP3.z; - posW[offs+0]=vP0.w; posW[offs+1]=vP1.w; posW[offs+2]=vP2.w; posW[offs+3]=vP3.w; - - int iNrSrcVerts = 4; - - // do true clipping - for(int p=0; p<6; p++) - { - const int nrVertsDst = ClipAgainstPlane(iSrcIndex, iNrSrcVerts, subLigt, p); - - iSrcIndex = 1-iSrcIndex; - iNrSrcVerts = nrVertsDst; - - if(iNrSrcVerts<3 || iNrSrcVerts>=MAX_PNTS) break; - } - - // final clipped convex primitive is in src buffer - if(iNrSrcVerts>2) - { - int offs_src = iSrcIndex*MAX_PNTS+subLigt*MAX_PNTS*2; - for(int k=0; kradius) - { - float2 vMi, vMa; - bool2 bMi, bMa; - CalcBound(bMi, bMa, vMi, vMa, g_mInvProjection, center, radius); - - vMin.xy = bMi ? max(vMin.xy, vMi) : vMin.xy; - vMax.xy = bMa ? min(vMax.xy, vMa) : vMax.xy; - } - else if(g_isOrthographic!=0) - { - float2 vMi = mul(g_mProjection, float4(center.xyz-radius,1)).xy; // no division needed for ortho - float2 vMa = mul(g_mProjection, float4(center.xyz+radius,1)).xy; // no division needed for ortho - vMin.xy = max(vMin.xy, vMi); - vMax.xy = min(vMax.xy, vMa); - } -#ifndef USE_OBLIQUE_MODE -#if USE_LEFT_HAND_CAMERA_SPACE - if((center.z-radius)>0.0) - { - float4 vPosF = mul(g_mProjection, float4(0,0,center.z-radius,1)); - vMin.z = max(vMin.z, vPosF.z/vPosF.w); - } - if((center.z+radius)>0.0) - { - float4 vPosB = mul(g_mProjection, float4(0,0,center.z+radius,1)); - vMax.z = min(vMax.z, vPosB.z/vPosB.w); - } -#else - if((center.z+radius)<0.0) - { - float4 vPosF = mul(g_mProjection, float4(0,0,center.z+radius,1)); - vMin.z = max(vMin.z, vPosF.z/vPosF.w); - } - if((center.z-radius)<0.0) - { - float4 vPosB = mul(g_mProjection, float4(0,0,center.z-radius,1)); - vMax.z = min(vMax.z, vPosB.z/vPosB.w); - } -#endif - else - { - vMin = float3(-3,-3,-3); - vMax = float3(-2,-2,-2); - } -#endif - } - - - // we should consider doing a look-up here into a max depth mip chain - // to see if the light is occluded: vMin.z*VIEWPORT_SCALE_Z > MipTexelMaxDepth - //g_vBoundsBuffer[lgtIndex+0] = float3(0.5*vMin.x+0.5, -0.5*vMax.y+0.5, vMin.z*VIEWPORT_SCALE_Z); - //g_vBoundsBuffer[lgtIndex+g_iNrVisibLights] = float3(0.5*vMax.x+0.5, -0.5*vMin.y+0.5, vMax.z*VIEWPORT_SCALE_Z); - - // changed for unity - - // Each light's AABB is represented by two float3s, the min and max of the box. - // And for stereo, we have two sets of lights. Therefore, each eye has a set of mins, followed by - // a set of maxs, and each set is equal to g_iNrVisibLights. - const ScreenSpaceBoundsIndices boundsIndices = GenerateScreenSpaceBoundsIndices(lgtIndex, g_iNrVisibLights, eyeIndex); - - // build a linear (in camera space) min/max Z for the aabb. This is needed for clustered when oblique is active - float linMiZ, linMaZ; -#ifndef USE_OBLIQUE_MODE - float2 vMiZW = mul(g_mInvProjection, float4(vMin,1)).zw; - float2 vMaZW = mul(g_mInvProjection, float4(vMax,1)).zw; - linMiZ = vMiZW.x/vMiZW.y; linMaZ = vMaZW.x/vMaZW.y; -#else - for(int i=0; i<8; i++) // establish 8 aabb points in camera space. - { - float3 vP = float3((i&1)!=0 ? vMax.x : vMin.x, (i&2)!=0 ? vMax.y : vMin.y, (i&4)!=0 ? vMax.z : vMin.z); - - float2 v2Pc = mul(g_mInvProjection, float4(vP,1)).zw; - float linZ = v2Pc.x/v2Pc.y; - - if(i==0) { linMiZ=linZ; linMaZ=linZ; } -#if USE_LEFT_HAND_CAMERA_SPACE - linMiZ = min(linMiZ, linZ); linMaZ = max(linMaZ, linZ); -#else - linMiZ = max(linMiZ, linZ); linMaZ = min(linMaZ, linZ); -#endif - } - - float z0 = center.z-radius, z1 = center.z+radius; -#if USE_LEFT_HAND_CAMERA_SPACE - linMiZ = max(linMiZ, z0); linMaZ = min(linMaZ, z1); -#else - linMiZ = min(linMiZ, z1); linMaZ = max(linMaZ, z0); -#endif - -#endif - - g_vBoundsBuffer[boundsIndices.min] = float4(0.5*vMin.x + 0.5, 0.5*vMin.y + 0.5, vMin.z*VIEWPORT_SCALE_Z, linMiZ); - g_vBoundsBuffer[boundsIndices.max] = float4(0.5*vMax.x + 0.5, 0.5*vMax.y + 0.5, vMax.z*VIEWPORT_SCALE_Z, linMaZ); - } - } -#endif // Z_BINNING -} - -#ifndef Z_BINNING - -float4 GenNewVert(const float4 vVisib, const float4 vInvisib, const int p); - -int ClipAgainstPlane(const int iSrcIndex, const int iNrSrcVerts, const int subLigt, const int p) -{ - int offs_src = iSrcIndex*MAX_PNTS+subLigt*MAX_PNTS*2; - int offs_dst = (1-iSrcIndex)*MAX_PNTS+subLigt*MAX_PNTS*2; - - float4 vPrev = float4(posX[offs_src+(iNrSrcVerts-1)], posY[offs_src+(iNrSrcVerts-1)], posZ[offs_src+(iNrSrcVerts-1)], posW[offs_src+(iNrSrcVerts-1)]); - - int nrVertsDst = 0; - - unsigned int uMask = (1<P.w)?2:0) | ((P.y<-P.w)?4:0) | ((P.y>P.w)?8:0) | ((P.z<0)?16:0) | ((P.z>P.w)?32:0)) & (bIsObliqueClipPlane ? 0x1f : 0x3f); -} - -float4 GenNewVert(const float4 vVisib, const float4 vInvisib, const int p) -{ - const float fS = p==4 ? 0 : ((p&1)==0 ? -1 : 1); - const int index = ((uint) p)/2; - float x1 = index==0 ? vVisib.x : (index==1 ? vVisib.y : vVisib.z); - float x0 = index==0 ? vInvisib.x : (index==1 ? vInvisib.y : vInvisib.z); - - //fS*((vVisib.w-vInvisib.w)*t + vInvisib.w) = (x1-x0)*t + x0; - - const float fT = (fS*vInvisib.w-x0)/((x1-x0) - fS*(vVisib.w-vInvisib.w)); - float4 vNew = vVisib*fT + vInvisib*(1-fT); - - // just to be really anal we make sure the clipped against coordinate is precise - if(index==0) vNew.x = fS*vNew.w; - else if(index==1) vNew.y = fS*vNew.w; - else vNew.z = fS*vNew.w; - - return vNew; -} - - -float4 TransformPlaneToPostSpace(float4x4 InvProjection, float4 plane) -{ - return mul(plane, InvProjection); -} - -float4 EvalPlanePair(out bool validPlanes, float2 posXY_in, float r) -{ - // rotate by 90 degrees to avoid potential division by zero - bool bMustFlip = abs(posXY_in.y)0.0; - - return res; } - -void CalcBound(out bool2 bIsMinValid, out bool2 bIsMaxValid, out float2 vMin, out float2 vMax, float4x4 InvProjection, float3 pos_view_space, float r) -{ - bool validX, validY; - float4 planeX = EvalPlanePair(validX, float2(pos_view_space.x, pos_view_space.z), r); - float4 planeY = EvalPlanePair(validY, float2(pos_view_space.y, pos_view_space.z), r); - - -#if USE_LEFT_HAND_CAMERA_SPACE - planeX = planeX.zwxy; // need to swap left/right and top/bottom planes when using left hand system - planeY = planeY.zwxy; -#endif - - bIsMinValid = bool2(planeX.z<0, planeY.z<0) && bool2(validX,validY); - bIsMaxValid = bool2((-planeX.x)<0, (-planeY.x)<0) && bool2(validX,validY); - - // hopefully the compiler takes zeros into account - // should be the case since the transformation in TransformPlaneToPostSpace() - // is done using multiply-adds and not dot product instructions. - float4 planeX0 = TransformPlaneToPostSpace(InvProjection, float4(planeX.x, 0, planeX.y, 0)); - float4 planeX1 = TransformPlaneToPostSpace(InvProjection, float4(planeX.z, 0, planeX.w, 0)); - float4 planeY0 = TransformPlaneToPostSpace(InvProjection, float4(0, planeY.x, planeY.y, 0)); - float4 planeY1 = TransformPlaneToPostSpace(InvProjection, float4(0, planeY.z, planeY.w, 0)); - - - // convert planes to the forms (1,0,0,D) and (0,1,0,D) - // 2D bound is given by -D components - float2 A = -float2(planeX0.w / planeX0.x, planeY0.w / planeY0.y); - float2 B = -float2(planeX1.w / planeX1.x, planeY1.w / planeY1.y); - - // Bound is complete - vMin = B; - vMax = A; -} - -#endif // !Z_BINNING From f1aeb3182125f1d5d01fd76f36f74537b84a7f9c Mon Sep 17 00:00:00 2001 From: Evgenii Date: Tue, 11 Aug 2020 17:25:46 -0700 Subject: [PATCH 16/24] Bounds check --- .../Lighting/LightLoop/LightCullUtils.hlsl | 16 ++++---- .../Lighting/LightLoop/scrbound.compute | 40 +++++++++---------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightCullUtils.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightCullUtils.hlsl index ea8d937ca7c..4a2a69df125 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightCullUtils.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightCullUtils.hlsl @@ -3,31 +3,33 @@ // Used to index into our SFiniteLightBound (g_data) and // LightVolumeData (_LightVolumeData) buffers. -int GenerateLightCullDataIndex(int lightIndex, uint numVisibleLights, uint eyeIndex) +uint GenerateLightCullDataIndex(uint lightIndex, uint numVisibleLights, uint eyeIndex) { + lightIndex = min(lightIndex, numVisibleLights - 1); // Stay within bounds + // For monoscopic, there is just one set of light cull data structs. // In stereo, all of the left eye structs are first, followed by the right eye structs. - const int perEyeBaseIndex = (int)eyeIndex * (int)numVisibleLights; + const uint perEyeBaseIndex = eyeIndex * numVisibleLights; return (perEyeBaseIndex + lightIndex); } struct ScreenSpaceBoundsIndices { - int min; - int max; + uint min; + uint max; }; // The returned values are used to index into our AABB screen space bounding box buffer // Usually named g_vBoundsBuffer. The two values represent the min/max indices. -ScreenSpaceBoundsIndices GenerateScreenSpaceBoundsIndices(int lightIndex, uint numVisibleLights, uint eyeIndex) +ScreenSpaceBoundsIndices GenerateScreenSpaceBoundsIndices(uint lightIndex, uint numVisibleLights, uint eyeIndex) { // In the monoscopic mode, there is one set of bounds (min,max -> 2 * g_iNrVisibLights) // In stereo, there are two sets of bounds (leftMin, leftMax, rightMin, rightMax -> 4 * g_iNrVisibLights) - const int eyeRelativeBase = (int)eyeIndex * 2 * (int)numVisibleLights; + const uint eyeRelativeBase = eyeIndex * 2 * numVisibleLights; ScreenSpaceBoundsIndices indices; indices.min = eyeRelativeBase + lightIndex; - indices.max = eyeRelativeBase + lightIndex + (int)numVisibleLights; + indices.max = indices.min + numVisibleLights; return indices; } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 86144d43973..c804adad4dc 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -13,8 +13,8 @@ /* ------------------------------ Inputs ------------------------------------ */ -uniform int g_isOrthographic; -uniform int g_iNrVisibLights; +uniform uint g_isOrthographic; +uniform uint g_iNrVisibLights; uniform float4x4 g_mInvProjectionArr[SHADEROPTIONS_XR_MAX_VIEWS]; uniform float4x4 g_mProjectionArr[SHADEROPTIONS_XR_MAX_VIEWS]; @@ -207,7 +207,7 @@ bool TryCullFace(uint f, uint behindMasksOfVerts[NUM_VERTS]) uint cullMaskOfFace = FACE_MASK; // Initially behind uint vertListOfFace = GetVertexListOfFace(f); - for (int j = 0; j < 4; j++) + for (uint j = 0; j < 4; j++) { uint v = BitFieldExtract(vertListOfFace, 3 * j, 3); // Non-zero if ALL the vertices are behind any of the planes. @@ -310,7 +310,7 @@ void ClipPolygonAgainstPlane(uint p, uint srcBegin, uint srcSize, } } -void ClipFaceAgainstViewVolume(uint f, uint behindMasksOfVerts[NUM_VERTS], uint firstVertexOffset, +void ClipFaceAgainstViewVolume(uint f, uint behindMasksOfVerts[NUM_VERTS], uint baseVertexOffset, out uint srcBegin, out uint srcSize, out float4 vertRingBuffer[MAX_CLIP_VERTS]) { @@ -320,7 +320,7 @@ void ClipFaceAgainstViewVolume(uint f, uint behindMasksOfVerts[NUM_VERTS], uint uint clipMaskOfFace = 0; // Initially in front uint vertListOfFace = GetVertexListOfFace(f); - for (int j = 0; j < 4; j++) + for (uint j = 0; j < 4; j++) { uint v = BitFieldExtract(vertListOfFace, 3 * j, 3); // Non-zero if ANY of the vertices are behind any of the planes. @@ -328,10 +328,10 @@ void ClipFaceAgainstViewVolume(uint f, uint behindMasksOfVerts[NUM_VERTS], uint // Not all edges may require clipping. However, filtering the vertex list // is somewhat expensive, so we currently don't do it. - vertRingBuffer[j].x = gs_HapVertsX[firstVertexOffset + v]; - vertRingBuffer[j].y = gs_HapVertsY[firstVertexOffset + v]; - vertRingBuffer[j].z = gs_HapVertsZ[firstVertexOffset + v]; - vertRingBuffer[j].w = gs_HapVertsW[firstVertexOffset + v]; + vertRingBuffer[j].x = gs_HapVertsX[baseVertexOffset + v]; + vertRingBuffer[j].y = gs_HapVertsY[baseVertexOffset + v]; + vertRingBuffer[j].z = gs_HapVertsZ[baseVertexOffset + v]; + vertRingBuffer[j].w = gs_HapVertsW[baseVertexOffset + v]; } // Sutherland-Hodgeman polygon clipping algorithm. @@ -418,10 +418,10 @@ void GenLightAABB(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) const uint intraGroupLightIndex = t / THREADS_PER_LIGHT; const uint globalLightIndex = g * LIGHTS_PER_GROUP + intraGroupLightIndex; - const uint firstVertexOffset = intraGroupLightIndex * NUM_VERTS; + const uint baseVertexOffset = intraGroupLightIndex * NUM_VERTS; - const int eyeAdjustedInputOffset = GenerateLightCullDataIndex(globalLightIndex, g_iNrVisibLights, eyeIndex); - const SFiniteLightBound cullData = g_data[eyeAdjustedInputOffset]; + const uint eyeAdjustedInputOffset = GenerateLightCullDataIndex(globalLightIndex, g_iNrVisibLights, eyeIndex); + const SFiniteLightBound cullData = g_data[eyeAdjustedInputOffset]; const float4x4 projMat = g_mProjectionArr[eyeIndex]; const float4x4 invProjMat = g_mInvProjectionArr[eyeIndex]; @@ -521,11 +521,11 @@ void GenLightAABB(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) cullClipFaceMask |= GetFaceMaskOfVertex(v); } - gs_HapVertsX[firstVertexOffset + v] = hapVert.x; - gs_HapVertsY[firstVertexOffset + v] = hapVert.y; - gs_HapVertsZ[firstVertexOffset + v] = hapVert.z; - gs_HapVertsW[firstVertexOffset + v] = hapVert.w; - gs_BehindMasksOfVerts[firstVertexOffset + v] = behindMask; + gs_HapVertsX[baseVertexOffset + v] = hapVert.x; + gs_HapVertsY[baseVertexOffset + v] = hapVert.y; + gs_HapVertsZ[baseVertexOffset + v] = hapVert.z; + gs_HapVertsW[baseVertexOffset + v] = hapVert.w; + gs_BehindMasksOfVerts[baseVertexOffset + v] = behindMask; } #ifdef PLATFORM_SUPPORTS_WAVE_INTRINSICS @@ -613,7 +613,7 @@ void GenLightAABB(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) for (uint i = 0; i < NUM_VERTS; i++) { - behindMasksOfVerts[i] = gs_BehindMasksOfVerts[firstVertexOffset + i]; + behindMasksOfVerts[i] = gs_BehindMasksOfVerts[baseVertexOffset + i]; } // (3) Cull the faces. @@ -669,7 +669,7 @@ void GenLightAABB(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) uint srcBegin, srcSize; float4 vertRingBuffer[MAX_CLIP_VERTS]; - ClipFaceAgainstViewVolume(f, behindMasksOfVerts, firstVertexOffset, + ClipFaceAgainstViewVolume(f, behindMasksOfVerts, baseVertexOffset, srcBegin, srcSize, vertRingBuffer); UpdateAaBb(srcBegin, srcSize, vertRingBuffer, g_isOrthographic != 0, invProjMat, ndcAaBbMinPt, ndcAaBbMaxPt); @@ -717,7 +717,7 @@ void GenLightAABB(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) ndcAaBbMaxPt.w = asfloat(gs_NdcAaBbMaxPtW[intraGroupLightIndex]); #endif // PLATFORM_SUPPORTS_WAVE_INTRINSICS - if (t % THREADS_PER_LIGHT == 0) // Avoid bank conflicts + if ((globalLightIndex < g_iNrVisibLights) && (t % THREADS_PER_LIGHT == 0)) // Avoid bank conflicts { // For stereo, we have two sets of lights. Therefore, each eye has a set of mins // followed by a set of maxs, and each set is equal to g_iNrVisibLights. From d2f3fdaf491a91aad220090dfc12a4599bdb9612 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Tue, 11 Aug 2020 17:39:14 -0700 Subject: [PATCH 17/24] Add a profiling marker --- .../Runtime/Lighting/LightLoop/LightLoop.cs | 19 +++++++++++-------- .../Lighting/LightLoop/scrbound.compute | 4 ++-- .../Runtime/RenderPipeline/HDProfileId.cs | 1 + 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs index c1850bd6602..5ee6f7d067c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs @@ -3074,18 +3074,21 @@ static void GenerateLightsScreenSpaceAABBs(in BuildGPULightListParameters parame { if (parameters.totalLightCount != 0) { - // With XR single-pass, we have one set of light bounds per view to iterate over (bounds are in view space for each view) - cmd.SetComputeBufferParam(parameters.screenSpaceAABBShader, parameters.screenSpaceAABBKernel, HDShaderIDs.g_data, resources.convexBoundsBuffer); - cmd.SetComputeBufferParam(parameters.screenSpaceAABBShader, parameters.screenSpaceAABBKernel, HDShaderIDs.g_vBoundsBuffer, resources.AABBBoundsBuffer); + using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.GenerateLightAABBs))) + { + // With XR single-pass, we have one set of light bounds per view to iterate over (bounds are in view space for each view) + cmd.SetComputeBufferParam(parameters.screenSpaceAABBShader, parameters.screenSpaceAABBKernel, HDShaderIDs.g_data, resources.convexBoundsBuffer); + cmd.SetComputeBufferParam(parameters.screenSpaceAABBShader, parameters.screenSpaceAABBKernel, HDShaderIDs.g_vBoundsBuffer, resources.AABBBoundsBuffer); - ConstantBuffer.Push(cmd, parameters.lightListCB, parameters.screenSpaceAABBShader, HDShaderIDs._ShaderVariablesLightList); + ConstantBuffer.Push(cmd, parameters.lightListCB, parameters.screenSpaceAABBShader, HDShaderIDs._ShaderVariablesLightList); - const int threadsPerLight = 4; // Shader: THREADS_PER_LIGHT (4) - const int threadsPerGroup = 64; // Shader: THREADS_PER_GROUP (64) + const int threadsPerLight = 4; // Shader: THREADS_PER_LIGHT (4) + const int threadsPerGroup = 64; // Shader: THREADS_PER_GROUP (64) - int groupCount = HDUtils.DivRoundUp(parameters.totalLightCount * threadsPerLight, threadsPerGroup); + int groupCount = HDUtils.DivRoundUp(parameters.totalLightCount * threadsPerLight, threadsPerGroup); - cmd.DispatchCompute(parameters.screenSpaceAABBShader, parameters.screenSpaceAABBKernel, groupCount, parameters.viewCount, 1); + cmd.DispatchCompute(parameters.screenSpaceAABBShader, parameters.screenSpaceAABBKernel, groupCount, parameters.viewCount, 1); + } } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index c804adad4dc..233876c830c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -9,7 +9,7 @@ // #pragma enable_d3d11_debug_symbols #pragma only_renderers d3d11 playstation xboxone vulkan metal switch -#pragma kernel GenLightAABB +#pragma kernel main /* ------------------------------ Inputs ------------------------------------ */ @@ -410,7 +410,7 @@ void UpdateAaBb(uint srcBegin, uint srcSize, float4 vertRingBuffer[MAX_CLIP_VERT // ********************************************************************************************* [numthreads(THREADS_PER_GROUP, 1, 1)] -void GenLightAABB(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) +void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) { const uint t = threadID; const uint g = groupID.x; diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs index 2a743e973ef..54f95210d35 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs @@ -18,6 +18,7 @@ internal enum HDProfileId ScreenSpaceShadows, ScreenSpaceShadowsDebug, BuildLightList, + GenerateLightAABBs, ContactShadows, BlitToFinalRTDevBuildOnly, Distortion, From 6aefaf3ad8aa020e7a5b7e43bb3175805f3e9a49 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Tue, 11 Aug 2020 17:58:14 -0700 Subject: [PATCH 18/24] Fix lane masks --- .../Lighting/LightLoop/scrbound.compute | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 233876c830c..585441e30c0 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -535,7 +535,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) uint orMask = 0; // Plays no role uint xorMask = 1 << i; // Flip bits one by one starting from the LSB // TODO: Francesco - expose the right intrinsic. - cullClipFaceMask |= LaneSwizzle(cullClipFaceMask, orMask, 0, xorMask); + cullClipFaceMask |= LaneSwizzle(cullClipFaceMask, andMask, orMask, xorMask); } #else InterlockedOr(gs_CullClipFaceMasks[intraGroupLightIndex], cullClipFaceMask); @@ -644,7 +644,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) uint orMask = 0; // Plays no role uint xorMask = 1 << i; // Flip bits one by one starting from the LSB // TODO: Francesco - expose the right intrinsic. - cullClipFaceMask &= LaneSwizzle(cullClipFaceMask, orMask, 0, xorMask); + cullClipFaceMask &= LaneSwizzle(cullClipFaceMask, andMask, orMask, xorMask); } #else InterlockedAnd(gs_CullClipFaceMasks[intraGroupLightIndex], cullClipFaceMask); @@ -684,14 +684,14 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) uint orMask = 0; // Plays no role uint xorMask = 1 << i; // Flip bits one by one starting from the LSB // TODO: Francesco - expose the right intrinsic. - ndcAaBbMinPt.x = min(ndcAaBbMinPt.x, LaneSwizzle(ndcAaBbMinPt.x, orMask, 0, xorMask)); - ndcAaBbMaxPt.x = max(ndcAaBbMaxPt.x, LaneSwizzle(ndcAaBbMaxPt.x, orMask, 0, xorMask)); - ndcAaBbMinPt.y = min(ndcAaBbMinPt.y, LaneSwizzle(ndcAaBbMinPt.y, orMask, 0, xorMask)); - ndcAaBbMaxPt.y = max(ndcAaBbMaxPt.y, LaneSwizzle(ndcAaBbMaxPt.y, orMask, 0, xorMask)); - ndcAaBbMinPt.z = min(ndcAaBbMinPt.z, LaneSwizzle(ndcAaBbMinPt.z, orMask, 0, xorMask)); - ndcAaBbMaxPt.z = max(ndcAaBbMaxPt.z, LaneSwizzle(ndcAaBbMaxPt.z, orMask, 0, xorMask)); - ndcAaBbMinPt.w = min(ndcAaBbMinPt.w, LaneSwizzle(ndcAaBbMinPt.w, orMask, 0, xorMask)); - ndcAaBbMaxPt.w = max(ndcAaBbMaxPt.w, LaneSwizzle(ndcAaBbMaxPt.w, orMask, 0, xorMask)); + ndcAaBbMinPt.x = min(ndcAaBbMinPt.x, LaneSwizzle(ndcAaBbMinPt.x, andMask, orMask, xorMask)); + ndcAaBbMaxPt.x = max(ndcAaBbMaxPt.x, LaneSwizzle(ndcAaBbMaxPt.x, andMask, orMask, xorMask)); + ndcAaBbMinPt.y = min(ndcAaBbMinPt.y, LaneSwizzle(ndcAaBbMinPt.y, andMask, orMask, xorMask)); + ndcAaBbMaxPt.y = max(ndcAaBbMaxPt.y, LaneSwizzle(ndcAaBbMaxPt.y, andMask, orMask, xorMask)); + ndcAaBbMinPt.z = min(ndcAaBbMinPt.z, LaneSwizzle(ndcAaBbMinPt.z, andMask, orMask, xorMask)); + ndcAaBbMaxPt.z = max(ndcAaBbMaxPt.z, LaneSwizzle(ndcAaBbMaxPt.z, andMask, orMask, xorMask)); + ndcAaBbMinPt.w = min(ndcAaBbMinPt.w, LaneSwizzle(ndcAaBbMinPt.w, andMask, orMask, xorMask)); + ndcAaBbMaxPt.w = max(ndcAaBbMaxPt.w, LaneSwizzle(ndcAaBbMaxPt.w, andMask, orMask, xorMask)); } #else // Integer comparison works for floating-point numbers as long as the sign bit is 0. From 157cd8eb936e358a53c87feb94667496cf55e827 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Tue, 11 Aug 2020 18:44:10 -0700 Subject: [PATCH 19/24] Fix compiler warning --- .../Lighting/LightLoop/scrbound.compute | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 585441e30c0..7cdeab66b02 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -101,7 +101,7 @@ float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) /* ------------------------------ Implementation ---------------------------- */ -#define DUMB_COMPILER // Improve the quality of generated code +#define DUMB_COMPILER // Improve the quality of generated code at the expense of readability #define CLEAR_SIGN_BIT(X) (asint(X) & INT_MAX) #define DIV_ROUND_UP(N, D) (((N) + (D) - 1) / (D)) // No division by 0 checks @@ -259,10 +259,10 @@ void ClipPolygonAgainstPlane(uint p, uint srcBegin, uint srcSize, uint modDstIdx = dstBegin % MAX_CLIP_VERTS; #endif - for (uint k = srcBegin; k < (srcBegin + srcSize); k++) + for (uint j = srcBegin; j < (srcBegin + srcSize); j++) { #ifndef DUMB_COMPILER - uint modSrcIdx = k % MAX_CLIP_VERTS; + uint modSrcIdx = j % MAX_CLIP_VERTS; #endif ClipVertex leadVert = CreateClipVertex(p, vertRingBuffer[modSrcIdx]); @@ -457,8 +457,10 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) // any single plane, we can trivially reject (cull) that face. uint cullClipFaceMask = 0; // Initially inside + uint i; // Avoid multiply-declared variable warning + // (1) Compute the vertices of the light volume. - for (uint i = 0; i < VERTS_PER_THREAD; i++) + for (i = 0; i < VERTS_PER_THREAD; i++) { uint v = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; @@ -529,7 +531,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) } #ifdef PLATFORM_SUPPORTS_WAVE_INTRINSICS - for (uint i = 0; i < FastLog2(THREADS_PER_LIGHT); i++) + for (i = 0; i < FastLog2(THREADS_PER_LIGHT); i++) { uint andMask = PLATFORM_LANE_COUNT - 1; // All lanes uint orMask = 0; // Plays no role @@ -575,7 +577,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) lightSpaceMatrix = mul(mul(perspProjMatrix, invTranslateEye), lightSpaceMatrix); } - for (uint i = 0; i < VERTS_PER_THREAD; i++) + for (i = 0; i < VERTS_PER_THREAD; i++) { uint v = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; @@ -611,7 +613,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) uint behindMasksOfVerts[NUM_VERTS]; - for (uint i = 0; i < NUM_VERTS; i++) + for (i = 0; i < NUM_VERTS; i++) { behindMasksOfVerts[i] = gs_BehindMasksOfVerts[baseVertexOffset + i]; } @@ -621,7 +623,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) const uint cullFaceMask = cullClipFaceMask; const uint numFacesToCull = countbits(cullFaceMask); // [0, 6] - for (uint i = 0; i < FACES_PER_THREAD; i++) + for (i = 0; i < FACES_PER_THREAD; i++) { uint n = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; @@ -638,7 +640,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) } #ifdef PLATFORM_SUPPORTS_WAVE_INTRINSICS - for (uint i = 0; i < FastLog2(THREADS_PER_LIGHT); i++) + for (i = 0; i < FastLog2(THREADS_PER_LIGHT); i++) { uint andMask = PLATFORM_LANE_COUNT - 1; // All lanes uint orMask = 0; // Plays no role @@ -659,7 +661,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) const uint clipFaceMask = cullClipFaceMask; const uint numFacesToClip = countbits(clipFaceMask); // [0, 6] - for (uint i = 0; i < FACES_PER_THREAD; i++) + for (i = 0; i < FACES_PER_THREAD; i++) { uint n = i * THREADS_PER_LIGHT + t % THREADS_PER_LIGHT; @@ -678,7 +680,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) } #ifdef PLATFORM_SUPPORTS_WAVE_INTRINSICS - for (uint i = 0; i < FastLog2(THREADS_PER_LIGHT); i++) + for (i = 0; i < FastLog2(THREADS_PER_LIGHT); i++) { uint andMask = PLATFORM_LANE_COUNT - 1; // All lanes uint orMask = 0; // Plays no role From a2da9243e15442f906a645a21663301d611fcceb Mon Sep 17 00:00:00 2001 From: Evgenii Date: Tue, 11 Aug 2020 18:56:38 -0700 Subject: [PATCH 20/24] Remove GPU Pro reference --- .../Runtime/Lighting/LightLoop/scrbound.compute | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 7cdeab66b02..26783afb163 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -1,16 +1,13 @@ -// The implementation is based on the demo on "fine pruned tiled lighting" published in GPU Pro 7. -// https://github.com/wolfgangfengel/GPU-Pro-7 +// #pragma enable_d3d11_debug_symbols +#pragma only_renderers d3d11 playstation xboxone vulkan metal switch + +#pragma kernel main #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.high-definition-config/Runtime/ShaderConfig.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightCullUtils.hlsl" -// #pragma enable_d3d11_debug_symbols -#pragma only_renderers d3d11 playstation xboxone vulkan metal switch - -#pragma kernel main - /* ------------------------------ Inputs ------------------------------------ */ uniform uint g_isOrthographic; From 70859f772d85c17f665f535b5494e8feebcfdd54 Mon Sep 17 00:00:00 2001 From: Evgenii Date: Wed, 16 Sep 2020 19:06:17 -0700 Subject: [PATCH 21/24] Be politically correct --- .../Lighting/LightLoop/scrbound.compute | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 26783afb163..7bbb2d44b97 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -10,12 +10,6 @@ /* ------------------------------ Inputs ------------------------------------ */ -uniform uint g_isOrthographic; -uniform uint g_iNrVisibLights; - -uniform float4x4 g_mInvProjectionArr[SHADEROPTIONS_XR_MAX_VIEWS]; -uniform float4x4 g_mProjectionArr[SHADEROPTIONS_XR_MAX_VIEWS]; - StructuredBuffer g_data : register(t0); /* ------------------------------ Outputs ----------------------------------- */ @@ -50,7 +44,7 @@ uint NthBitLow(uint value, uint n) float4x4 Translation4x4(float3 d) { - float4x4 M = k_Identity4x4; + float4x4 M = k_identity4x4; M._14_24_34 = d; // Last column @@ -98,7 +92,7 @@ float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) /* ------------------------------ Implementation ---------------------------- */ -#define DUMB_COMPILER // Improve the quality of generated code at the expense of readability +#define OBTUSE_COMPILER // Improve the quality of generated code at the expense of readability #define CLEAR_SIGN_BIT(X) (asint(X) & INT_MAX) #define DIV_ROUND_UP(N, D) (((N) + (D) - 1) / (D)) // No division by 0 checks @@ -251,14 +245,14 @@ void ClipPolygonAgainstPlane(uint p, uint srcBegin, uint srcSize, ClipVertex tailVert = CreateClipVertex(p, vertRingBuffer[(srcBegin + srcSize - 1) % MAX_CLIP_VERTS]); -#ifdef DUMB_COMPILER +#ifdef OBTUSE_COMPILER uint modSrcIdx = srcBegin % MAX_CLIP_VERTS; uint modDstIdx = dstBegin % MAX_CLIP_VERTS; #endif for (uint j = srcBegin; j < (srcBegin + srcSize); j++) { - #ifndef DUMB_COMPILER + #ifndef OBTUSE_COMPILER uint modSrcIdx = j % MAX_CLIP_VERTS; #endif ClipVertex leadVert = CreateClipVertex(p, vertRingBuffer[modSrcIdx]); @@ -275,11 +269,11 @@ void ClipPolygonAgainstPlane(uint p, uint srcBegin, uint srcSize, { // The line segment is guaranteed to cross the plane. float4 clipVert = IntersectEdgeAgainstPlane(tailVert, leadVert); - #ifndef DUMB_COMPILER + #ifndef OBTUSE_COMPILER uint modDstIdx = (dstBegin + dstSize++) % MAX_CLIP_VERTS; #endif vertRingBuffer[modDstIdx] = clipVert; - #ifdef DUMB_COMPILER + #ifdef OBTUSE_COMPILER dstSize++; modDstIdx++; modDstIdx = (modDstIdx == MAX_CLIP_VERTS) ? 0 : modDstIdx; @@ -288,18 +282,18 @@ void ClipPolygonAgainstPlane(uint p, uint srcBegin, uint srcSize, if (leadVert.bc >= 0) { - #ifndef DUMB_COMPILER + #ifndef OBTUSE_COMPILER uint modDstIdx = (dstBegin + dstSize++) % MAX_CLIP_VERTS; #endif vertRingBuffer[modDstIdx] = leadVert.pt; - #ifdef DUMB_COMPILER + #ifdef OBTUSE_COMPILER dstSize++; modDstIdx++; modDstIdx = (modDstIdx == MAX_CLIP_VERTS) ? 0 : modDstIdx; #endif } - #ifdef DUMB_COMPILER + #ifdef OBTUSE_COMPILER modSrcIdx++; modSrcIdx = (modSrcIdx == MAX_CLIP_VERTS) ? 0 : modSrcIdx; #endif @@ -351,12 +345,12 @@ void UpdateAaBb(uint srcBegin, uint srcSize, float4 vertRingBuffer[MAX_CLIP_VERT bool isOrthoProj, float4x4 invProjMat, inout float4 ndcAaBbMinPt, inout float4 ndcAaBbMaxPt) { -#ifdef DUMB_COMPILER +#ifdef OBTUSE_COMPILER uint modSrcIdx = srcBegin % MAX_CLIP_VERTS; #endif for (uint j = srcBegin; j < (srcBegin + srcSize); j++) { - #ifndef DUMB_COMPILER + #ifndef OBTUSE_COMPILER uint modSrcIdx = j % MAX_CLIP_VERTS; #endif float4 hapVert = vertRingBuffer[modSrcIdx]; @@ -371,7 +365,7 @@ void UpdateAaBb(uint srcBegin, uint srcSize, float4 vertRingBuffer[MAX_CLIP_VERT ndcAaBbMinPt = min(ndcAaBbMinPt, float4(rapVertNDC, rbpVertVSz)); ndcAaBbMaxPt = max(ndcAaBbMaxPt, float4(rapVertNDC, rbpVertVSz)); - #ifdef DUMB_COMPILER + #ifdef OBTUSE_COMPILER modSrcIdx++; modSrcIdx = (modSrcIdx == MAX_CLIP_VERTS) ? 0 : modSrcIdx; #endif From 6e8a605c943a31035d5c19c4c59073837a24e60f Mon Sep 17 00:00:00 2001 From: Evgenii Date: Wed, 26 Aug 2020 12:00:28 -0700 Subject: [PATCH 22/24] No instrinsics on Xbox --- .../Lighting/LightLoop/scrbound.compute | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute index 7bbb2d44b97..62ed317dad9 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/scrbound.compute @@ -92,7 +92,19 @@ float4x4 PerspectiveProjection4x4(float a, float g, float n, float f) /* ------------------------------ Implementation ---------------------------- */ -#define OBTUSE_COMPILER // Improve the quality of generated code at the expense of readability +// Improve the quality of generated code at the expense of readability. +// Remove when the shader compiler is clever enough to perform this optimization for us. +#define OBTUSE_COMPILER + +#ifdef SHADER_API_XBOXONE +// The Xbox shader compiler expects the lane swizzle mask to be a compile-time constant. +// In our case, the mask is a compile-time constant, but it is defined inside a loop +// that is unrolled at the compile time, and the constants are generated during the +// constant propagation pass of the optimizer. This works fine on PlayStation, but does not work +// on Xbox. In order to avoid writing hideous code specifically for Xbox, we disable the support +// of wave intrinsics on Xbox until the Xbox compiler is fixed. +#undef PLATFORM_SUPPORTS_WAVE_INTRINSICS +#endif #define CLEAR_SIGN_BIT(X) (asint(X) & INT_MAX) #define DIV_ROUND_UP(N, D) (((N) + (D) - 1) / (D)) // No division by 0 checks @@ -527,7 +539,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) uint andMask = PLATFORM_LANE_COUNT - 1; // All lanes uint orMask = 0; // Plays no role uint xorMask = 1 << i; // Flip bits one by one starting from the LSB - // TODO: Francesco - expose the right intrinsic. + cullClipFaceMask |= LaneSwizzle(cullClipFaceMask, andMask, orMask, xorMask); } #else @@ -636,7 +648,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) uint andMask = PLATFORM_LANE_COUNT - 1; // All lanes uint orMask = 0; // Plays no role uint xorMask = 1 << i; // Flip bits one by one starting from the LSB - // TODO: Francesco - expose the right intrinsic. + cullClipFaceMask &= LaneSwizzle(cullClipFaceMask, andMask, orMask, xorMask); } #else @@ -676,7 +688,7 @@ void main(uint threadID : SV_GroupIndex, uint3 groupID : SV_GroupID) uint andMask = PLATFORM_LANE_COUNT - 1; // All lanes uint orMask = 0; // Plays no role uint xorMask = 1 << i; // Flip bits one by one starting from the LSB - // TODO: Francesco - expose the right intrinsic. + ndcAaBbMinPt.x = min(ndcAaBbMinPt.x, LaneSwizzle(ndcAaBbMinPt.x, andMask, orMask, xorMask)); ndcAaBbMaxPt.x = max(ndcAaBbMaxPt.x, LaneSwizzle(ndcAaBbMaxPt.x, andMask, orMask, xorMask)); ndcAaBbMinPt.y = min(ndcAaBbMinPt.y, LaneSwizzle(ndcAaBbMinPt.y, andMask, orMask, xorMask)); From 0165f68760fe02b4312727384d3aadea26e994f8 Mon Sep 17 00:00:00 2001 From: sebastienlagarde Date: Tue, 29 Sep 2020 12:33:19 +0200 Subject: [PATCH 23/24] Update CHANGELOG.md --- com.unity.render-pipelines.high-definition/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index c3ae54de7f0..dfb7ad5639f 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -101,6 +101,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Moved SSGI out of preview. - Skip an unneeded depth buffer copy on consoles. - Replaced the Density Volume Texture Tool with the new 3D Texture Importer. +- Improve performance of GPU light AABB generation ## [10.0.0] - 2019-06-10 From b6a1ba99bca84f654d67ce6a13885a1833d77a92 Mon Sep 17 00:00:00 2001 From: Sebastien Lagarde Date: Wed, 30 Sep 2020 19:31:37 +0200 Subject: [PATCH 24/24] update reference screenshots for mac --- .../Linear/OSXEditor/Metal/None/1303_StackLitSG_Testers_a.png | 4 ++-- .../Linear/OSXEditor/Metal/None/1303_StackLitSG_Testers_b.png | 4 ++-- .../Linear/OSXEditor/Metal/None/1501_EyeTestSG.png | 4 ++-- .../OSXEditor/Metal/None/2204_ReflectionProbes_Lights.png | 4 ++-- .../OSXEditor/Metal/None/2305_Contact_Shadow_PointLight.png | 4 ++-- .../OSXEditor/Metal/None/2306_Contact_Shadow_SpotLight.png | 4 ++-- .../Linear/OSXEditor/Metal/None/2311_ShadowMaps.png | 4 ++-- .../OSXEditor/Metal/None/5003_Fog_DensityVolumesShadows.png | 4 ++-- .../Linear/OSXEditor/Metal/None/9004_MultiViewport.png | 4 ++-- .../Linear/OSXEditor/Metal/None/9401_MSAAForwardBoth.png | 4 ++-- .../Linear/OSXEditor/Metal/None/9401_MSAAForwardOnly.png | 4 ++-- .../OSXEditor/Metal/None/9701_CustomPass_DrawRenderers.png | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1303_StackLitSG_Testers_a.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1303_StackLitSG_Testers_a.png index 981fc7ccb1f..4f38f80f606 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1303_StackLitSG_Testers_a.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1303_StackLitSG_Testers_a.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e45e4b1c3e5f34c9d74e7bdafa0068df148df97ea17d973a23c9873387670d5e -size 237826 +oid sha256:1ab13e322c857384d29581d4f5e7dc2344458601b505ccd63c609b3b9cbe38d6 +size 248654 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1303_StackLitSG_Testers_b.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1303_StackLitSG_Testers_b.png index 7d328dea026..40b37677aba 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1303_StackLitSG_Testers_b.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1303_StackLitSG_Testers_b.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:619e8b7d51809b4b9faaa99b6669450dfe2886b9ba2d4b76f697331650836bfc -size 264539 +oid sha256:469857bef2f12f3b24b98970eb3125264669d8440398df615a2e9f3c307d380b +size 277997 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1501_EyeTestSG.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1501_EyeTestSG.png index 7cf1d73c307..21e81ece8a9 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1501_EyeTestSG.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1501_EyeTestSG.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f1c2bf72c163965563b0a5dd51d131304fe25cfb6230eecde328ddc24a9c6780 -size 127138 +oid sha256:1865ed7f9ea0104218196f641c970407b582ca82424fcd730fa45a8e9bcce9d8 +size 139848 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2204_ReflectionProbes_Lights.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2204_ReflectionProbes_Lights.png index 837737e0782..e1032dfea07 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2204_ReflectionProbes_Lights.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2204_ReflectionProbes_Lights.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b276b9d47c142761d3b73e53446ba1b7a7a3d7628629b1972c54518f081445b -size 40489 +oid sha256:55ea2ee1127f6edbc7b0cf40ac8bf1cfee3e7ac7b4e0d11134bc746562a20795 +size 45156 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2305_Contact_Shadow_PointLight.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2305_Contact_Shadow_PointLight.png index 0db9cc11ae7..32b7a3b00fb 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2305_Contact_Shadow_PointLight.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2305_Contact_Shadow_PointLight.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9793cfdb451273d2e96965f1775acb767815d9640fc5182c260f86a18a3d0d97 -size 11936 +oid sha256:d53feb80168ce1ff3465dcdc022b550a4fce96abd852c0cf2400d75d08c239f8 +size 146112 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2306_Contact_Shadow_SpotLight.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2306_Contact_Shadow_SpotLight.png index 54cb60c1870..0a5750d07ce 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2306_Contact_Shadow_SpotLight.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2306_Contact_Shadow_SpotLight.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:486b8ec690808ea10872b811355c1b43edb3c1baf81837a32015dba01b33fe1d -size 11923 +oid sha256:440c5130d0c7be57661f8f3372dac90085d9278bde7c9f1c12be302ede803841 +size 143862 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2311_ShadowMaps.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2311_ShadowMaps.png index be7b0661406..38fe7474bf9 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2311_ShadowMaps.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/2311_ShadowMaps.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:819994e69eb8e5c3a473ed3cdc1ddc3907a62869d1e28aa9a0f98157b718b127 -size 124457 +oid sha256:79bcbc7e44ef35983e89ecec5707f5c8673cbef5db36354e04439f0e817670b1 +size 178659 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/5003_Fog_DensityVolumesShadows.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/5003_Fog_DensityVolumesShadows.png index 6feff7c3684..1e9625dd55d 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/5003_Fog_DensityVolumesShadows.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/5003_Fog_DensityVolumesShadows.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebe62a04958dff25b0461e43fbb668d191e483ad4bda110b336cb17220c5d7ed -size 106085 +oid sha256:f8960d432b7036b851fcecb1df3688be935170e74c0c8ce5b3a55365970d53a9 +size 107575 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9004_MultiViewport.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9004_MultiViewport.png index f186ee6dfe6..572ee3697c9 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9004_MultiViewport.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9004_MultiViewport.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b756114b2650b792acd32a37ff8881f25af2ba63373d2fd193509c3190aa2cc1 -size 112642 +oid sha256:2babc86b61aa9b3f633ad57179a0bb94df06aac1177c45a490e850e5dd9f2a3e +size 168579 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9401_MSAAForwardBoth.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9401_MSAAForwardBoth.png index 7ae4abef137..9658a485cfd 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9401_MSAAForwardBoth.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9401_MSAAForwardBoth.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa28088ccdd36a14ca35d7cbc44d4ddf09709667ad7a3ee4a8548db717078929 -size 134332 +oid sha256:ada3a1bc6d40c73c9ff811430068cbd45edb3c587a9593d9977698737a263580 +size 243546 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9401_MSAAForwardOnly.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9401_MSAAForwardOnly.png index 0b114ec5510..d762807fc1e 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9401_MSAAForwardOnly.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9401_MSAAForwardOnly.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a40d948965426ff4603ce0bf20499ec73892130c373f3862a4e00d5bc1563ff -size 156342 +oid sha256:c305c887642c2551a67b6813590c36f3fa82024e04e59c59f5c4490fbdcc6463 +size 267981 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9701_CustomPass_DrawRenderers.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9701_CustomPass_DrawRenderers.png index 4a34755117f..543c3337443 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9701_CustomPass_DrawRenderers.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/9701_CustomPass_DrawRenderers.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01ab7b22ff9e62b9cf89f4de8627bcf9bca5998b5959df6db8decec9fd1d2dc9 -size 57772 +oid sha256:638db820b2e4801bfaba3a8de57627c48c4db2b31edf0cd4ec7600585fe0cf01 +size 60207