#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch #pragma kernel TileNeighbourhood NEIGHBOURHOOD_PASS #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/MotionBlurTileCommon.hlsl" #pragma multi_compile _ SCATTERING #ifdef SCATTERING // TODO: Consider a conservative line rasterization. // Line drawing algorithms. #if 0 void Bresenham(int2 center, float2 lineToDraw, uint centerInfo) { int2 maxCoords = int2(_TileTargetSize.xy - 1); // Determine start and end point of the line. Forward int2 startPoint = center; int2 endPoint = int2(ceil(startPoint.x + lineToDraw.x), ceil(startPoint.y + lineToDraw.y)); endPoint = clamp(endPoint, int2(0, 0), maxCoords); int2 delta = int2(abs(startPoint - endPoint)); int2 steps = int2(startPoint.x < endPoint.x ? 1 : -1, startPoint.y < endPoint.y ? 1 : -1); int tMax = (delta.x > delta.y ? delta.x : -delta.y) / 2; int x = startPoint.x; int y = startPoint.y; for (int i = 0; i < 32; ++i) { if (x >= endPoint.x && y >= endPoint.y) { break; } if (tMax > -delta.x) { tMax -= delta.y; x += steps.x; } if (tMax < delta.y) { tMax += delta.x; y += steps.y; } InterlockedMax(_TileToScatterMax[COORD_TEXTURE2D_X(int2(x, y))], uint(centerInfo)); } } #endif // Derived from Voxel traversal along a 3D line (https://dl.acm.org/doi/10.5555/180895.180926) void Cohen(int2 center, float2 lineToDraw, uint centerInfo) { // We push the extremes by one to be safer. Not great, but works better than not doing it. int2 startPoint = center - 1; int2 dir = ceil(lineToDraw + 1); int2 signs = int2(sign(dir.x), sign(dir.y)); int2 a = dir * signs; int2 b = 2 * a; int2 pos = startPoint; int e = a.y - a.x; int l = (a.x + a.y); for (int i = 0; i < l; i++) { if (e < 0) { pos.x += signs.x; e += b.y; } else { pos.y += signs.y; e -= b.x; } InterlockedMax(_TileToScatterMax[COORD_TEXTURE2D_X(pos)], uint(centerInfo)); } } void DDA(int2 center, float2 lineToDraw, uint centerInfo) { int2 maxCoords = int2(_TileTargetSize.xy); int2 startPoint = center; int2 endPoint = int2(ceil(startPoint.x + lineToDraw.x), ceil(startPoint.y + lineToDraw.y)); endPoint = clamp(endPoint, int2(0, 0), maxCoords); bool permute = false; if (abs(lineToDraw.x) < abs(lineToDraw.y)) { permute = true; lineToDraw = lineToDraw.yx; startPoint = startPoint.yx; endPoint = endPoint.yx; } float dirSign = sign(lineToDraw.x); float invDeltaX = dirSign / lineToDraw.x; float2 step = float2(dirSign, lineToDraw.y * invDeltaX); float end = endPoint.x * dirSign; float2 currPoint = startPoint; const int maxIter = 64; for (int i = 0; ((currPoint.x * dirSign) <= end) && (i < maxIter); ++i) { currPoint += step; float2 hitPixel = permute ? currPoint.yx : currPoint; hitPixel = clamp(hitPixel, 0, _TileTargetSize.xy); InterlockedMax(_TileToScatterMax[COORD_TEXTURE2D_X(ceil(hitPixel))], uint(centerInfo)); } } #define USE_NEIGHBOURHOOD_MIN 0 // Note: the neighbourhood building for min is done during merge pass as we need to have easy access to maxVal [numthreads(8, 8, 1)] void TileNeighbourhood(uint3 dispatchID : SV_DispatchThreadID, uint gid : SV_GroupIndex, uint2 groupThreadId : SV_GroupThreadID, uint3 groupID : SV_GroupID) { #ifndef SHADER_API_METAL UNITY_XR_ASSIGN_VIEW_INDEX(dispatchID.z); int2 id = dispatchID.xy; int2 maxCoords = int2(_TileTargetSize.xy - 1); if (any(id > maxCoords)) return; // Sample the motionVec at this tile. uint packedTileInfo = _TileToScatterMax[COORD_TEXTURE2D_X(id)]; float minMotionVec = _TileToScatterMin[COORD_TEXTURE2D_X(id)]; float2 MotionVecData = UnpackMotionVec(packedTileInfo); // Covert to a per pixel motionVec. float2 maxMotionVectorInTiles = DecodeMotionVectorFromPacked(MotionVecData.xy) * _TileTargetSize.xy * 0.5f; // If the central motionVec is small, no need to spread it. if (MotionVecLengthInPixelsFromEncoded(MotionVecData) > 0.5f) { // Spread Forward Cohen(id, maxMotionVectorInTiles, packedTileInfo); // Spread Backward Cohen(id, -maxMotionVectorInTiles, packedTileInfo); } // TODO: We need to find a better min motionVec determination. #if USE_NEIGHBOURHOOD_MIN // Find min of the tile in the 1-ring neighbourhood? This is incorrect, but might be worth perf wise. float v0 = _TileToScatterMin[COORD_TEXTURE2D_X(clamp(id.xy + int2(-1, 1), int2(0, 0), maxCoords))]; float v1 = _TileToScatterMin[COORD_TEXTURE2D_X(clamp(id.xy + int2(0, 1), int2(0, 0), maxCoords))]; float v2 = _TileToScatterMin[COORD_TEXTURE2D_X(clamp(id.xy + int2(1, 1), int2(0, 0), maxCoords))]; float v3 = _TileToScatterMin[COORD_TEXTURE2D_X(clamp(id.xy + int2(-1, 0), int2(0, 0), maxCoords))]; float v4 = minMotionVec; float v5 = _TileToScatterMin[COORD_TEXTURE2D_X(clamp(id.xy + int2(1, 0), int2(0, 0), maxCoords))]; float v6 = _TileToScatterMin[COORD_TEXTURE2D_X(clamp(id.xy + int2(-1, -1), int2(0, 0), maxCoords))]; float v7 = _TileToScatterMin[COORD_TEXTURE2D_X(clamp(id.xy + int2(0, -1), int2(0, 0), maxCoords))]; float v8 = _TileToScatterMin[COORD_TEXTURE2D_X(clamp(id.xy + int2(1, -1), int2(0, 0), maxCoords))]; float minMotionVec0 = Min3(v0, v1, v2); float minMotionVec1 = Min3(v3, v4, v5); float minMotionVec2 = Min3(v6, v7, v8); minMotionVec = Min3(minMotionVec0, minMotionVec1, minMotionVec2); #endif _TileToScatterMin[COORD_TEXTURE2D_X(id)] = minMotionVec; #endif } #else // SCATTERING RW_TEXTURE2D_X(float3, _TileMaxNeighbourhood); [numthreads(8, 8, 1)] void TileNeighbourhood(uint3 dispatchID : SV_DispatchThreadID, uint gid : SV_GroupIndex, uint2 groupThreadId : SV_GroupThreadID, uint3 groupID : SV_GroupID) { UNITY_XR_ASSIGN_VIEW_INDEX(dispatchID.z); int2 id = dispatchID.xy; int2 maxCoords = int2(_TileTargetSize.xy - 1); float3 centralSample = _TileMinMaxMotionVec[COORD_TEXTURE2D_X(id.xy + uint2(0, 0))].xyz; float3 v0 = _TileMinMaxMotionVec[COORD_TEXTURE2D_X(clamp(id.xy + int2(-1, 1), int2(0, 0), maxCoords))].xyz; float3 v1 = _TileMinMaxMotionVec[COORD_TEXTURE2D_X(clamp(id.xy + int2(0, 1), int2(0, 0), maxCoords))].xyz; float3 v2 = _TileMinMaxMotionVec[COORD_TEXTURE2D_X(clamp(id.xy + int2(1, 1), int2(0, 0), maxCoords))].xyz; float3 v3 = _TileMinMaxMotionVec[COORD_TEXTURE2D_X(clamp(id.xy + int2(-1, 0), int2(0, 0), maxCoords))].xyz; float3 v4 = centralSample.xyz; float3 v5 = _TileMinMaxMotionVec[COORD_TEXTURE2D_X(clamp(id.xy + int2(1, 0), int2(0, 0), maxCoords))].xyz; float3 v6 = _TileMinMaxMotionVec[COORD_TEXTURE2D_X(clamp(id.xy + int2(-1, -1), int2(0, 0), maxCoords))].xyz; float3 v7 = _TileMinMaxMotionVec[COORD_TEXTURE2D_X(clamp(id.xy + int2(0, -1), int2(0, 0), maxCoords))].xyz; float3 v8 = _TileMinMaxMotionVec[COORD_TEXTURE2D_X(clamp(id.xy + int2(1, -1), int2(0, 0), maxCoords))].xyz; float2 maxMotionVec0 = MaxMotionVec(v0.xy, MaxMotionVec(v1.xy, v2.xy)); float2 maxMotionVec1 = MaxMotionVec(v3.xy, MaxMotionVec(v4.xy, v5.xy)); float2 maxMotionVec2 = MaxMotionVec(v6.xy, MaxMotionVec(v7.xy, v8.xy)); float minMotionVec0 = Min3(v0.z, v1.z, v2.z); float minMotionVec1 = Min3(v3.z, v4.z, v5.z); float minMotionVec2 = Min3(v6.z, v7.z, v8.z); _TileMaxNeighbourhood[COORD_TEXTURE2D_X(id.xy)] = float3(MaxMotionVec(maxMotionVec0, MaxMotionVec(maxMotionVec1, maxMotionVec2)), Min3(minMotionVec0, minMotionVec1, minMotionVec2)); } #endif