diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/BatchRenderGroupRaw.png index fe9d05f06cd..f022ffa201c 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57dad4d6f707fafb054e9d65a9b93b10c4ae4cafbf87ac16db5c9b2547128ca7 -size 797758 +oid sha256:27995dd07f12b1436b5556f795bf808c1fa8656f4801ca02bbaf6af7f94eaf7c +size 726314 diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/LinuxPlayer/Vulkan/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/LinuxPlayer/Vulkan/None/BatchRenderGroupRaw.png index fc528f351d6..f022ffa201c 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/LinuxPlayer/Vulkan/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/LinuxPlayer/Vulkan/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c13dea55e22c16475aa2dcb2fcadccc4d9328ce550ecc69b3770a59d5da02840 -size 796575 +oid sha256:27995dd07f12b1436b5556f795bf808c1fa8656f4801ca02bbaf6af7f94eaf7c +size 726314 diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/BatchRenderGroupRaw.png index 4511317b2fc..0f6017007cf 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de808282285e4f6db9575c57fe7cb0c61a4ac5aab275eb92b7e9dc8493ccf83c -size 711039 +oid sha256:b13ffa4cb163ff2694e91d7c6a8b32f544f3efda2129096465d6b52c40190ba4 +size 726248 diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png index 4511317b2fc..b926a33981e 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de808282285e4f6db9575c57fe7cb0c61a4ac5aab275eb92b7e9dc8493ccf83c -size 711039 +oid sha256:cf2d351e44a41e5c32a11982a3e8299e62766466a34f7581ab901b9fa457e56f +size 726256 diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png.meta b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png.meta index 754ca005abc..299cf4a84e3 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png.meta +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a2a135f8eec8b8e43880aadc45cda8f0 +guid: cac766be0bdd5604ab967bdd136b81d6 TextureImporter: internalIDToNameTable: [] externalObjects: {} @@ -20,6 +20,7 @@ TextureImporter: externalNormalMap: 0 heightScale: 0.25 normalMapFilter: 0 + flipGreenChannel: 0 isReadable: 1 streamingMipmaps: 0 streamingMipmapsPriority: 0 @@ -63,6 +64,7 @@ TextureImporter: textureFormatSet: 0 ignorePngGamma: 0 applyGammaDecoding: 0 + swizzle: 50462976 platformSettings: - serializedVersion: 3 buildTarget: DefaultTexturePlatform @@ -92,7 +94,6 @@ TextureImporter: nameFileIdTable: {} spritePackingTag: pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 userData: assetBundleName: assetBundleVariant: diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/BatchRenderGroupRaw.png index 3b19b58e7e4..f022ffa201c 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d2fc50329128db9974360f029047ab0157388eaef536fb7ab7ac9767c8440b0 -size 797756 +oid sha256:27995dd07f12b1436b5556f795bf808c1fa8656f4801ca02bbaf6af7f94eaf7c +size 726314 diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/BatchRenderGroupRaw.png index 4511317b2fc..0f6017007cf 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de808282285e4f6db9575c57fe7cb0c61a4ac5aab275eb92b7e9dc8493ccf83c -size 711039 +oid sha256:b13ffa4cb163ff2694e91d7c6a8b32f544f3efda2129096465d6b52c40190ba4 +size 726248 diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png index 433aa9a74bd..b926a33981e 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b21e51ec9a89f94d84516946757232cce8c1a5385198feac946fbaf812966b7 -size 710444 +oid sha256:cf2d351e44a41e5c32a11982a3e8299e62766466a34f7581ab901b9fa457e56f +size 726256 diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png.meta b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png.meta index b9dfdfad7da..51c6c76553e 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png.meta +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png.meta @@ -6,7 +6,7 @@ TextureImporter: serializedVersion: 11 mipmaps: mipMapMode: 0 - enableMipMap: 0 + enableMipMap: 1 sRGBTexture: 1 linearTexture: 0 fadeOut: 0 @@ -20,7 +20,8 @@ TextureImporter: externalNormalMap: 0 heightScale: 0.25 normalMapFilter: 0 - isReadable: 1 + flipGreenChannel: 0 + isReadable: 0 streamingMipmaps: 0 streamingMipmapsPriority: 0 vTOnly: 0 @@ -39,7 +40,7 @@ TextureImporter: wrapU: 0 wrapV: 0 wrapW: 0 - nPOTScale: 0 + nPOTScale: 1 lightmap: 0 compressionQuality: 50 spriteMode: 0 @@ -63,13 +64,14 @@ TextureImporter: textureFormatSet: 0 ignorePngGamma: 0 applyGammaDecoding: 0 + swizzle: 50462976 platformSettings: - serializedVersion: 3 buildTarget: DefaultTexturePlatform maxTextureSize: 2048 resizeAlgorithm: 0 textureFormat: -1 - textureCompression: 0 + textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 @@ -92,7 +94,6 @@ TextureImporter: nameFileIdTable: {} spritePackingTag: pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 userData: assetBundleName: assetBundleVariant: diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Vulkan/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Vulkan/None/BatchRenderGroupRaw.png index 3b19b58e7e4..f022ffa201c 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Vulkan/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/ReferenceImages/Linear/WindowsPlayer/Vulkan/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d2fc50329128db9974360f029047ab0157388eaef536fb7ab7ac9767c8440b0 -size 797756 +oid sha256:27995dd07f12b1436b5556f795bf808c1fa8656f4801ca02bbaf6af7f94eaf7c +size 726314 diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BRGGameObjects/FrustumPlanes.cs b/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BRGGameObjects/FrustumPlanes.cs index 11c24cb6b31..4d3e79a7ad7 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BRGGameObjects/FrustumPlanes.cs +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BRGGameObjects/FrustumPlanes.cs @@ -1,9 +1,8 @@ -using System; using Unity.Collections; using Unity.Mathematics; using UnityEngine; -namespace Unity.Rendering +namespace Unity.Rendering.FrustumPlanes { public struct FrustumPlanes { @@ -14,77 +13,112 @@ public enum IntersectResult Partial }; - static public void FromCamera(Camera camera, NativeArray planes) + public struct PlanePacket4 { - if (planes == null) - throw new ArgumentNullException("The argument planes cannot be null."); - if (planes.Length != 6) - throw new ArgumentException("The argument planes does not have the expected length 6."); + public float4 Xs; + public float4 Ys; + public float4 Zs; + public float4 Distances; + } - Plane[] sourcePlanes = GeometryUtility.CalculateFrustumPlanes(camera); + public static NativeArray BuildSOAPlanePacketsMulti(NativeArray cullingPlanes, NativeArray splitCounts, Allocator allocator) + { + int totalPacketCount = 0; + for (int s = 0; s < splitCounts.Length; ++s) + { + totalPacketCount += (splitCounts[s] + 3) >> 2; + } - var cameraToWorld = camera.cameraToWorldMatrix; - var eyePos = cameraToWorld.MultiplyPoint(Vector3.zero); - var viewDir = new float3(cameraToWorld.m02, cameraToWorld.m12, cameraToWorld.m22); - viewDir = -math.normalizesafe(viewDir); + var planes = new NativeArray(totalPacketCount, allocator, NativeArrayOptions.UninitializedMemory); - // Near Plane - sourcePlanes[4].SetNormalAndPosition(viewDir, eyePos); - sourcePlanes[4].distance -= camera.nearClipPlane; + int dstOffset = 0; + int srcOffset = 0; + for (int s = 0; s < splitCounts.Length; ++s) + { + int cullingPlaneCount = splitCounts[s]; + int packetCount = (cullingPlaneCount + 3) >> 2; - // Far plane - sourcePlanes[5].SetNormalAndPosition(-viewDir, eyePos); - sourcePlanes[5].distance += camera.farClipPlane; + for (int i = 0; i < cullingPlaneCount; i++) + { + var p = planes[dstOffset + (i >> 2)]; + p.Xs[i & 3] = cullingPlanes[srcOffset + i].normal.x; + p.Ys[i & 3] = cullingPlanes[srcOffset + i].normal.y; + p.Zs[i & 3] = cullingPlanes[srcOffset + i].normal.z; + p.Distances[i & 3] = cullingPlanes[srcOffset + i].distance; + planes[dstOffset + (i >> 2)] = p; + } - for (int i = 0; i < 6; ++i) - { - planes[i] = new float4(sourcePlanes[i].normal.x, sourcePlanes[i].normal.y, sourcePlanes[i].normal.z, - sourcePlanes[i].distance); + // Populate the remaining planes with values that are always "in" + for (int i = cullingPlaneCount; i < 4 * packetCount; ++i) + { + var p = planes[dstOffset + (i >> 2)]; + p.Xs[i & 3] = 1.0f; + p.Ys[i & 3] = 0.0f; + p.Zs[i & 3] = 0.0f; + + // This value was before hardcoded to 32786.0f. + // It was causing the culling system to discard the rendering of entities having a X coordinate approximately less than -32786. + // We could not find anything relying on this number, so the value has been increased to 1 billion + p.Distances[i & 3] = 1e9f; + + planes[dstOffset + (i >> 2)] = p; + } + + srcOffset += cullingPlaneCount; + dstOffset += packetCount; } + return planes; } - static public IntersectResult Intersect(NativeArray cullingPlanes, AABB a) + // Returns a bitmask (one 1 per split): 1 = visible, 0 = not visible in that split. + static public uint Intersect2NoPartialMulti(NativeArray cullingPlanePackets, NativeArray splitCounts, AABB a) { - float3 m = a.Center; - float3 extent = a.Extents; + float4 mx = a.Center.xxxx; + float4 my = a.Center.yyyy; + float4 mz = a.Center.zzzz; - var inCount = 0; - for (int i = 0; i < cullingPlanes.Length; i++) + float4 ex = a.Extents.xxxx; + float4 ey = a.Extents.yyyy; + float4 ez = a.Extents.zzzz; + + uint outMask = 0; + + int offset = 0; + for (int s = 0; s < splitCounts.Length; ++s) { - float3 normal = cullingPlanes[i].xyz; - float dist = math.dot(normal, m) + cullingPlanes[i].w; - float radius = math.dot(extent, math.abs(normal)); - if (dist + radius <= 0) - return IntersectResult.Out; - - if (dist > radius) - inCount++; - } + int packetCount = (splitCounts[s] + 3) >> 2; - return (inCount == cullingPlanes.Length) ? IntersectResult.In : IntersectResult.Partial; - } + int4 masks = 0; + for (int i = 0; i < packetCount; i++) + { + var p = cullingPlanePackets[offset + i]; + float4 distances = dot4(p.Xs, p.Ys, p.Zs, mx, my, mz) + p.Distances; + float4 radii = dot4(ex, ey, ez, math.abs(p.Xs), math.abs(p.Ys), math.abs(p.Zs)); + masks += (int4)(distances + radii <= 0); + } - public struct PlanePacket4 - { - public float4 Xs; - public float4 Ys; - public float4 Zs; - public float4 Distances; + int outCount = math.csum(masks); + if (outCount == 0) outMask |= 1u << s; + + offset += packetCount; + } + + return outMask; } - public static NativeArray BuildSOAPlanePackets(NativeArray cullingPlanes, Allocator allocator) + public static NativeArray BuildSOAPlanePackets(NativeArray cullingPlanes, int offset, int count, Allocator allocator) { - int cullingPlaneCount = cullingPlanes.Length; + int cullingPlaneCount = count; int packetCount = (cullingPlaneCount + 3) >> 2; var planes = new NativeArray(packetCount, allocator, NativeArrayOptions.UninitializedMemory); for (int i = 0; i < cullingPlaneCount; i++) { var p = planes[i >> 2]; - p.Xs[i & 3] = cullingPlanes[i].normal.x; - p.Ys[i & 3] = cullingPlanes[i].normal.y; - p.Zs[i & 3] = cullingPlanes[i].normal.z; - p.Distances[i & 3] = cullingPlanes[i].distance; + p.Xs[i & 3] = cullingPlanes[i + offset].normal.x; + p.Ys[i & 3] = cullingPlanes[i + offset].normal.y; + p.Zs[i & 3] = cullingPlanes[i + offset].normal.z; + p.Distances[i & 3] = cullingPlanes[i + offset].distance; planes[i >> 2] = p; } @@ -107,38 +141,6 @@ public static NativeArray BuildSOAPlanePackets(NativeArray return planes; } - static public IntersectResult Intersect2(NativeArray cullingPlanePackets, AABB a) - { - float4 mx = a.Center.xxxx; - float4 my = a.Center.yyyy; - float4 mz = a.Center.zzzz; - - float4 ex = a.Extents.xxxx; - float4 ey = a.Extents.yyyy; - float4 ez = a.Extents.zzzz; - - int4 outCounts = 0; - int4 inCounts = 0; - - for (int i = 0; i < cullingPlanePackets.Length; i++) - { - var p = cullingPlanePackets[i]; - float4 distances = dot4(p.Xs, p.Ys, p.Zs, mx, my, mz) + p.Distances; - float4 radii = dot4(ex, ey, ez, math.abs(p.Xs), math.abs(p.Ys), math.abs(p.Zs)); - - outCounts += (int4)(distances + radii < 0); - inCounts += (int4)(distances >= radii); - } - - int inCount = math.csum(inCounts); - int outCount = math.csum(outCounts); - - if (outCount != 0) - return IntersectResult.Out; - else - return (inCount == 4 * cullingPlanePackets.Length) ? IntersectResult.In : IntersectResult.Partial; - } - static public IntersectResult Intersect2NoPartial(NativeArray cullingPlanePackets, AABB a) { float4 mx = a.Center.xxxx; @@ -168,26 +170,5 @@ private static float4 dot4(float4 xs, float4 ys, float4 zs, float4 mx, float4 my { return xs * mx + ys * my + zs * mz; } - - static public IntersectResult Intersect(NativeArray planes, float3 center, float radius) - { - var inCount = 0; - - for (int i = 0; i < planes.Length; i++) - { - var d = math.dot(planes[i].xyz, center) + planes[i].w; - if (d < -radius) - { - return IntersectResult.Out; - } - - if (d > radius) - { - inCount++; - } - } - - return (inCount == planes.Length) ? IntersectResult.In : IntersectResult.Partial; - } } } diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BRGGameObjects/RenderBRG.cs b/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BRGGameObjects/RenderBRG.cs index f38ad0a9d61..fffe96af462 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BRGGameObjects/RenderBRG.cs +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BRGGameObjects/RenderBRG.cs @@ -7,7 +7,7 @@ using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering; -using FrustumPlanes = Unity.Rendering.FrustumPlanes; +using FrustumPlanes = Unity.Rendering.FrustumPlanes.FrustumPlanes; public struct RangeKey : IEquatable { @@ -145,6 +145,10 @@ private struct CullingJob : IJobParallelFor [ReadOnly] public NativeArray planes; + [DeallocateOnJobCompletion] + [ReadOnly] + public NativeArray splitCounts; + [ReadOnly] public NativeArray renderers; @@ -153,15 +157,15 @@ private struct CullingJob : IJobParallelFor public void Execute(int index) { - // Each invocation is culling 64 renderers (64 bit bitfield) - int start = index * 64; - int end = math.min(start + 64, renderers.Length); + // Each invocation is culling 8 renderers (8 split bits * 8 renderers = 64 bit bitfield) + int start = index * 8; + int end = math.min(start + 8, renderers.Length); ulong visibleBits = 0; for (int i = start; i < end; i++) { - ulong bit = FrustumPlanes.Intersect2NoPartial(planes, renderers[i].bounds) == FrustumPlanes.IntersectResult.In ? 1ul : 0ul; - visibleBits |= bit << (i - start); + ulong splitMask = FrustumPlanes.Intersect2NoPartialMulti(planes, splitCounts, renderers[i].bounds); + visibleBits |= splitMask << (8 * (i - start)); } rendererVisibility[index] = visibleBits; @@ -207,20 +211,48 @@ public void Execute() int internalDraw = 0; int rangeStartIndex = 0; + uint visibleMaskPrev = 0; + for (int i = 0; i < instanceIndices.Length; i++) { + var remappedIndex = drawIndices[activeBatch]; // DrawIndices remap to get DrawCommands ordered by DrawRange var rendererIndex = instanceIndices[i]; - bool visible = (rendererVisibility[rendererIndex / 64] & (1ul << (rendererIndex % 64))) != 0; + uint visibleMask = (uint)((rendererVisibility[rendererIndex / 8] >> ((rendererIndex % 8) * 8)) & 0xfful); - if (visible) + if (visibleMask != 0) { + // Emit extra DrawCommand if visible mask changes + // TODO: Sort draws by visibilityMask in batch first to minimize the number of DrawCommands + if (visibleMask != visibleMaskPrev) + { + var visibleCount = outIndex - batchStartIndex; + if (visibleCount > 0) + { + draws.drawCommands[outBatch] = new BatchDrawCommand + { + visibleOffset = (uint)batchStartIndex, + visibleCount = (uint)visibleCount, + batchID = batchID, + materialID = drawBatches[remappedIndex].key.material, + meshID = drawBatches[remappedIndex].key.meshID, + submeshIndex = (ushort)drawBatches[remappedIndex].key.submeshIndex, + splitVisibilityMask = (ushort)visibleMaskPrev, + flags = BatchDrawCommandFlags.None, + sortingPosition = 0 + }; + outBatch++; + } + batchStartIndex = outIndex; + } + visibleMaskPrev = visibleMask; + + // Insert the visible instance to the array draws.visibleInstances[outIndex] = instanceIndices[i]; outIndex++; } internalIndex++; // Next draw batch? - var remappedIndex = drawIndices[activeBatch]; // DrawIndices remap to get DrawCommands ordered by DrawRange if (internalIndex == drawBatches[remappedIndex].instanceCount) { var visibleCount = outIndex - batchStartIndex; @@ -233,7 +265,8 @@ public void Execute() batchID = batchID, materialID = drawBatches[remappedIndex].key.material, meshID = drawBatches[remappedIndex].key.meshID, - submeshIndex = drawBatches[remappedIndex].key.submeshIndex, + submeshIndex = (ushort)drawBatches[remappedIndex].key.submeshIndex, + splitVisibilityMask = (ushort)visibleMaskPrev, flags = BatchDrawCommandFlags.None, sortingPosition = 0 }; @@ -246,6 +279,7 @@ public void Execute() outBatch++; } + visibleMaskPrev = 0; batchStartIndex = outIndex; internalIndex = 0; activeBatch++; @@ -296,11 +330,20 @@ public JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCulling return new JobHandle(); } - var planes = FrustumPlanes.BuildSOAPlanePackets(cullingContext.cullingPlanes, Allocator.TempJob); + var splitCounts = new NativeArray(cullingContext.cullingSplits.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + for (int i = 0; i < splitCounts.Length; ++i) + { + var split = cullingContext.cullingSplits[i]; + splitCounts[i] = split.cullingPlaneCount; + } + + var planes = FrustumPlanes.BuildSOAPlanePacketsMulti(cullingContext.cullingPlanes, splitCounts, Allocator.TempJob); BatchCullingOutputDrawCommands drawCommands = new BatchCullingOutputDrawCommands(); drawCommands.drawRanges = Malloc(m_drawRanges.Length); - drawCommands.drawCommands = Malloc(m_drawBatches.Length); + drawCommands.drawCommands = Malloc(m_drawBatches.Length * + splitCounts.Length * 10); // TODO: Multiplying the DrawCommand count by splitCount*10 is NOT an conservative upper bound. But in practice is enough. Sorting would give us a real conservative bound... + drawCommands.drawCommandPickingInstanceIDs = Malloc(m_drawBatches.Length); drawCommands.visibleInstances = Malloc(m_instanceIndices.Length); @@ -314,12 +357,13 @@ public JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCulling cullingOutput.drawCommands[0] = drawCommands; - var visibilityLength = (m_renderers.Length + 63) / 64; + var visibilityLength = (m_renderers.Length + 7) / 8; var rendererVisibility = new NativeArray(visibilityLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); var cullingJob = new CullingJob { planes = planes, + splitCounts = splitCounts, renderers = m_renderers, rendererVisibility = rendererVisibility }; @@ -335,7 +379,7 @@ public JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCulling drawCommands = cullingOutput.drawCommands }; - var jobHandleCulling = cullingJob.Schedule(visibilityLength, 2); + var jobHandleCulling = cullingJob.Schedule(visibilityLength, 8); var jobHandleOutput = drawOutputJob.Schedule(jobHandleCulling); return jobHandleOutput; diff --git a/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BatchRenderGroupRaw/BRGSetup.cs b/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BatchRenderGroupRaw/BRGSetup.cs index d2e552d078a..03638c13d72 100644 --- a/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BatchRenderGroupRaw/BRGSetup.cs +++ b/TestProjects/BatchRendererGroup_HDRP/Assets/SampleScenes/BatchRenderGroupRaw/BRGSetup.cs @@ -110,6 +110,7 @@ public JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCulling materialID = m_materialID, meshID = m_meshID, submeshIndex = 0, + splitVisibilityMask = 0xff, flags = m_motionVectorTest ? BatchDrawCommandFlags.HasMotion : BatchDrawCommandFlags.None, sortingPosition = 0 }; @@ -142,30 +143,26 @@ void Start() // Batch metadata buffer int objectToWorldID = Shader.PropertyToID("unity_ObjectToWorld"); int matrixPreviousMID = Shader.PropertyToID("unity_MatrixPreviousM"); - - // int worldToObjectID = Shader.PropertyToID("unity_WorldToObject"); + int worldToObjectID = Shader.PropertyToID("unity_WorldToObject"); int colorID = Shader.PropertyToID("_BaseColor"); // Generate a grid of objects... - int bigDataBufferVector4Count = itemCount * 3 * 2 + itemCount; // mat4x3, colors + int bigDataBufferVector4Count = 4 + itemCount * (3 * 3 + 1); // 4xfloat4 zero + per instance = { 3x mat4x3, 1x float4 color } m_sysmemBuffer = new NativeArray(bigDataBufferVector4Count, Allocator.Persistent, NativeArrayOptions.ClearMemory); m_GPUPersistentInstanceData = new GraphicsBuffer(GraphicsBuffer.Target.Raw, (int)bigDataBufferVector4Count * 16 / 4, 4); - // Matrices - /* - * mat4x3 packed like this: - * - float4x4( - p1.x, p1.w, p2.z, p3.y, - p1.y, p2.x, p2.w, p3.z, - p1.z, p2.y, p3.x, p3.w, - 0.0, 0.0, 0.0, 1.0 - ); - */ + // 64 bytes of zeroes, so loads from address 0 return zeroes. This is a BatchRendererGroup convention. + int positionOffset = 4; + m_sysmemBuffer[0] = new Vector4(0, 0, 0, 0); + m_sysmemBuffer[1] = new Vector4(0, 0, 0, 0); + m_sysmemBuffer[2] = new Vector4(0, 0, 0, 0); + m_sysmemBuffer[3] = new Vector4(0, 0, 0, 0); + // Matrices UpdatePositions(m_center); + // Colors - int colorOffset = itemCount * 3 * 2; + int colorOffset = positionOffset + itemCount * 3 * 3; for (int i = 0; i < itemCount; i++) { Color col = Color.HSVToRGB(((float)(i) / (float)itemCount) % 1.0f, 1.0f, 1.0f); @@ -175,21 +172,22 @@ void Start() } m_GPUPersistentInstanceData.SetData(m_sysmemBuffer); - var batchMetadata = new NativeArray(3, Allocator.Temp, NativeArrayOptions.UninitializedMemory); - batchMetadata[0] = CreateMetadataValue(objectToWorldID, 0, true); // matrices - batchMetadata[1] = CreateMetadataValue(matrixPreviousMID, itemCount * UnsafeUtility.SizeOf() * 3, true); // previous matrices - batchMetadata[2] = CreateMetadataValue(colorID, itemCount * UnsafeUtility.SizeOf() * 3 * 2, true); // colors + var batchMetadata = new NativeArray(4, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + batchMetadata[0] = CreateMetadataValue(objectToWorldID, 64, true); // matrices + batchMetadata[1] = CreateMetadataValue(matrixPreviousMID, 64 + itemCount * UnsafeUtility.SizeOf() * 3, true); // previous matrices + batchMetadata[2] = CreateMetadataValue(worldToObjectID, 64 + itemCount * UnsafeUtility.SizeOf() * 3 * 2, true); // inverse matrices + batchMetadata[3] = CreateMetadataValue(colorID, 64 + itemCount * UnsafeUtility.SizeOf() * 3 * 3, true); // colors // Register batch m_batchID = m_BatchRendererGroup.AddBatch(batchMetadata, m_GPUPersistentInstanceData.bufferHandle); - m_initialized = true; } void UpdatePositions(Vector3 pos) { - int itemCountOffset = itemGridSize * itemGridSize * 3; // 3 float4 per matrix + int positionOffset = 4; + int itemCountOffset = itemGridSize * itemGridSize * 3; // 3xfloat4 per matrix for (int z = 0; z < itemGridSize; z++) { for (int x = 0; x < itemGridSize; x++) @@ -198,16 +196,31 @@ void UpdatePositions(Vector3 pos) float pz = (z - itemGridSize / 2) * m_spacingFactor; int i = z * itemGridSize + x; + /* + * mat4x3 packed like this: + * + float4x4( + p1.x, p1.w, p2.z, p3.y, + p1.y, p2.x, p2.w, p3.z, + p1.z, p2.y, p3.x, p3.w, + 0.0, 0.0, 0.0, 1.0 + ); + */ + // update previous matrix with previous frame current matrix - m_sysmemBuffer[i * 3 + 0 + itemCountOffset] = m_sysmemBuffer[i * 3 + 0]; - m_sysmemBuffer[i * 3 + 1 + itemCountOffset] = m_sysmemBuffer[i * 3 + 1]; - m_sysmemBuffer[i * 3 + 2 + itemCountOffset] = m_sysmemBuffer[i * 3 + 2]; + m_sysmemBuffer[positionOffset + i * 3 + 0 + itemCountOffset] = m_sysmemBuffer[positionOffset + i * 3 + 0]; + m_sysmemBuffer[positionOffset + i * 3 + 1 + itemCountOffset] = m_sysmemBuffer[positionOffset + i * 3 + 1]; + m_sysmemBuffer[positionOffset + i * 3 + 2 + itemCountOffset] = m_sysmemBuffer[positionOffset + i * 3 + 2]; // compute the new current frame matrix - m_sysmemBuffer[i * 3 + 0] = new Vector4(1, 0, 0, 0); // hacky float3x4 layout - m_sysmemBuffer[i * 3 + 1] = new Vector4(1, 0, 0, 0); - m_sysmemBuffer[i * 3 + 2] = new Vector4(1, px + pos.x, pos.y, pz + pos.z); - + m_sysmemBuffer[positionOffset + i * 3 + 0] = new Vector4(1, 0, 0, 0); + m_sysmemBuffer[positionOffset + i * 3 + 1] = new Vector4(1, 0, 0, 0); + m_sysmemBuffer[positionOffset + i * 3 + 2] = new Vector4(1, px + pos.x, pos.y, pz + pos.z); + + // compute the new inverse matrix + m_sysmemBuffer[positionOffset + i * 3 + 0 + itemCountOffset * 2] = new Vector4(1, 0, 0, 0); + m_sysmemBuffer[positionOffset + i * 3 + 1 + itemCountOffset * 2] = new Vector4(1, 0, 0, 0); + m_sysmemBuffer[positionOffset + i * 3 + 2 + itemCountOffset * 2] = new Vector4(1, -(px + pos.x), -pos.y, -(pz + pos.z)); } } diff --git a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/BatchRenderGroupRaw.png index eb48f16ff27..d2b933a28b2 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:477684bf25c23f09ac77ed1bf1467f08824e6acdf494cbb0cdab423547c2920d -size 434303 +oid sha256:15d8c72ec57f262b46a3542f1b93e7ff300cfd86f0b691850710ed6610694497 +size 417353 diff --git a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/LinuxPlayer/Vulkan/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/LinuxPlayer/Vulkan/None/BatchRenderGroupRaw.png index eb48f16ff27..d2b933a28b2 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/LinuxPlayer/Vulkan/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/LinuxPlayer/Vulkan/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:477684bf25c23f09ac77ed1bf1467f08824e6acdf494cbb0cdab423547c2920d -size 434303 +oid sha256:15d8c72ec57f262b46a3542f1b93e7ff300cfd86f0b691850710ed6610694497 +size 417353 diff --git a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/BatchRenderGroupRaw.png index eb48f16ff27..d2b933a28b2 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:477684bf25c23f09ac77ed1bf1467f08824e6acdf494cbb0cdab423547c2920d -size 434303 +oid sha256:15d8c72ec57f262b46a3542f1b93e7ff300cfd86f0b691850710ed6610694497 +size 417353 diff --git a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/OSXPlayer/Metal/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/OSXPlayer/Metal/None/BatchRenderGroupRaw.png index eb48f16ff27..d2b933a28b2 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/OSXPlayer/Metal/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/OSXPlayer/Metal/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:477684bf25c23f09ac77ed1bf1467f08824e6acdf494cbb0cdab423547c2920d -size 434303 +oid sha256:15d8c72ec57f262b46a3542f1b93e7ff300cfd86f0b691850710ed6610694497 +size 417353 diff --git a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/BatchRenderGroupRaw.png index eb48f16ff27..d2b933a28b2 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:477684bf25c23f09ac77ed1bf1467f08824e6acdf494cbb0cdab423547c2920d -size 434303 +oid sha256:15d8c72ec57f262b46a3542f1b93e7ff300cfd86f0b691850710ed6610694497 +size 417353 diff --git a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png index eb48f16ff27..d2b933a28b2 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:477684bf25c23f09ac77ed1bf1467f08824e6acdf494cbb0cdab423547c2920d -size 434303 +oid sha256:15d8c72ec57f262b46a3542f1b93e7ff300cfd86f0b691850710ed6610694497 +size 417353 diff --git a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/BatchRenderGroupRaw.png index eb48f16ff27..d2b933a28b2 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:477684bf25c23f09ac77ed1bf1467f08824e6acdf494cbb0cdab423547c2920d -size 434303 +oid sha256:15d8c72ec57f262b46a3542f1b93e7ff300cfd86f0b691850710ed6610694497 +size 417353 diff --git a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/BatchRenderGroupRaw.png index eb48f16ff27..d2b933a28b2 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D11/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:477684bf25c23f09ac77ed1bf1467f08824e6acdf494cbb0cdab423547c2920d -size 434303 +oid sha256:15d8c72ec57f262b46a3542f1b93e7ff300cfd86f0b691850710ed6610694497 +size 417353 diff --git a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png index eb48f16ff27..d2b933a28b2 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Direct3D12/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:477684bf25c23f09ac77ed1bf1467f08824e6acdf494cbb0cdab423547c2920d -size 434303 +oid sha256:15d8c72ec57f262b46a3542f1b93e7ff300cfd86f0b691850710ed6610694497 +size 417353 diff --git a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Vulkan/None/BatchRenderGroupRaw.png b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Vulkan/None/BatchRenderGroupRaw.png index eb48f16ff27..d2b933a28b2 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Vulkan/None/BatchRenderGroupRaw.png +++ b/TestProjects/BatchRendererGroup_URP/Assets/ReferenceImages/Linear/WindowsPlayer/Vulkan/None/BatchRenderGroupRaw.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:477684bf25c23f09ac77ed1bf1467f08824e6acdf494cbb0cdab423547c2920d -size 434303 +oid sha256:15d8c72ec57f262b46a3542f1b93e7ff300cfd86f0b691850710ed6610694497 +size 417353 diff --git a/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BRGGameObjects/FrustumPlanes.cs b/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BRGGameObjects/FrustumPlanes.cs index 11c24cb6b31..4d3e79a7ad7 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BRGGameObjects/FrustumPlanes.cs +++ b/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BRGGameObjects/FrustumPlanes.cs @@ -1,9 +1,8 @@ -using System; using Unity.Collections; using Unity.Mathematics; using UnityEngine; -namespace Unity.Rendering +namespace Unity.Rendering.FrustumPlanes { public struct FrustumPlanes { @@ -14,77 +13,112 @@ public enum IntersectResult Partial }; - static public void FromCamera(Camera camera, NativeArray planes) + public struct PlanePacket4 { - if (planes == null) - throw new ArgumentNullException("The argument planes cannot be null."); - if (planes.Length != 6) - throw new ArgumentException("The argument planes does not have the expected length 6."); + public float4 Xs; + public float4 Ys; + public float4 Zs; + public float4 Distances; + } - Plane[] sourcePlanes = GeometryUtility.CalculateFrustumPlanes(camera); + public static NativeArray BuildSOAPlanePacketsMulti(NativeArray cullingPlanes, NativeArray splitCounts, Allocator allocator) + { + int totalPacketCount = 0; + for (int s = 0; s < splitCounts.Length; ++s) + { + totalPacketCount += (splitCounts[s] + 3) >> 2; + } - var cameraToWorld = camera.cameraToWorldMatrix; - var eyePos = cameraToWorld.MultiplyPoint(Vector3.zero); - var viewDir = new float3(cameraToWorld.m02, cameraToWorld.m12, cameraToWorld.m22); - viewDir = -math.normalizesafe(viewDir); + var planes = new NativeArray(totalPacketCount, allocator, NativeArrayOptions.UninitializedMemory); - // Near Plane - sourcePlanes[4].SetNormalAndPosition(viewDir, eyePos); - sourcePlanes[4].distance -= camera.nearClipPlane; + int dstOffset = 0; + int srcOffset = 0; + for (int s = 0; s < splitCounts.Length; ++s) + { + int cullingPlaneCount = splitCounts[s]; + int packetCount = (cullingPlaneCount + 3) >> 2; - // Far plane - sourcePlanes[5].SetNormalAndPosition(-viewDir, eyePos); - sourcePlanes[5].distance += camera.farClipPlane; + for (int i = 0; i < cullingPlaneCount; i++) + { + var p = planes[dstOffset + (i >> 2)]; + p.Xs[i & 3] = cullingPlanes[srcOffset + i].normal.x; + p.Ys[i & 3] = cullingPlanes[srcOffset + i].normal.y; + p.Zs[i & 3] = cullingPlanes[srcOffset + i].normal.z; + p.Distances[i & 3] = cullingPlanes[srcOffset + i].distance; + planes[dstOffset + (i >> 2)] = p; + } - for (int i = 0; i < 6; ++i) - { - planes[i] = new float4(sourcePlanes[i].normal.x, sourcePlanes[i].normal.y, sourcePlanes[i].normal.z, - sourcePlanes[i].distance); + // Populate the remaining planes with values that are always "in" + for (int i = cullingPlaneCount; i < 4 * packetCount; ++i) + { + var p = planes[dstOffset + (i >> 2)]; + p.Xs[i & 3] = 1.0f; + p.Ys[i & 3] = 0.0f; + p.Zs[i & 3] = 0.0f; + + // This value was before hardcoded to 32786.0f. + // It was causing the culling system to discard the rendering of entities having a X coordinate approximately less than -32786. + // We could not find anything relying on this number, so the value has been increased to 1 billion + p.Distances[i & 3] = 1e9f; + + planes[dstOffset + (i >> 2)] = p; + } + + srcOffset += cullingPlaneCount; + dstOffset += packetCount; } + return planes; } - static public IntersectResult Intersect(NativeArray cullingPlanes, AABB a) + // Returns a bitmask (one 1 per split): 1 = visible, 0 = not visible in that split. + static public uint Intersect2NoPartialMulti(NativeArray cullingPlanePackets, NativeArray splitCounts, AABB a) { - float3 m = a.Center; - float3 extent = a.Extents; + float4 mx = a.Center.xxxx; + float4 my = a.Center.yyyy; + float4 mz = a.Center.zzzz; - var inCount = 0; - for (int i = 0; i < cullingPlanes.Length; i++) + float4 ex = a.Extents.xxxx; + float4 ey = a.Extents.yyyy; + float4 ez = a.Extents.zzzz; + + uint outMask = 0; + + int offset = 0; + for (int s = 0; s < splitCounts.Length; ++s) { - float3 normal = cullingPlanes[i].xyz; - float dist = math.dot(normal, m) + cullingPlanes[i].w; - float radius = math.dot(extent, math.abs(normal)); - if (dist + radius <= 0) - return IntersectResult.Out; - - if (dist > radius) - inCount++; - } + int packetCount = (splitCounts[s] + 3) >> 2; - return (inCount == cullingPlanes.Length) ? IntersectResult.In : IntersectResult.Partial; - } + int4 masks = 0; + for (int i = 0; i < packetCount; i++) + { + var p = cullingPlanePackets[offset + i]; + float4 distances = dot4(p.Xs, p.Ys, p.Zs, mx, my, mz) + p.Distances; + float4 radii = dot4(ex, ey, ez, math.abs(p.Xs), math.abs(p.Ys), math.abs(p.Zs)); + masks += (int4)(distances + radii <= 0); + } - public struct PlanePacket4 - { - public float4 Xs; - public float4 Ys; - public float4 Zs; - public float4 Distances; + int outCount = math.csum(masks); + if (outCount == 0) outMask |= 1u << s; + + offset += packetCount; + } + + return outMask; } - public static NativeArray BuildSOAPlanePackets(NativeArray cullingPlanes, Allocator allocator) + public static NativeArray BuildSOAPlanePackets(NativeArray cullingPlanes, int offset, int count, Allocator allocator) { - int cullingPlaneCount = cullingPlanes.Length; + int cullingPlaneCount = count; int packetCount = (cullingPlaneCount + 3) >> 2; var planes = new NativeArray(packetCount, allocator, NativeArrayOptions.UninitializedMemory); for (int i = 0; i < cullingPlaneCount; i++) { var p = planes[i >> 2]; - p.Xs[i & 3] = cullingPlanes[i].normal.x; - p.Ys[i & 3] = cullingPlanes[i].normal.y; - p.Zs[i & 3] = cullingPlanes[i].normal.z; - p.Distances[i & 3] = cullingPlanes[i].distance; + p.Xs[i & 3] = cullingPlanes[i + offset].normal.x; + p.Ys[i & 3] = cullingPlanes[i + offset].normal.y; + p.Zs[i & 3] = cullingPlanes[i + offset].normal.z; + p.Distances[i & 3] = cullingPlanes[i + offset].distance; planes[i >> 2] = p; } @@ -107,38 +141,6 @@ public static NativeArray BuildSOAPlanePackets(NativeArray return planes; } - static public IntersectResult Intersect2(NativeArray cullingPlanePackets, AABB a) - { - float4 mx = a.Center.xxxx; - float4 my = a.Center.yyyy; - float4 mz = a.Center.zzzz; - - float4 ex = a.Extents.xxxx; - float4 ey = a.Extents.yyyy; - float4 ez = a.Extents.zzzz; - - int4 outCounts = 0; - int4 inCounts = 0; - - for (int i = 0; i < cullingPlanePackets.Length; i++) - { - var p = cullingPlanePackets[i]; - float4 distances = dot4(p.Xs, p.Ys, p.Zs, mx, my, mz) + p.Distances; - float4 radii = dot4(ex, ey, ez, math.abs(p.Xs), math.abs(p.Ys), math.abs(p.Zs)); - - outCounts += (int4)(distances + radii < 0); - inCounts += (int4)(distances >= radii); - } - - int inCount = math.csum(inCounts); - int outCount = math.csum(outCounts); - - if (outCount != 0) - return IntersectResult.Out; - else - return (inCount == 4 * cullingPlanePackets.Length) ? IntersectResult.In : IntersectResult.Partial; - } - static public IntersectResult Intersect2NoPartial(NativeArray cullingPlanePackets, AABB a) { float4 mx = a.Center.xxxx; @@ -168,26 +170,5 @@ private static float4 dot4(float4 xs, float4 ys, float4 zs, float4 mx, float4 my { return xs * mx + ys * my + zs * mz; } - - static public IntersectResult Intersect(NativeArray planes, float3 center, float radius) - { - var inCount = 0; - - for (int i = 0; i < planes.Length; i++) - { - var d = math.dot(planes[i].xyz, center) + planes[i].w; - if (d < -radius) - { - return IntersectResult.Out; - } - - if (d > radius) - { - inCount++; - } - } - - return (inCount == planes.Length) ? IntersectResult.In : IntersectResult.Partial; - } } } diff --git a/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BRGGameObjects/RenderBRG.cs b/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BRGGameObjects/RenderBRG.cs index 07ce0162127..0debf61bab5 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BRGGameObjects/RenderBRG.cs +++ b/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BRGGameObjects/RenderBRG.cs @@ -7,7 +7,7 @@ using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering; -using FrustumPlanes = Unity.Rendering.FrustumPlanes; +using FrustumPlanes = Unity.Rendering.FrustumPlanes.FrustumPlanes; public struct RangeKey : IEquatable { @@ -146,6 +146,10 @@ private struct CullingJob : IJobParallelFor [ReadOnly] public NativeArray planes; + [DeallocateOnJobCompletion] + [ReadOnly] + public NativeArray splitCounts; + [ReadOnly] public NativeArray renderers; @@ -154,15 +158,15 @@ private struct CullingJob : IJobParallelFor public void Execute(int index) { - // Each invocation is culling 64 renderers (64 bit bitfield) - int start = index * 64; - int end = math.min(start + 64, renderers.Length); + // Each invocation is culling 8 renderers (8 split bits * 8 renderers = 64 bit bitfield) + int start = index * 8; + int end = math.min(start + 8, renderers.Length); ulong visibleBits = 0; for (int i = start; i < end; i++) { - ulong bit = FrustumPlanes.Intersect2NoPartial(planes, renderers[i].bounds) == FrustumPlanes.IntersectResult.In ? 1ul : 0ul; - visibleBits |= bit << (i - start); + ulong splitMask = FrustumPlanes.Intersect2NoPartialMulti(planes, splitCounts, renderers[i].bounds); + visibleBits |= splitMask << (8 * (i - start)); } rendererVisibility[index] = visibleBits; @@ -208,20 +212,48 @@ public void Execute() int internalDraw = 0; int rangeStartIndex = 0; + uint visibleMaskPrev = 0; + for (int i = 0; i < instanceIndices.Length; i++) { + var remappedIndex = drawIndices[activeBatch]; // DrawIndices remap to get DrawCommands ordered by DrawRange var rendererIndex = instanceIndices[i]; - bool visible = (rendererVisibility[rendererIndex / 64] & (1ul << (rendererIndex % 64))) != 0; + uint visibleMask = (uint)((rendererVisibility[rendererIndex / 8] >> ((rendererIndex % 8) * 8)) & 0xfful); - if (visible) + if (visibleMask != 0) { + // Emit extra DrawCommand if visible mask changes + // TODO: Sort draws by visibilityMask in batch first to minimize the number of DrawCommands + if (visibleMask != visibleMaskPrev) + { + var visibleCount = outIndex - batchStartIndex; + if (visibleCount > 0) + { + draws.drawCommands[outBatch] = new BatchDrawCommand + { + visibleOffset = (uint)batchStartIndex, + visibleCount = (uint)visibleCount, + batchID = batchID, + materialID = drawBatches[remappedIndex].key.material, + meshID = drawBatches[remappedIndex].key.meshID, + submeshIndex = (ushort)drawBatches[remappedIndex].key.submeshIndex, + splitVisibilityMask = (ushort)visibleMaskPrev, + flags = BatchDrawCommandFlags.None, + sortingPosition = 0 + }; + outBatch++; + } + batchStartIndex = outIndex; + } + visibleMaskPrev = visibleMask; + + // Insert the visible instance to the array draws.visibleInstances[outIndex] = instanceIndices[i]; outIndex++; } internalIndex++; // Next draw batch? - var remappedIndex = drawIndices[activeBatch]; // DrawIndices remap to get DrawCommands ordered by DrawRange if (internalIndex == drawBatches[remappedIndex].instanceCount) { var visibleCount = outIndex - batchStartIndex; @@ -234,7 +266,8 @@ public void Execute() batchID = batchID, materialID = drawBatches[remappedIndex].key.material, meshID = drawBatches[remappedIndex].key.meshID, - submeshIndex = drawBatches[remappedIndex].key.submeshIndex, + submeshIndex = (ushort)drawBatches[remappedIndex].key.submeshIndex, + splitVisibilityMask = (ushort)visibleMaskPrev, flags = BatchDrawCommandFlags.None, sortingPosition = 0 }; @@ -247,6 +280,7 @@ public void Execute() outBatch++; } + visibleMaskPrev = 0; batchStartIndex = outIndex; internalIndex = 0; activeBatch++; @@ -297,11 +331,20 @@ public JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCulling return new JobHandle(); } - var planes = FrustumPlanes.BuildSOAPlanePackets(cullingContext.cullingPlanes, Allocator.TempJob); + var splitCounts = new NativeArray(cullingContext.cullingSplits.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + for (int i = 0; i < splitCounts.Length; ++i) + { + var split = cullingContext.cullingSplits[i]; + splitCounts[i] = split.cullingPlaneCount; + } + + var planes = FrustumPlanes.BuildSOAPlanePacketsMulti(cullingContext.cullingPlanes, splitCounts, Allocator.TempJob); BatchCullingOutputDrawCommands drawCommands = new BatchCullingOutputDrawCommands(); drawCommands.drawRanges = Malloc(m_drawRanges.Length); - drawCommands.drawCommands = Malloc(m_drawBatches.Length); + drawCommands.drawCommands = Malloc(m_drawBatches.Length * + splitCounts.Length * 10); // TODO: Multiplying the DrawCommand count by splitCount*10 is NOT an conservative upper bound. But in practice is enough. Sorting would give us a real conservative bound... + drawCommands.drawCommandPickingInstanceIDs = Malloc(m_drawBatches.Length); drawCommands.visibleInstances = Malloc(m_instanceIndices.Length); @@ -315,12 +358,13 @@ public JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCulling cullingOutput.drawCommands[0] = drawCommands; - var visibilityLength = (m_renderers.Length + 63) / 64; + var visibilityLength = (m_renderers.Length + 7) / 8; var rendererVisibility = new NativeArray(visibilityLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); var cullingJob = new CullingJob { planes = planes, + splitCounts = splitCounts, renderers = m_renderers, rendererVisibility = rendererVisibility }; @@ -336,7 +380,7 @@ public JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCulling drawCommands = cullingOutput.drawCommands }; - var jobHandleCulling = cullingJob.Schedule(visibilityLength, 2); + var jobHandleCulling = cullingJob.Schedule(visibilityLength, 8); var jobHandleOutput = drawOutputJob.Schedule(jobHandleCulling); return jobHandleOutput; diff --git a/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BatchRenderGroupRaw/BRGSetup.cs b/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BatchRenderGroupRaw/BRGSetup.cs index 847c019a903..03638c13d72 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BatchRenderGroupRaw/BRGSetup.cs +++ b/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/BatchRenderGroupRaw/BRGSetup.cs @@ -110,6 +110,7 @@ public JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCulling materialID = m_materialID, meshID = m_meshID, submeshIndex = 0, + splitVisibilityMask = 0xff, flags = m_motionVectorTest ? BatchDrawCommandFlags.HasMotion : BatchDrawCommandFlags.None, sortingPosition = 0 }; @@ -142,30 +143,26 @@ void Start() // Batch metadata buffer int objectToWorldID = Shader.PropertyToID("unity_ObjectToWorld"); int matrixPreviousMID = Shader.PropertyToID("unity_MatrixPreviousM"); - - // int worldToObjectID = Shader.PropertyToID("unity_WorldToObject"); + int worldToObjectID = Shader.PropertyToID("unity_WorldToObject"); int colorID = Shader.PropertyToID("_BaseColor"); // Generate a grid of objects... - int bigDataBufferVector4Count = itemCount * 3 * 2 + itemCount; // mat4x3, colors + int bigDataBufferVector4Count = 4 + itemCount * (3 * 3 + 1); // 4xfloat4 zero + per instance = { 3x mat4x3, 1x float4 color } m_sysmemBuffer = new NativeArray(bigDataBufferVector4Count, Allocator.Persistent, NativeArrayOptions.ClearMemory); m_GPUPersistentInstanceData = new GraphicsBuffer(GraphicsBuffer.Target.Raw, (int)bigDataBufferVector4Count * 16 / 4, 4); - // Matrices - /* - * mat4x3 packed like this: - * - float4x4( - p1.x, p1.w, p2.z, p3.y, - p1.y, p2.x, p2.w, p3.z, - p1.z, p2.y, p3.x, p3.w, - 0.0, 0.0, 0.0, 1.0 - ); - */ + // 64 bytes of zeroes, so loads from address 0 return zeroes. This is a BatchRendererGroup convention. + int positionOffset = 4; + m_sysmemBuffer[0] = new Vector4(0, 0, 0, 0); + m_sysmemBuffer[1] = new Vector4(0, 0, 0, 0); + m_sysmemBuffer[2] = new Vector4(0, 0, 0, 0); + m_sysmemBuffer[3] = new Vector4(0, 0, 0, 0); + // Matrices UpdatePositions(m_center); + // Colors - int colorOffset = itemCount * 3 * 2; + int colorOffset = positionOffset + itemCount * 3 * 3; for (int i = 0; i < itemCount; i++) { Color col = Color.HSVToRGB(((float)(i) / (float)itemCount) % 1.0f, 1.0f, 1.0f); @@ -175,10 +172,11 @@ void Start() } m_GPUPersistentInstanceData.SetData(m_sysmemBuffer); - var batchMetadata = new NativeArray(3, Allocator.Temp, NativeArrayOptions.UninitializedMemory); - batchMetadata[0] = CreateMetadataValue(objectToWorldID, 0, true); // matrices - batchMetadata[1] = CreateMetadataValue(matrixPreviousMID, itemCount * UnsafeUtility.SizeOf() * 3, true); // previous matrices - batchMetadata[2] = CreateMetadataValue(colorID, itemCount * UnsafeUtility.SizeOf() * 3 * 2, true); // colors + var batchMetadata = new NativeArray(4, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + batchMetadata[0] = CreateMetadataValue(objectToWorldID, 64, true); // matrices + batchMetadata[1] = CreateMetadataValue(matrixPreviousMID, 64 + itemCount * UnsafeUtility.SizeOf() * 3, true); // previous matrices + batchMetadata[2] = CreateMetadataValue(worldToObjectID, 64 + itemCount * UnsafeUtility.SizeOf() * 3 * 2, true); // inverse matrices + batchMetadata[3] = CreateMetadataValue(colorID, 64 + itemCount * UnsafeUtility.SizeOf() * 3 * 3, true); // colors // Register batch m_batchID = m_BatchRendererGroup.AddBatch(batchMetadata, m_GPUPersistentInstanceData.bufferHandle); @@ -188,7 +186,8 @@ void Start() void UpdatePositions(Vector3 pos) { - int itemCountOffset = itemGridSize * itemGridSize * 3; // 3 float4 per matrix + int positionOffset = 4; + int itemCountOffset = itemGridSize * itemGridSize * 3; // 3xfloat4 per matrix for (int z = 0; z < itemGridSize; z++) { for (int x = 0; x < itemGridSize; x++) @@ -197,16 +196,31 @@ void UpdatePositions(Vector3 pos) float pz = (z - itemGridSize / 2) * m_spacingFactor; int i = z * itemGridSize + x; + /* + * mat4x3 packed like this: + * + float4x4( + p1.x, p1.w, p2.z, p3.y, + p1.y, p2.x, p2.w, p3.z, + p1.z, p2.y, p3.x, p3.w, + 0.0, 0.0, 0.0, 1.0 + ); + */ + // update previous matrix with previous frame current matrix - m_sysmemBuffer[i * 3 + 0 + itemCountOffset] = m_sysmemBuffer[i * 3 + 0]; - m_sysmemBuffer[i * 3 + 1 + itemCountOffset] = m_sysmemBuffer[i * 3 + 1]; - m_sysmemBuffer[i * 3 + 2 + itemCountOffset] = m_sysmemBuffer[i * 3 + 2]; + m_sysmemBuffer[positionOffset + i * 3 + 0 + itemCountOffset] = m_sysmemBuffer[positionOffset + i * 3 + 0]; + m_sysmemBuffer[positionOffset + i * 3 + 1 + itemCountOffset] = m_sysmemBuffer[positionOffset + i * 3 + 1]; + m_sysmemBuffer[positionOffset + i * 3 + 2 + itemCountOffset] = m_sysmemBuffer[positionOffset + i * 3 + 2]; // compute the new current frame matrix - m_sysmemBuffer[i * 3 + 0] = new Vector4(1, 0, 0, 0); // hacky float3x4 layout - m_sysmemBuffer[i * 3 + 1] = new Vector4(1, 0, 0, 0); - m_sysmemBuffer[i * 3 + 2] = new Vector4(1, px + pos.x, pos.y, pz + pos.z); - + m_sysmemBuffer[positionOffset + i * 3 + 0] = new Vector4(1, 0, 0, 0); + m_sysmemBuffer[positionOffset + i * 3 + 1] = new Vector4(1, 0, 0, 0); + m_sysmemBuffer[positionOffset + i * 3 + 2] = new Vector4(1, px + pos.x, pos.y, pz + pos.z); + + // compute the new inverse matrix + m_sysmemBuffer[positionOffset + i * 3 + 0 + itemCountOffset * 2] = new Vector4(1, 0, 0, 0); + m_sysmemBuffer[positionOffset + i * 3 + 1 + itemCountOffset * 2] = new Vector4(1, 0, 0, 0); + m_sysmemBuffer[positionOffset + i * 3 + 2 + itemCountOffset * 2] = new Vector4(1, -(px + pos.x), -pos.y, -(pz + pos.z)); } } diff --git a/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/SimpleExample/SimpleBRGExample.cs b/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/SimpleExample/SimpleBRGExample.cs index 7aeb62cb3d9..c5a7af96b6a 100644 --- a/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/SimpleExample/SimpleBRGExample.cs +++ b/TestProjects/BatchRendererGroup_URP/Assets/SampleScenes/SimpleExample/SimpleBRGExample.cs @@ -203,6 +203,7 @@ public unsafe JobHandle OnPerformCulling( drawCommands->drawCommands = (BatchDrawCommand*)UnsafeUtility.Malloc(UnsafeUtility.SizeOf(), alignment, Allocator.TempJob); drawCommands->drawRanges = (BatchDrawRange*)UnsafeUtility.Malloc(UnsafeUtility.SizeOf(), alignment, Allocator.TempJob); drawCommands->visibleInstances = (int*)UnsafeUtility.Malloc(kNumInstances * sizeof(int), alignment, Allocator.TempJob); + drawCommands->drawCommandPickingInstanceIDs = null; drawCommands->drawCommandCount = 1; drawCommands->drawRangeCount = 1; @@ -221,6 +222,7 @@ public unsafe JobHandle OnPerformCulling( drawCommands->drawCommands[0].materialID = m_MaterialID; drawCommands->drawCommands[0].meshID = m_MeshID; drawCommands->drawCommands[0].submeshIndex = 0; + drawCommands->drawCommands[0].splitVisibilityMask = 0xff; drawCommands->drawCommands[0].flags = 0; drawCommands->drawCommands[0].sortingPosition = 0;