From 76d064298ffaa6fc02d07e4078e6a6ebdb63338a Mon Sep 17 00:00:00 2001 From: Unity Technologies <@unity> Date: Tue, 11 Jun 2024 00:00:00 +0000 Subject: [PATCH] com.unity.entities.graphics@1.3.0-exp.1 ## [1.3.0-exp.1] - 2024-06-11 ### Changed * Updated entities packages dependencies ### Fixed * Fixed an issue with RegisterMaterialsAndMeshes --- CHANGELOG.md | 14 +- Documentation~/TableOfContents.md | 9 - ...t-occlusion-culling-components-occluder.md | 19 - ...usion-culling-components-occlusion-view.md | 16 - .../burst-occlusion-culling-components.md | 12 - .../burst-occlusion-culling-debug.md | 57 - .../burst-occlusion-culling-optimize.md | 13 - .../burst-occlusion-culling-overview.md | 66 - .../burst-occlusion-culling-requirements.md | 45 - .../burst-occlusion-culling-setup.md | 37 - Documentation~/burst-occlusion-culling.md | 19 - Documentation~/entities-graphics-versions.md | 1 - LICENSE.md | 2 +- .../EntitiesGraphicsSystem.cs | 32 +- Unity.Entities.Graphics/Occlusion.meta | 8 - Unity.Entities.Graphics/Occlusion/Masked.meta | 8 - .../Occlusion/Masked/BufferGroup.cs | 207 -- .../Occlusion/Masked/BufferGroup.cs.meta | 11 - .../Occlusion/Masked/ClearJob.cs | 24 - .../Occlusion/Masked/ClearJob.cs.meta | 11 - .../Occlusion/Masked/ClippedOccluder.cs | 20 - .../Occlusion/Masked/ClippedOccluder.cs.meta | 11 - .../Occlusion/Masked/ComputeBoundsJob.cs | 126 -- .../Occlusion/Masked/ComputeBoundsJob.cs.meta | 11 - .../Occlusion/Masked/Dots.meta | 8 - .../Occlusion/Masked/Dots/OccludeeBaking.cs | 154 -- .../Masked/Dots/OccludeeBaking.cs.meta | 11 - .../Occlusion/Masked/Dots/OccluderBaking.cs | 153 -- .../Masked/Dots/OccluderBaking.cs.meta | 11 - .../Occlusion/Masked/Dots/OccluderMesh.cs | 494 ----- .../Masked/Dots/OccluderMesh.cs.meta | 11 - .../Occlusion/Masked/Dots/OcclusionTest.cs | 27 - .../Masked/Dots/OcclusionTest.cs.meta | 11 - .../Occlusion/Masked/IntrinsicUtils.cs | 221 -- .../Occlusion/Masked/IntrinsicUtils.cs.meta | 11 - .../Occlusion/Masked/MeshTransformJob.cs | 61 - .../Occlusion/Masked/MeshTransformJob.cs.meta | 11 - .../Occlusion/Masked/RasterizeJob.cs | 1812 ----------------- .../Occlusion/Masked/RasterizeJob.cs.meta | 11 - .../Occlusion/Masked/TestJob.cs | 463 ----- .../Occlusion/Masked/TestJob.cs.meta | 11 - .../Occlusion/Masked/Types.cs | 29 - .../Occlusion/Masked/Types.cs.meta | 11 - .../Occlusion/Masked/Visualization.meta | 8 - .../Masked/Visualization/DebugSettings.cs | 172 -- .../Visualization/DebugSettings.cs.meta | 11 - .../Masked/Visualization/DebugView.cs | 376 ---- .../Masked/Visualization/DebugView.cs.meta | 11 - .../Visualization/DecodeMaskedDepthJob.cs | 96 - .../DecodeMaskedDepthJob.cs.meta | 11 - .../Visualization/FilterOccludedTestJob.cs | 52 - .../FilterOccludedTestJob.cs.meta | 11 - .../Visualization/MeshAggregationJob.cs | 72 - .../Visualization/MeshAggregationJob.cs.meta | 11 - .../Masked/Visualization/OccludeeAABBJob.cs | 44 - .../Visualization/OccludeeAABBJob.cs.meta | 11 - .../Visualization/OccludeeOutlineJob.cs | 92 - .../Visualization/OccludeeOutlineJob.cs.meta | 11 - Unity.Entities.Graphics/Occlusion/Occluder.cs | 73 - .../Occlusion/Occluder.cs.meta | 11 - .../Occlusion/OccluderInspector.cs | 61 - .../Occlusion/OccluderInspector.cs.meta | 11 - .../Occlusion/OcclusionBrowseWindow.cs | 184 -- .../Occlusion/OcclusionBrowseWindow.cs.meta | 11 - .../Occlusion/OcclusionBrowseWindow.uss | 94 - .../Occlusion/OcclusionBrowseWindow.uss.meta | 11 - .../Occlusion/OcclusionBrowseWindow.uxml | 7 - .../Occlusion/OcclusionBrowseWindow.uxml.meta | 10 - .../Occlusion/OcclusionDebugRenderSystem.cs | 129 -- .../OcclusionDebugRenderSystem.cs.meta | 11 - .../Occlusion/OcclusionMenu.cs | 149 -- .../Occlusion/OcclusionMenu.cs.meta | 11 - .../Occlusion/OcclusionSortJob.cs | 18 - .../Occlusion/OcclusionSortJob.cs.meta | 11 - .../Occlusion/OcclusionView.cs | 99 - .../Occlusion/OcclusionView.cs.meta | 11 - .../Occlusion/OcclusionViewEditor.cs | 69 - .../Occlusion/OcclusionViewEditor.cs.meta | 11 - Unity.Entities.Graphics/Occlusion/UI.meta | 8 - .../Occlusion/UI/OcclusionBrowserView.cs | 243 --- .../Occlusion/UI/OcclusionBrowserView.cs.meta | 11 - .../Occlusion/UI/OcclusionBrowserView.uss | 4 - .../UI/OcclusionBrowserView.uss.meta | 11 - .../Occlusion/UI/OcclusionBrowserView.uxml | 27 - .../UI/OcclusionBrowserView.uxml.meta | 10 - .../Occlusion/UnityOcclusion.cs | 488 ----- .../Occlusion/UnityOcclusion.cs.meta | 11 - .../Resources/Occlusion.meta | 8 - .../Occlusion/OccludeeScreenSpaceAABB.shader | 46 - .../OccludeeScreenSpaceAABB.shader.meta | 10 - .../Occlusion/OcclusionDebugComposite.shader | 80 - .../OcclusionDebugComposite.shader.meta | 13 - .../Occlusion/OcclusionDebugOccluders.shader | 75 - .../OcclusionDebugOccluders.shader.meta | 9 - .../Occlusion/ShowOccluderMesh.shadergraph | 24 - .../ShowOccluderMesh.shadergraph.meta | 10 - ValidationExceptions.json | 13 +- package.json | 12 +- 98 files changed, 29 insertions(+), 7365 deletions(-) delete mode 100644 Documentation~/burst-occlusion-culling-components-occluder.md delete mode 100644 Documentation~/burst-occlusion-culling-components-occlusion-view.md delete mode 100644 Documentation~/burst-occlusion-culling-components.md delete mode 100644 Documentation~/burst-occlusion-culling-debug.md delete mode 100644 Documentation~/burst-occlusion-culling-optimize.md delete mode 100644 Documentation~/burst-occlusion-culling-overview.md delete mode 100644 Documentation~/burst-occlusion-culling-requirements.md delete mode 100644 Documentation~/burst-occlusion-culling-setup.md delete mode 100644 Documentation~/burst-occlusion-culling.md delete mode 100644 Unity.Entities.Graphics/Occlusion.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/BufferGroup.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/BufferGroup.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/ClearJob.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/ClearJob.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/ClippedOccluder.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/ClippedOccluder.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/ComputeBoundsJob.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/ComputeBoundsJob.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Dots.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Dots/OccludeeBaking.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Dots/OccludeeBaking.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderBaking.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderBaking.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderMesh.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderMesh.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Dots/OcclusionTest.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Dots/OcclusionTest.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/IntrinsicUtils.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/IntrinsicUtils.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/MeshTransformJob.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/MeshTransformJob.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/RasterizeJob.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/RasterizeJob.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/TestJob.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/TestJob.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Types.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Types.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugSettings.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugSettings.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugView.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugView.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/DecodeMaskedDepthJob.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/DecodeMaskedDepthJob.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/FilterOccludedTestJob.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/FilterOccludedTestJob.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/MeshAggregationJob.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/MeshAggregationJob.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeAABBJob.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeAABBJob.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeOutlineJob.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeOutlineJob.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/Occluder.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/Occluder.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/OccluderInspector.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/OccluderInspector.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uss delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uss.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uxml delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uxml.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionDebugRenderSystem.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionDebugRenderSystem.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionMenu.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionMenu.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionSortJob.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionSortJob.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionView.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionView.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionViewEditor.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/OcclusionViewEditor.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/UI.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/UI/OcclusionBrowserView.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/UI/OcclusionBrowserView.cs.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/UI/OcclusionBrowserView.uss delete mode 100644 Unity.Entities.Graphics/Occlusion/UI/OcclusionBrowserView.uss.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/UI/OcclusionBrowserView.uxml delete mode 100644 Unity.Entities.Graphics/Occlusion/UI/OcclusionBrowserView.uxml.meta delete mode 100644 Unity.Entities.Graphics/Occlusion/UnityOcclusion.cs delete mode 100644 Unity.Entities.Graphics/Occlusion/UnityOcclusion.cs.meta delete mode 100644 Unity.Entities.Graphics/Resources/Occlusion.meta delete mode 100644 Unity.Entities.Graphics/Resources/Occlusion/OccludeeScreenSpaceAABB.shader delete mode 100644 Unity.Entities.Graphics/Resources/Occlusion/OccludeeScreenSpaceAABB.shader.meta delete mode 100644 Unity.Entities.Graphics/Resources/Occlusion/OcclusionDebugComposite.shader delete mode 100644 Unity.Entities.Graphics/Resources/Occlusion/OcclusionDebugComposite.shader.meta delete mode 100644 Unity.Entities.Graphics/Resources/Occlusion/OcclusionDebugOccluders.shader delete mode 100644 Unity.Entities.Graphics/Resources/Occlusion/OcclusionDebugOccluders.shader.meta delete mode 100644 Unity.Entities.Graphics/Resources/Occlusion/ShowOccluderMesh.shadergraph delete mode 100644 Unity.Entities.Graphics/Resources/Occlusion/ShowOccluderMesh.shadergraph.meta diff --git a/CHANGELOG.md b/CHANGELOG.md index 27a4e28..62db02c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ uid: changelog # Changelog +## [1.3.0-exp.1] - 2024-06-11 + +### Changed + +* Updated entities packages dependencies + +### Fixed + +* Fixed an issue with RegisterMaterialsAndMeshes + + ## [1.2.3] - 2024-05-30 ### Changed @@ -35,7 +46,7 @@ uid: changelog ### Changed -* Fixed the issue with LOD objects being culled when the camera is static +* fixed the issue with LOD objects being culled when the camera is static ### Fixed @@ -45,7 +56,6 @@ uid: changelog ## [1.2.0-pre.12] - 2024-02-13 - ### Added * `RenderMeshUnmanaged` an unmanaged IComponentData using the new UnityObjRef for big performance gains in baking! diff --git a/Documentation~/TableOfContents.md b/Documentation~/TableOfContents.md index 686795a..71d3123 100644 --- a/Documentation~/TableOfContents.md +++ b/Documentation~/TableOfContents.md @@ -14,15 +14,6 @@ * [Companion Components](companion-components.md) * [The BatchRendererGroup API](batch-renderer-group-api.md) * [Mesh Deformations](mesh_deformations.md) - * [Burst Occlusion Culling](burst-occlusion-culling.md) - * [Requirements and Compatibility](burst-occlusion-culling-requirements.md) - * [Overview](burst-occlusion-culling-overview.md) - * [Setup](burst-occlusion-culling-setup.md) - * [Optimize Occlusion Culling](burst-occlusion-culling-optimize.md) - * [Rendering Debugger Culling Tab Reference](burst-occlusion-culling-debug.md) - * [Components](burst-occlusion-culling-components.md) - * [Occluder Component](burst-occlusion-culling-components-occluder.md) - * [Occlusion View Component](burst-occlusion-culling-components-occlusion-view.md) * [Runtime Usage](runtime-usage.md) * [Runtime Entity Creation](runtime-entity-creation.md) * [Entities Graphics Performance](entities-graphics-performance.md) diff --git a/Documentation~/burst-occlusion-culling-components-occluder.md b/Documentation~/burst-occlusion-culling-components-occluder.md deleted file mode 100644 index a511985..0000000 --- a/Documentation~/burst-occlusion-culling-components-occluder.md +++ /dev/null @@ -1,19 +0,0 @@ -# Occluder component - -An Occluder MonoBehaviour component specifies which entities are occluders. After you attach this component to a GameObject, the [baking process](https://docs.unity3d.com/Packages/com.unity.entities@latest?subfolder=/manual/baking.html) attaches the relevant occluder ECS components to the converted entity. - -> [!TIP] -> The [Scene view](xref:UsingTheSceneView) provides a visual [Gizmo](xref:GizmosMenu) that displays a wireframe of the assigned occluder mesh using the current settings. This helps you to position, rotate, and scale the occluder mesh, if necessary, so it's inscribed within the visual mesh. - -## Occluder Inspector reference - -| **Property** | **Description** | -| ------------------ | ------------------------------------------------------------ | -| **Mesh** | The mesh to use for occlusion culling calculations. This should be a low-poly mesh that is inscribed within the visual mesh. | -| **Local Position** | The position offset to apply to the occlusion mesh. | -| **Local Rotation** | The rotation offset to apply to the occlusion mesh. | -| **Local Scale** | The scale offset to apply to the occlusion mesh. | - -## Additional resource - -* [Occlusion View component](burst-occlusion-culling-components-occlusion-view.md) \ No newline at end of file diff --git a/Documentation~/burst-occlusion-culling-components-occlusion-view.md b/Documentation~/burst-occlusion-culling-components-occlusion-view.md deleted file mode 100644 index b168e1d..0000000 --- a/Documentation~/burst-occlusion-culling-components-occlusion-view.md +++ /dev/null @@ -1,16 +0,0 @@ -# Occlusion View component - -An Occlusion View MonoBehaviour component specifies which cameras, lights, and reflection probes use Burst Occlusion Culling. It also configures the size of the buffer to use for occlusion culling calculations which affects the resource intensity and precision of the calculations. - -## Occlusion View Inspector reference - -| **Property** | **Description** | -| --------------------------- | ------------------------------------------------------------ | -| **Occlusion Enabled** | Controls whether the attached cameras, light, or reflection probes uses Burst Occlusion Culling. | -| **Occlusion Buffer Width** | The width of the buffer to use for occlusion culling calculations. This value should always be a multiple of 16. | -| **Occlusion Buffer Height** | The height of the buffer to use for occlusion culling calculations. This value should always be a multiple of 16. | - -## Additional resource - -* [Occluder component](burst-occlusion-culling-components-occluder.md) -* [Optimize occlusion views](burst-occlusion-culling-optimize.md#optimize-occlusion-views) \ No newline at end of file diff --git a/Documentation~/burst-occlusion-culling-components.md b/Documentation~/burst-occlusion-culling-components.md deleted file mode 100644 index d3b4437..0000000 --- a/Documentation~/burst-occlusion-culling-components.md +++ /dev/null @@ -1,12 +0,0 @@ -# Burst Occlusion Culling components - -This section contains information on the MonoBehaviour components that configure Burst Occlusion Culling. - -| **Topic** | **Description** | -| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| [Occluder component](burst-occlusion-culling-components-occluder.md) | Understand the Occluder component, which creates and configures occluders. | -| [Occlusion View component](burst-occlusion-culling-components-occlusion-view.md) | Understand the Occlusion View component, which configures Burst Occlusion Culling for individual cameras, lights, and reflection probes. | - -## Additional resources - -- [MonoBehaviour](xref:class-MonoBehaviour) diff --git a/Documentation~/burst-occlusion-culling-debug.md b/Documentation~/burst-occlusion-culling-debug.md deleted file mode 100644 index 0560c6d..0000000 --- a/Documentation~/burst-occlusion-culling-debug.md +++ /dev/null @@ -1,57 +0,0 @@ -# Rendering Debugger Culling tab reference - -The **Culling** tab in the [Rendering debugger](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@latest?subfolder=/manual/Render-Pipeline-Debug-Window.html) includes debugging options and visualizations to help you investigate Burst Occlusion Culling issues. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
Freeze OcclusionIndicates whether to temporarily pause the occlusion culling system and maintain the current occlusion results.

This is useful to confirm that objects you expect to be occluded during a single frame from a particular point-of-view are.

For an example of how to use this: enable **Freeze Occlusion** and identify some objects that you expect to be occluded from the current camera’s current point-of-view. Move the camera to a different position to view those objects and visually confirm what was occluded and what wasn't.

**Note**: The culling results include both occlusion culling and frustum culling.
Pinned ViewSpecifies a view to use the occlusion culling results from. Unity uses the occlusion results from the pinned view in all other views. For example, other cameras, reflection probes, and the Scene view camera. When enabled, all views won't render objects that are occluded from the perspective of the pinned view, even if the objects aren't occluded from the perspective of the other views.

This is useful to view occlusion culling results over time.

For an example of how to use this: pin the [main camera](xref:UnityEngine.Camera.main) in your scene, [move the Scene view camera](xref:SceneViewNavigation) around to inspect which objects are hidden. Then move the main camera around to update the culling results.

Select **None** to disable view pinning.

**Note**: The culling results include both occlusion culling and frustum culling.
Debug ModeSpecifies an occlusion culling debug visualization to render.
NoneDisables occlusion culling debug visualizations.
DepthVisualizes the occlusion depth buffer for the camera viewport. This view of the scene only contains occluders. The view also displays the occlusion mesh for the occluders; not their visual mesh. -
TestVisualizes the occlusion depth buffer and shows occluded entities as red squares. This is the same view as **Depth** with the addition of red squares that show the bounds of the occludees that were determined to be hidden and thus not sent to the GPU for rendering in the current frame -
MeshVisualizes the occluder meshes and uses a unique color per mesh. This helps you to view how each configured occlusion mesh aligns with the render mesh and to confirm if the mesh is inscribed.
BoundsVisualizes the axis-aligned bounding boxes for all meshes in the viewport.
InvertedInverts the logic Unity uses to display culling results. When enabled, Unity shows culled objects and hides not-culled objects. **Note**: Unity also renders all objects that aren't occluders or occludees.
- -## Additional resources - -- [Universal Render Pipeline Rendering Debugger](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest?subfolder=/manual/features/rendering-debugger.html) -- [High Definition Render Pipeline Rendering Debugger](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@latest?subfolder=/manual/Render-Pipeline-Debug-Window.html) diff --git a/Documentation~/burst-occlusion-culling-optimize.md b/Documentation~/burst-occlusion-culling-optimize.md deleted file mode 100644 index 960fd98..0000000 --- a/Documentation~/burst-occlusion-culling-optimize.md +++ /dev/null @@ -1,13 +0,0 @@ -# Optimize Burst Occlusion Culling - -If Burst Occlusion Culling is a good fit for a scene (refer to [When to use Burst Occlusion Culling](burst-occlusion-culling-overview.md#when-to-use-burst-occlusion-culling)), you can configure it to be bespokely optimized for the scene. This page explains the different methods you can use to get the best performance out of Burst Occlusion Culling for a particular scene. - -## Optimize occlusion views - -The Burst Occlusion Culling system can use a different buffer resolution for each view it processes. A lower-resolution buffer is less resource-intensive to process but produces a less precise culling result. If a view doesn't require precise occlusion culling results, you can reduce the resolution of its occlusion buffer to increase the performance of the Burst Occlusion Culling process. - -If an occlusion view uses a lower resolution buffer, the Burst Occlusion Culling system can misidentify some totally hidden objects as being visible. This means that the rendering system must unnecessarily process the objects. If you reduce the resolution of an occlusion view buffer, it's best practice to [profile](xref:Profiler) the scene to make sure that the reduced resolution doesn't degrade overall performance. - -## Additional resources - -- [Rendering Debugger Culling tab reference](burst-occlusion-culling-debug.md) diff --git a/Documentation~/burst-occlusion-culling-overview.md b/Documentation~/burst-occlusion-culling-overview.md deleted file mode 100644 index cd89714..0000000 --- a/Documentation~/burst-occlusion-culling-overview.md +++ /dev/null @@ -1,66 +0,0 @@ -# Burst Occlusion Culling overview - -The Burst Occlusion Culling system disables rendering for entities that are hidden behind other entities. This reduces the amount of data that Unity uploads to the GPU every frame and the amount of unnecessary work that the GPU must do. - -## How the Burst Occlusion Culling system works - -The Burst Occlusion Culling system takes a viewpoint, either a camera, reflection probe, or a light with shadows enabled, and calculates which entities are visible and which are hidden behind other entities. Unity then uses this information to only send visible entities to the GPU for rendering. This improves performance if the added CPU overhead from the time it takes to calculate which entities are visible is less than the time it would take to render the hidden entities on the GPU. The hidden entities render time includes the CPU draw overhead, the upload time from the CPU to GPU, and the GPU time it would take to render the hidden entities. - -The Burst Occlusion Culling system uses the following concepts: - -* **Occlusion View**: Any viewpoint for which the system is enabled to calculate occlusion culling. -* **Occluder**: An entity that can hide other entities. Any entity that has an occluder component added with a low resolution fully inscribed mesh which is used to determine the screen space coverage of the original mesh and used to test if other meshes are fully occluded by this mesh. -* **Occludee**: Any entity that can be hidden behind occluders and which may be culled if it's fully hidden by an occluder entity. -* **Inscribed Mesh**: A mesh is fully inscribed if it's fully enclosed within the original mesh. This means that when the inscribed mesh is projected and rendered into screen space, it's always within the area that's rendered for the original mesh. This provides a view consistent area coverage of the original mesh that can be used to determine if other objects are fully hidden behind it. - -For each occlusion view, the Burst Occlusion Culling system calculates which occluders hide which occludees, and for any remaining occludees that are not hidden, the render pipeline will handle those as normal and dispatch them to the GPU for rendering. - -Entities that use the Mesh Renderer component with **Dynamic Occlusion** enabled at [author time](https://docs.unity3d.com/Packages/com.unity.entities@latest?subfolder=/manual/editor-authoring-runtime.html) are automatically occludees. To fully setup Burst Occlusion Culling in your scene, you additionally need to specify which entities are occluders, and which viewpoints are occlusion views. For help on how to decide which entities should be occluders, refer to [How to choose occluders](#how-to-choose-occluders). For help of which cameras, reflections probes, and lights should be occlusion views, refer to [How to choose occlusion views](#how-to-choose-occlusion-views). - -For performance reasons, the culling system is not intended to use the same meshes in its culling calculations that the rendering system uses to draw entities. Instead, each occluder entity allows you to specify an additional mesh for the culling system to use. This occlusion mesh must be completely inscribed within the original mesh to avoid artifacts such as visible popping (which is where objects appear and disappear visibly on-screen). The occluder mesh must be completely inscribed within the original mesh for the occlusion culling system to be fully conservative. Otherwise, artifacts such as visible popping can occur (which is where objects appear and disappear visibly on-screen). For optimal performance, the Occlusion Mesh should also have a minimal triangle count. - -## When to use Burst Occlusion Culling - -Burst Occlusion Culling isn't appropriate for every application or scene. Scenes with many unique objects (with unique meshes or materials) that produce a lot of overdraw are perfect for Burst Occlusion Culling. Examples of this type of scene include large open worlds, dense cities, or interiors with separate rooms. - -Entities graphics can render instanced objects very quickly so it's often not beneficial to calculate which instanced objects are or aren't occluded and instead pass them all to the GPU to render. This is because the overhead of the occlusion culling calculations can exceed the overhead saved by reducing the number of instanced objects to draw. - -If there is a mix of unique and instanced objects in a scene, you can enable Burst Occlusion Culling for the scene, but make the instanced objects not occludees (disable **Dynamic Occlusion** on their Mesh Renderer component). This makes Burst Occlusion Culling optimize the draw submission for unique objects without wasting resources processing the instanced objects. - -## How to choose occluders - -Occlusion culling gives performance improvements if the overhead of the occlusion culling process is less than the overhead saved by reducing the number of entities to draw. The more occluders there are and the more complex their shape, the more resource intensive the occlusion culling process is. This means that it's important to choose which entities to set as occluders. An entity's size and shape decide its suitability as an occluder. - -Entities likely to be suitable occluders are: - -* Entities that fill a significant part of the screen. For example, in a first-person perspective, items in the character’s hands are close to the camera. Also buildings and other large objects that the character can approach, that move toward the character, or that move around the scene. -* Entities that are likely to occlude other entities. For example, a large mountain or building located in the center of the scene, or the walls in the interior of a building. - -Entities likely to be unsuitable occluders are: - -* Entities unlikely to fill a significant part of the screen. For example, a small book that the camera will never go very close to, or large objects that will always be far away from the camera and will therefore have small screen-space coverage. -* Entities that are unlikely to occlude other entities. For example, mountains at the far distant edge of a world, or a road mesh where there are few/no objects below the ground. Objects that are thin from more than one perspective are also unlikely to occlude other entities. -* Entities that have a complex and non-convex shape. For example, hands or a tree with many branches and leaves. - -## How to choose occlusion views - -By default, every frustum view in your scene (except light probes) is an implicit occlusion view with a 512x512 resolution. To manually configure Burst Occlusion Culling for each view, you can add an Occlusion View component to it. With this component you can explicitly set the resolution of the occlusion buffer and enable/disable Burst Occlusion Culling for the view. - -Not every viewpoint will benefit from occlusion culling, as it dependent on scene content visible in that view, so it's important to carefully choose which viewpoints to set as occlusion views. - -Views likely to be suitable occlusion view are: - -* Cameras that view many or complex (in either material or resolution) objects that are large in screen-space area. - -Views likely to be unsuitable occlusion views - -* Frustum views that have few and/or small or low complexity objects in the view. -* Frustum views which already benefit significantly from frustum culling and/or instancing. - -**Note**: For shadow casting lights occlusion culling, material settings don’t have an impact on performance. This means you shouldn't consider complex materials when deciding whether to use Burst Occlusion Culling for lights. - -**Note**: The resolution of the occlusion buffer can affect the workload distribution across jobs, so just reducing the resolution may not reduce the CPU overhead. Reducing the resolution can also reduce the total amount of hidden geometry, which can reduce the GPU performance gains. If all the occluder triangles for the scene are fairly evenly distrubted across screen tiles, or no single screen tile is overly high in occluder triangle geometry density than all other tiles, then reducing the resolution of the occlduer buffer should reduce the CPU overhead. - -## Additional resources - -- [Set up Burst Occlusion Culling](burst-occlusion-culling-setup.md) diff --git a/Documentation~/burst-occlusion-culling-requirements.md b/Documentation~/burst-occlusion-culling-requirements.md deleted file mode 100644 index 8625310..0000000 --- a/Documentation~/burst-occlusion-culling-requirements.md +++ /dev/null @@ -1,45 +0,0 @@ -# Burst Occlusion Culling requirements and compatibility - -This page contains information on requirements and feature compatibility of Burst Occlusion Culling. Burst Occlusion Culling currently only supports [Entities](https://docs.unity3d.com/Packages/com.unity.entities@latest/index.html)-based applications. - -## Hardware requirements - -Burst Occlusion Culling requires the target CPU to support SSE4 or Neon instructions. Burst doesn't support 32-bit intrinsics for Neon so, to build for ARM, you must use a 64-bit build target. - -## Renderer compatibility - -The following table shows which renderers Burst Occlusion Culling supports. - -| Renderer | Occludee support | Occluder support | -| ------------------------ | ---------------- | ---------------- | -| Mesh Renderer | Yes | Yes | -| Skinned Mesh Renderer | No | No | -| Visual Effect | No | No | -| Particle System Renderer | No | No | -| Light Probe Proxy Volume | No | N/A | -| Sprite Renderer | No | No | -| Decal Projectors | No | No | -| TextMesh | No | No | - -## Occlusion view compatibility - -The following table shows which components Burst Occlusion Culling supports as views: - -| Component | View support | -| ------------------------ | ------------ | -| Camera | Yes | -| Light (for shadows) | Yes | -| Reflection Probe | Yes | -| Planar Reflection Probe | Yes | - - -## Feature compatibility - -Burst Occlusion Culling doesn't support the following: - -* [Mesh deformations](mesh_deformations.md). -* Concurrent usage with Unity's [built-in occlusion culling system](xref:OcclusionCulling). - -## Additional resources - -* [Burst Occlusion Culling overview](burst-occlusion-culling-overview.md) \ No newline at end of file diff --git a/Documentation~/burst-occlusion-culling-setup.md b/Documentation~/burst-occlusion-culling-setup.md deleted file mode 100644 index 0013abd..0000000 --- a/Documentation~/burst-occlusion-culling-setup.md +++ /dev/null @@ -1,37 +0,0 @@ -# Set up Burst Occlusion Culling - -To set up Burst Occlusion Culling in your Unity project: - -1. Enable the feature. -2. Enable and configure Burst Occlusion Culling for individual cameras, lights, and reflection probes. -3. Configure some entities to be occluders. - -## Enable Burst Occlusion Culling - -The first step to set up Burst Occlusion Culling is to enable the feature for your project. To do this: - -1. Set the `ENABLE_UNITY_OCCLUSION` custom scripting symbol. For information on how to do this, refer to [Custom scripting symbols](xref:CustomScriptingSymbols). -2. Ensure that [Burst](https://docs.unity3d.com/Packages/com.unity.burst@latest/index.html) is enabled. To do this, select **Jobs** > **Burst** > **Enable Compilation**. -3. Select **Occlusion** > **Enable**. -4. Burst Occlusion Culling requires the target CPU to support SSE4 instructions. To be able to build a Unity Player, go to **Edit** > **Project Settings** > **Burst AOT Settings** and set **Target CPU architectures** to **SSE4**. - -## Configure per-view occlusion culling - -You can enable and configure Burst Occlusion Culling on a per-camera, per-light, and per reflection probe basis. By default, only the [main camera](xref:UnityEngine.Camera.main) uses Burst Occlusion Culling. To enable Burst Occlusion Culling for a camera, light, and reflection probe, add the **Occlusion View** component and enable the **Occlusion Enable** property. The Occlusion View component also controls the resolution of the occlusion buffer for the camera, light, or reflection probe. The occlusion buffer resolution affects the resource intensity of the occlusion culling calculations. For more information about configuration options and performance, refer to [Optimize Burst Occlusion Culling](burst-occlusion-culling-optimize.md) - -## Create occluders - -By default, [render entities](burst-occlusion-culling-overview.md#how-burst-occlusion-culling-works) are occludees but not occluders. To make an entity able to occlude other entities, you must set it to be an occluder. For performance reasons, not every entity should be an occluder. For information on how to choose which entities to make occluders, see [How to choose occluders](burst-occlusion-culling-overview.md#how-to-choose-occluders). - -To set up an entity as an occluder: - -1. Create the occluder mesh. This is a low-poly mesh that's inscribed within the original mesh. An inscribed mesh is one that's always perfectly inside the volume of the original mesh. You can author an occluder mesh via an external 3D modelling application or with [ProBuilder](https://docs.unity3d.com/Packages/com.unity.probuilder@latest/index.html). -2. If you created the occluder mesh outside of Unity, import it into your project. -3. Select the authoring GameObject and view it in the Inspector. -4. Add an [Occluder component](burst-occlusion-culling-components-occluder.md) to the authoring GameObject. -5. In the Occluder component, set **Mesh** to the occluder mesh asset. -6. If necessary, modify the offset position, scale, and rotation of the occluder mesh so that it lines up with the original mesh. You can use the [Rendering Debugger](burst-occlusion-culling-debug.md) to visualize your changes to position, scale and rotation. - -## Additional resources - -- [Optimize Burst Occlusion Culling](burst-occlusion-culling-optimize.md) diff --git a/Documentation~/burst-occlusion-culling.md b/Documentation~/burst-occlusion-culling.md deleted file mode 100644 index 6396471..0000000 --- a/Documentation~/burst-occlusion-culling.md +++ /dev/null @@ -1,19 +0,0 @@ -# Burst Occlusion Culling - -Burst Occlusion Culling is a Burst-optimized occlusion culling system available in the Entities Graphics package. It provides a cross-platform occlusion system that disables the rendering of objects when they're occluded by other objects, and hence are not seen by the camera. - -> [!IMPORTANT] -> This version of Burst Occlusion Culling is experimental. This means that it isn't yet ready to use for production and parts of the implementation and API will change. - -| **Topic** | **Description** | -| ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -| [Requirements and compatibility](burst-occlusion-culling-requirements.md) | Learn about the system requirements and feature compatibility of Burst Occlusion Culling. | -| [Overview](burst-occlusion-culling-overview.md) | Learn how the Burst Occlusion Culling system works and whether it's suitable for your project. | -| [Setup](burst-occlusion-culling-setup.md) | Enable Burst Occlusion Culling and create your first occluders. | -| [Optimize occlusion culling](burst-occlusion-culling-optimize.md) | Optimize Burst Occlusion Culling for your project. | -| [Rendering Debugger Culling tab reference](burst-occlusion-culling-debug.md) | Learn about the debugging options and visualizations available to help you investigate Burst Occlusion Culling issues. | -| [Components](burst-occlusion-culling-components.md) | Learn about the MonoBehaviour components that control which views use Burst Occlusion Culling and which objects act as occluders. | - -## Additional resources - -- [Burst](https://docs.unity3d.com/Packages/com.unity.burst@latest/index.html) diff --git a/Documentation~/entities-graphics-versions.md b/Documentation~/entities-graphics-versions.md index d1408a2..5e1c4df 100644 --- a/Documentation~/entities-graphics-versions.md +++ b/Documentation~/entities-graphics-versions.md @@ -31,7 +31,6 @@ The following table compares Entities Graphics feature support between HDRP and | **Sorted Transparencies** | Yes | Yes | | **Color Space** | Only Linear color space is supported | Only Linear color space is supported | | **Rendering Path** | Only Forward+ is supported | Yes | -| **Burst Occlusion culling** | Experimental

Note: Must enable define and configure scene with occluders. See [Burst Occlusion Culling](burst-occlusion-culling.md). | Experimental

Note: Must enable define and configure scene with occluders. See [Burst Occlusion Culling](burst-occlusion-culling.md). | | **Skinning & deformations** | Experimental

Note: Must use Compute Deformation node in Shader Graph. See [Mesh Deformations](mesh_deformations.md). | Experimental

Note: Must use Compute Deformation node in Shader Graph. See [Mesh Deformations](mesh_deformations.md). | | **Texture Streaming** | No | No | | **Streaming Virtual Texturing** | N/A | No | diff --git a/LICENSE.md b/LICENSE.md index e4ffe4c..9cab06b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -com.unity.entities.graphics copyright © 2023 Unity Technologies ApS +com.unity.entities.graphics copyright © 2024 Unity Technologies ApS Licensed under the Unity Companion License for Unity-dependent projects (see https://unity3d.com/legal/licenses/unity_companion_license). diff --git a/Unity.Entities.Graphics/EntitiesGraphicsSystem.cs b/Unity.Entities.Graphics/EntitiesGraphicsSystem.cs index 2652a2a..003847a 100644 --- a/Unity.Entities.Graphics/EntitiesGraphicsSystem.cs +++ b/Unity.Entities.Graphics/EntitiesGraphicsSystem.cs @@ -39,10 +39,6 @@ #define DEBUG_PROPERTY_NAMES #endif -#if ENABLE_UNITY_OCCLUSION -#define USE_UNITY_OCCLUSION -#endif - #if UNITY_EDITOR && !DISABLE_HYBRID_RENDERER_PICKING #define ENABLE_PICKING #endif @@ -79,10 +75,6 @@ using UnityEditor; #endif -#if USE_UNITY_OCCLUSION -using Unity.Rendering.Occlusion; -#endif - namespace Unity.Rendering { // Describes a single material property that can be mapped to an ECS type. @@ -465,7 +457,7 @@ private JobHandle RegisterMaterialsAndMeshes(JobHandle inputDeps) sortedKeys.Sort(); // Single pass O(n) algorithm. Both arrays are guaranteed to be sorted. - for (int i = 0, j = 0; (i < sortedKeys.Length) && (j < renderArrays.Count); i++) + for (int i = 0, j = 0; i < sortedKeys.Length; i++) { var oldKey = sortedKeys[i]; while ((j < renderArrays.Count) && (sharedIndices[j] < oldKey)) @@ -887,10 +879,6 @@ private void ValidateUsingURPForwardPlus() internal static Dictionary s_TypeIndexToName = new Dictionary(); #endif -#if USE_UNITY_OCCLUSION - internal OcclusionCulling OcclusionCulling { get; private set; } -#endif - private bool m_FirstFrameAfterInit; private EntitiesGraphicsArchetypes m_GraphicsArchetypes; @@ -1077,11 +1065,6 @@ protected override void OnCreate() m_GPUUploader = new SparseUploader(m_GPUPersistentInstanceData, kGPUUploaderChunkSize); -#if USE_UNITY_OCCLUSION - OcclusionCulling = new OcclusionCulling(); - OcclusionCulling.Create(EntityManager); -#endif - m_ThreadLocalAllocators = new ThreadLocalAllocator(-1); if (ErrorShaderEnabled) @@ -1421,10 +1404,6 @@ private void Dispose() m_SortedBatchIds = null; -#if USE_UNITY_OCCLUSION - OcclusionCulling.Dispose(); -#endif - m_GraphicsArchetypes.Dispose(); m_FilterSettings.Dispose(); @@ -1596,15 +1575,6 @@ private JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCullin var disposeFrustumCullingHandle = frustumCullingJob.IncludeExcludeListFilter.Dispose(frustumCullingJobHandle); DidScheduleCullingJob(frustumCullingJobHandle); -#if USE_UNITY_OCCLUSION - var occlusionCullingDependency = OcclusionCulling.Cull(EntityManager, cullingContext, m_CullingJobDependency, visibilityItems -#if UNITY_EDITOR - , m_PerThreadStats -#endif - ); - DidScheduleCullingJob(occlusionCullingDependency); -#endif - // TODO: Dynamically estimate this based on past frames int binCountEstimate = 1; var chunkDrawCommandOutput = new ChunkDrawCommandOutput( diff --git a/Unity.Entities.Graphics/Occlusion.meta b/Unity.Entities.Graphics/Occlusion.meta deleted file mode 100644 index 4265ddf..0000000 --- a/Unity.Entities.Graphics/Occlusion.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 52fd9427620b68c48916b6dc55f336af -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked.meta b/Unity.Entities.Graphics/Occlusion/Masked.meta deleted file mode 100644 index 1491e32..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a46e7d254c5e82e4ead4bca63bd77d10 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/BufferGroup.cs b/Unity.Entities.Graphics/Occlusion/Masked/BufferGroup.cs deleted file mode 100644 index cc05bb7..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/BufferGroup.cs +++ /dev/null @@ -1,207 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Burst.Intrinsics; -using Unity.Collections; -using Unity.Entities; -using Unity.Jobs; -using Unity.Mathematics; -using Unity.Rendering.Occlusion.Masked.Visualization; -using UnityEngine; -using UnityEngine.Profiling; -using UnityEngine.Rendering; - -namespace Unity.Rendering.Occlusion.Masked -{ - - class BufferGroup - { - public const int TileWidthShift = 5; - public const int TileHeightShift = 2; - public const int TileWidth = 1 << TileWidthShift; - public const int TileHeight = 1 << TileHeightShift; - // Sub-tiles (used for updating the masked HiZ buffer) are 8x4 tiles, so there are 4x2 sub-tiles in a tile - public const int SubTileWidth = 8; - public const int SubTileHeight = 4; - // The number of fixed point bits used to represent vertex coordinates / edge slopes. - public const int FpBits = 8; - public const int FpHalfPixel = 1 << (FpBits - 1); - // Tile dimensions in fixed point coordinates - public const int FpTileHeightShift = (FpBits + TileHeightShift); - public const int FpTileHeight = (1 << FpTileHeightShift); - // Size of guard band in pixels. Clipping doesn't seem to be very expensive so we use a small guard band - // to improve rasterization performance. It's not recommended to set the guard band to zero, as this may - // cause leakage along the screen border due to precision/rounding. - public const float GuardBandPixelSize = 1f; - - // Depth buffer - public int NumBuffers; - public int NumPixelsX; - public int NumPixelsY; - public int NumTilesX; - public int NumTilesY; - - public NativeArray Tiles; - - // Resolution-dependent values - public v128 PixelCenterX; - public v128 PixelCenterY; - public v128 PixelCenter; - public v128 HalfWidth; - public v128 HalfHeight; - public v128 HalfSize; - public v128 ScreenSize; - - public readonly BatchCullingViewType ViewType; - public Matrix4x4 CullingMatrix; - public BatchCullingProjectionType ProjectionType; - public float NearClip; - public NativeArray FrustumPlanes; - public ScissorRect FullScreenScissor; - - // Visualization - DebugView m_DebugView; - - public bool Enabled; - - public BufferGroup(BatchCullingViewType viewType) - { - ViewType = viewType; - NumBuffers = math.clamp(Jobs.LowLevel.Unsafe.JobsUtility.JobWorkerMaximumCount, 1, 10); - NearClip = float.MaxValue; - FrustumPlanes = new NativeArray(5, Allocator.Persistent); - Enabled = true; - } - - public void Dispose() - { - FrustumPlanes.Dispose(); - if (Tiles.IsCreated) - { - Tiles.Dispose(); - } - - m_DebugView?.Dispose(); - } - - public void SetResolutionAndClip(int numPixelsX, int numPixelsY, BatchCullingProjectionType projectionType, float nearClip) - { - if (numPixelsX != NumPixelsX || numPixelsY != NumPixelsY || projectionType != ProjectionType) - { - NumPixelsX = numPixelsX; - NumPixelsY = numPixelsY; - NumTilesX = (numPixelsX + TileWidth - 1) >> TileWidthShift; - NumTilesY = (numPixelsY + TileHeight - 1) >> TileHeightShift; - - float w = numPixelsX; // int -> float - float h = numPixelsY; // - float hw = w * 0.5f; - float hh = h * 0.5f; - PixelCenterX = new v128(hw); - PixelCenterY = new v128(hh); - PixelCenter = new v128(hw, hw, hh, hh); - HalfWidth = new v128(hw); - HalfHeight = new v128(-hh); - HalfSize = new v128(hw, hw, -hh, -hh); - ScreenSize = new v128(numPixelsX - 1, numPixelsX - 1, numPixelsY - 1, numPixelsY - 1); - // TODO: Delete this after full implementation. This isn't needed because min values are zero, and - // so there is opportunity for optimization. - // Setup a full screen scissor rectangle - FullScreenScissor.mMinX = 0; - FullScreenScissor.mMinY = 0; - FullScreenScissor.mMaxX = NumTilesX << TileWidthShift; - FullScreenScissor.mMaxY = NumTilesY << TileHeightShift; - // Allocate the tiles buffers - if (Tiles.IsCreated) - { - Tiles.Dispose(); - } - - Tiles = new NativeArray(NumBuffers * NumTilesX * NumTilesY, - Allocator.Persistent, NativeArrayOptions.UninitializedMemory); - - // Set orthographic mode and the rest of the frustum planes - { - ProjectionType = projectionType; - float guardBandWidth = 2f / NumPixelsX; - float guardBandHeight = 2f / NumPixelsY; - - if (projectionType == BatchCullingProjectionType.Orthographic) - { - FrustumPlanes[1] = new v128(1f - guardBandWidth, 0f, 0f, 1f); - FrustumPlanes[2] = new v128(-1f + guardBandWidth, 0f, 0f, 1f); - FrustumPlanes[3] = new v128(0f, 1f - guardBandHeight, 0f, 1f); - FrustumPlanes[4] = new v128(0f, -1f + guardBandHeight, 0f, 1f); - } - else - { - FrustumPlanes[1] = new v128(1f - guardBandWidth, 0f, 1f, 0f); - FrustumPlanes[2] = new v128(-1f + guardBandWidth, 0f, 1f, 0f); - FrustumPlanes[3] = new v128(0f, 1f - guardBandHeight, 1f, 0f); - FrustumPlanes[4] = new v128(0f, -1f + guardBandHeight, 1f, 0f); - } - } - } - - if (NearClip != nearClip) - { - // Set near clip - NearClip = nearClip; - FrustumPlanes[0] = new v128(0f, 0f, 1f, -nearClip); - } - } - - public Texture2D GetVisualizationTexture() - { - if (m_DebugView != null) - { - m_DebugView.ReallocateIfNeeded(NumPixelsX, NumPixelsY); - return m_DebugView.gpuDepth; - } - return null; - } - - public void RenderToTextures(EntityQuery testQuery, EntityQuery meshQuery, JobHandle dependency, DebugRenderMode mode) - { - if (mode == DebugRenderMode.None -#if UNITY_EDITOR - && !OcclusionBrowseWindow.IsVisible -#endif - ) - { - return; - } - - bool refresh = (m_DebugView == null); - if (refresh) - { - m_DebugView = new DebugView(); - } - - dependency.Complete(); - - m_DebugView.ReallocateIfNeeded(NumPixelsX, NumPixelsY); - - Profiler.BeginSample("Occlusion.Debug.RenderView"); - m_DebugView.RenderToTextures(testQuery, meshQuery, this, mode -#if UNITY_EDITOR - , OcclusionBrowseWindow.IsVisible -#endif - ); - Profiler.EndSample(); - -#if UNITY_EDITOR - if (refresh) - { - OcclusionBrowseWindow.Refresh(); - } -#endif - } - - public void RenderToCamera(DebugRenderMode renderMode, Camera camera, CommandBuffer cmd, Mesh fullScreenQuad) - { - m_DebugView?.RenderToCamera(renderMode, camera, cmd, fullScreenQuad, CullingMatrix); - } - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/BufferGroup.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/BufferGroup.cs.meta deleted file mode 100644 index 784bf5c..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/BufferGroup.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6e641247e835adf4db82d2aaeebd5abd -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/ClearJob.cs b/Unity.Entities.Graphics/Occlusion/Masked/ClearJob.cs deleted file mode 100644 index 538931d..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/ClearJob.cs +++ /dev/null @@ -1,24 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Burst; -using Unity.Burst.Intrinsics; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; - -namespace Unity.Rendering.Occlusion.Masked -{ - [BurstCompile] - unsafe struct ClearJob: IJobFor - { - [ReadOnly, NativeDisableUnsafePtrRestriction] public Tile* Tiles; - public void Execute(int i) - { - Tiles[i].zMin0 = new v128(-1f); - Tiles[i].zMin1 = new v128(0); - Tiles[i].mask = new v128(0); - } - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/ClearJob.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/ClearJob.cs.meta deleted file mode 100644 index 2b3afb6..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/ClearJob.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 100431faf4e589a4c972a61f02e4c33d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/ClippedOccluder.cs b/Unity.Entities.Graphics/Occlusion/Masked/ClippedOccluder.cs deleted file mode 100644 index 78d80a1..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/ClippedOccluder.cs +++ /dev/null @@ -1,20 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Mathematics; - -namespace Unity.Rendering.Occlusion.Masked -{ - internal struct ClippedOccluder - { - public int sourceIndexOffset; - public float4 screenMin, screenMax; - - // If a triangle intersects the frustum, it needs to be clipped. The process of clipping converts the triangle - // into a non-triangular polygon with more vertices. Up to 5 new vertices can be added in this process, - // depending on how the triangle intersects the frustum. We allocate a large enough vertex buffer to be able to - // fit all the vertices, and we track how many vertices are actually generated after clipping in this variable. - public int expandedVertexSize; - } -} - -#endif diff --git a/Unity.Entities.Graphics/Occlusion/Masked/ClippedOccluder.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/ClippedOccluder.cs.meta deleted file mode 100644 index 468b55c..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/ClippedOccluder.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 707cf1309fa18e14987952a2ff8a824e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/ComputeBoundsJob.cs b/Unity.Entities.Graphics/Occlusion/Masked/ComputeBoundsJob.cs deleted file mode 100644 index dc0592d..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/ComputeBoundsJob.cs +++ /dev/null @@ -1,126 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Assertions; -using Unity.Burst; -using Unity.Burst.Intrinsics; -using Unity.Collections; -using Unity.Entities; -using Unity.Mathematics; -using Unity.Transforms; -using Unity.Rendering.Occlusion.Masked.Dots; -using UnityEngine.Rendering; - -namespace Unity.Rendering.Occlusion.Masked -{ - [BurstCompile] - unsafe struct ComputeBoundsJob : IJobChunk - { - [ReadOnly] public ComponentTypeHandle Bounds; - [ReadOnly] public ComponentTypeHandle LocalToWorld; - public ComponentTypeHandle OcclusionTest; - public ComponentTypeHandle ChunkOcclusionTest; - - [ReadOnly] public float4x4 ViewProjection; - [ReadOnly] public float NearClip; - [ReadOnly] public BatchCullingProjectionType ProjectionType; - - public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) - { - const float EPSILON = 1E-12f; - // This job is not written to support queries with enableable component types. - Assert.IsFalse(useEnabledMask); - - var bounds = chunk.GetNativeArray(ref Bounds); - var localToWorld = chunk.GetNativeArray(ref LocalToWorld); - var tests = chunk.GetNativeArray(ref OcclusionTest); - - var verts = stackalloc float4[16]; - - var edges = stackalloc int2[] - { - new int2(0,1), new int2(1,3), new int2(3,2), new int2(2,0), - new int2(4,6), new int2(6,7), new int2(7,5), new int2(5,4), - new int2(4,0), new int2(2,6), new int2(1,5), new int2(7,3) - }; - - float4 screenMin = float.MaxValue; - float4 screenMax = float.MinValue; - - for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++) - { - var aabb = bounds[entityIndex].Value; - var occlusionTest = tests[entityIndex]; - var local = localToWorld[entityIndex].Value; - - occlusionTest.screenMin = float.MaxValue; - occlusionTest.screenMax = -float.MaxValue; - - // TODO: There's likely still room for optimization here. Investigate more approximate bounding box - // calculations which use less ALU ops. - var mvp = math.mul(ViewProjection, local); - - float4x2 u = new float4x2(mvp.c0 * aabb.Min.x, mvp.c0 * aabb.Max.x); - float4x2 v = new float4x2(mvp.c1 * aabb.Min.y, mvp.c1 * aabb.Max.y); - float4x2 w = new float4x2(mvp.c2 * aabb.Min.z, mvp.c2 * aabb.Max.z); - - for (int corner = 0; corner < 8; corner++) - { - float4 p = u[corner & 1] + v[(corner & 2) >> 1] + w[(corner & 4) >> 2] + mvp.c3; - p.y = -p.y; - verts[corner] = p; - } - - int vertexCount = 8; - float clipW = NearClip; - for (int i = 0; i < 12; i++) - { - var e = edges[i]; - var a = verts[e.x]; - var b = verts[e.y]; - - if ((a.w < clipW) != (b.w < clipW)) - { - var p = math.lerp(a, b, (clipW - a.w) / (b.w - a.w)); - verts[vertexCount++] = p; - } - } - - if (ProjectionType == BatchCullingProjectionType.Orthographic) - { - for (int i = 0; i < vertexCount; i++) - { - float4 p = verts[i]; - p.w = p.z; - occlusionTest.screenMin = math.min(occlusionTest.screenMin, p); - occlusionTest.screenMax = math.max(occlusionTest.screenMax, p); - } - } - else - { - for (int i = 0; i < vertexCount; i++) - { - float4 p = verts[i]; - if (p.w >= EPSILON) - { - p.xyz /= p.w; - occlusionTest.screenMin = math.min(occlusionTest.screenMin, p); - occlusionTest.screenMax = math.max(occlusionTest.screenMax, p); - } - } - } - - screenMin = math.min(screenMin, occlusionTest.screenMin); - screenMax = math.max(screenMax, occlusionTest.screenMax); - - tests[entityIndex] = occlusionTest; - } - - var combined = new ChunkOcclusionTest(); - combined.screenMin = screenMin; - combined.screenMax = screenMax; - chunk.SetChunkComponentData(ref ChunkOcclusionTest, combined); - } - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/ComputeBoundsJob.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/ComputeBoundsJob.cs.meta deleted file mode 100644 index 722db14..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/ComputeBoundsJob.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 061fdfe4a0643d145912ec8cc920ca04 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Dots.meta b/Unity.Entities.Graphics/Occlusion/Masked/Dots.meta deleted file mode 100644 index 9a3f1c2..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Dots.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: c269caabbf2f57942928005151c8d275 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccludeeBaking.cs b/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccludeeBaking.cs deleted file mode 100644 index 8cdd547..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccludeeBaking.cs +++ /dev/null @@ -1,154 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Collections; -using Unity.Entities; -using Unity.Entities.Hybrid.Baking; -using UnityEngine; - -namespace Unity.Rendering.Occlusion.Masked.Dots -{ - // This is an empty tag component - [BakingType] - struct ProcessThisOccludee : IComponentData - { - } - - class OccludeeBaker : Baker - { - public override void Bake(MeshRenderer authoring) - { - if (authoring.allowOcclusionWhenDynamic) - { - // Add the tag component, which is then picked up by our baking system - var entity = GetEntity(TransformUsageFlags.Dynamic); - AddComponent(entity, new ProcessThisOccludee()); - } - } - } - - [RequireMatchingQueriesForUpdate] - [WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)] - partial class AddChunkOcclusionTests : SystemBase - { - EntityQuery m_SoloQuery; - EntityQuery m_ParentQuery; - EntityQuery m_NoChunkQuery; - EntityQuery m_RemovedOccludeesQuery; - - /// - protected override void OnCreate() - { - m_SoloQuery = GetEntityQuery - ( - new EntityQueryDesc - { - All = new[] - { - ComponentType.ReadOnly(), - ComponentType.ReadOnly(), - }, - None = new[] {ComponentType.ReadOnly()}, - Options = EntityQueryOptions.IncludeDisabledEntities | EntityQueryOptions.IncludePrefab - } - ); - m_ParentQuery = GetEntityQuery - ( - new EntityQueryDesc - { - All = new[] - { - ComponentType.ReadOnly(), - ComponentType.ReadOnly(), - }, - None = new[] - { - ComponentType.ReadOnly(), - ComponentType.ReadOnly() - }, - Options = EntityQueryOptions.IncludeDisabledEntities | EntityQueryOptions.IncludePrefab - } - ); - m_NoChunkQuery = GetEntityQuery - ( - new EntityQueryDesc - { - All = new[] {ComponentType.ReadOnly()}, - None = new[] {ComponentType.ReadOnly()}, - Options = EntityQueryOptions.IncludeDisabledEntities | EntityQueryOptions.IncludePrefab - } - ); - m_RemovedOccludeesQuery = GetEntityQuery - ( - new EntityQueryDesc - { - All = new[] {ComponentType.ReadOnly()}, - None = new[] {ComponentType.ReadOnly()}, - Options = EntityQueryOptions.IncludeDisabledEntities | EntityQueryOptions.IncludePrefab - } - ); - } - - /// - protected override void OnUpdate() - { - // By default, the `OcclusionTest` component is not cleaned up live from the entity if you remove the - // `Occludee` monobehavior from the GameObject in the subscene. - // This is why we've made `ProcessThisOccludee` a `[BakingType]` so that it isn't removed automatically in a - // Bake pass, but kept alive in the Baking-only world. - // - // Now we use this persistant tag to identify removal. This works because Bakers will automatically remove - // any components they add when the MonoBehaviour for the Baker is removed. - { - var entities = m_RemovedOccludeesQuery.ToEntityArray(Allocator.Temp); - EntityManager.RemoveComponent(entities); - for (int i = 0; i < entities.Length; i++) - { - EntityManager.RemoveChunkComponent(entities[i]); - } - entities.Dispose(); - } - - var ecb = new EntityCommandBuffer(Allocator.TempJob); - - // Solo entities have render bounds. To these entities, we attach the chunk component. No other processing - // is needed. - { - var solos = m_SoloQuery.ToEntityArray(Allocator.Temp); - for (int i = 0; i < solos.Length; i++) - { - ecb.AddComponent(solos[i], new OcclusionTest(true)); - } - solos.Dispose(); - } - - // Parent entities don't have render bounds, but during DOTS baking they might have spawned additional - // "child" entities. This can happen when the occludees have sub-meshes, In which case each sub-mesh spawns - // its own entity. - // We need to iterate through all of these child entities and add chunk components to them, if they also - // have render bounds. - var parents = m_ParentQuery.ToEntityArray(Allocator.Temp); - for (int i = 0; i < parents.Length; i++) - { - var children = EntityManager.GetBuffer(parents[i]); - for (int j = 0; j < children.Length; j++) - { - var child = children[j].Value; - if (SystemAPI.HasComponent(child)) - { - ecb.AddComponent(child, new OcclusionTest(true)); - } - } - } - parents.Dispose(); - - ecb.Playback(EntityManager); - ecb.Dispose(); - - // Now we have added the occludee component to both, solo and parent entities. Now we add chunk components - // to all of them together. - EntityManager.AddComponent(m_NoChunkQuery, ComponentType.ChunkComponent()); - } - } -} - -#endif diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccludeeBaking.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccludeeBaking.cs.meta deleted file mode 100644 index 18ebedb..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccludeeBaking.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: efc4d90ec5d67ff4c8b2ce7cfae5ca59 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderBaking.cs b/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderBaking.cs deleted file mode 100644 index 655cbf4..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderBaking.cs +++ /dev/null @@ -1,153 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) && UNITY_EDITOR - -using Unity.Assertions; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Entities; -using Unity.Mathematics; -using UnityEngine; -using UnityEngine.Rendering; -using Hash128 = UnityEngine.Hash128; -using UnityEditor; - -namespace Unity.Rendering.Occlusion.Masked.Dots -{ - class OccluderBaker : Baker - { - public override void Bake(Occluder authoring) - { - if (IsActive() && authoring.mesh != null) - { - // This tells the baker API to create a dependency. If the referenced mesh changes, then baking will be - // re-triggered. - Mesh mesh = DependsOn(authoring.mesh); - - // Add the occluder mesh component to each submesh. This involves copying the submesh's triangle data to - // the new component. - if (mesh.subMeshCount > 1) - { - for (int i = 0; i < authoring.mesh.subMeshCount; i++) - { - Entity entity = CreateAdditionalEntity(TransformUsageFlags.Dynamic); - AddOccluderComponent(entity, authoring, i); - } - } - else - { - Entity entity = GetEntity(authoring, TransformUsageFlags.Dynamic); - AddOccluderComponent(entity, authoring, 0); - } - - } - } - - private unsafe void AddOccluderComponent(Entity entity, Occluder occluder, int submeshIndex) - { - var component = new OcclusionMesh(); - - // Get fast zero-copy access to raw mesh data - using var meshDataArray = MeshUtility.AcquireReadOnlyMeshData(occluder.mesh); - // Since we passed in only one mesh to `Mesh.AcquireReadOnlyMeshData()`, the array is guaranteed to have - // only one mesh data. - Assert.IsTrue(meshDataArray.Length == 1); - var meshData = meshDataArray[0]; - - - // Each occluder component references a blob asset containing the vertex data, and another one containing - // index data. If multiple occluders in the scene have the same meshes, then we want to share their index - // and vertex data. This is why we compute two hashes. - // - // When computing the hashes, we use the raw mesh data coupled with the sub-mesh index. When creating the - // actual blob asset, we do some extra calculations like applying the sub-mesh's index offset. We skip these - // calculations when computing the hash, in the interest of speed. - Hash128 indexHash = default; - Hash128 vertexHash = default; - { - // Hash the sub-mesh index only to the index data, since the vertex buffer is the same across - // sub-meshes - HashUtilities.ComputeHash128(ref submeshIndex, ref indexHash); - - // Hash the index buffer - var indexHashPtr = (Hash128*) UnsafeUtility.AddressOf(ref indexHash); - var indices = meshData.GetIndexData(); - HashUnsafeUtilities.ComputeHash128( - indices.GetUnsafeReadOnlyPtr(), - (ulong) indices.Length, - indexHashPtr - ); - // Hash the vertex buffer - var vertexHashPtr = (Hash128*) UnsafeUtility.AddressOf(ref vertexHash); - var vertices = meshData.GetVertexData(); - HashUnsafeUtilities.ComputeHash128( - vertices.GetUnsafeReadOnlyPtr(), - (ulong) vertices.Length, - vertexHashPtr - ); - } - - SubMeshDescriptor subMesh = meshData.GetSubMesh(submeshIndex); - - // Create/get a blob asset reference with the mesh's vertices, and assign it to our new component. Submeshes - // can arbitrarily index into the mesh's vertex buffer, so instead of slowly copying out only the vertices - // that a submesh needs, we quickly copy the whole vertex buffer. - { - if (!TryGetBlobAssetReference(vertexHash, out BlobAssetReference blobAssetRef)) - { - // ^ A blob asset with the given hash doesn't already exist, so we need to add one. - var vertices = new NativeArray(meshData.vertexCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory); - meshData.GetVertices(vertices); - - // Vector3 arrays can be directly reinterpreted as float3 arrays. If we weren't getting a pointer, - // we could do: `NativeArray verticesFloat3s = vertices.Reinterpret();` - // The pointer makes this unnecessary. - void* verticesPtr = vertices.GetUnsafeReadOnlyPtr(); - - blobAssetRef = BlobAssetReference.Create( - verticesPtr, - sizeof(float3) * vertices.Length - ); - - vertices.Dispose(); - AddBlobAssetWithCustomHash(ref blobAssetRef, vertexHash); - } - - component.vertexCount = meshData.vertexCount; - component.vertexData = blobAssetRef; - } - - // Create a blob asset reference with the submesh's indices, and assign it to our new component - { - if (!TryGetBlobAssetReference(indexHash, out BlobAssetReference blobAssetRef)) - { - // ^ A blob asset with the given hash doesn't already exist, so we need to add one. - var indices = new NativeArray(subMesh.indexCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory); - meshData.GetIndices(indices, submeshIndex, false); - - blobAssetRef = BlobAssetReference.Create( - indices.GetUnsafeReadOnlyPtr(), - sizeof(int) * indices.Length - ); - - indices.Dispose(); - AddBlobAssetWithCustomHash(ref blobAssetRef, indexHash); - } - - component.indexCount = subMesh.indexCount; - component.indexData = blobAssetRef; - } - - // Set the transform of the occluder - { - // Compute the full 4x4 matrix. The last row will always be (0, 0, 0, 1). We discard this row to reduce - // memory bandwidth and then reconstruct it later while transforming the occluders. - float4x4 mtx = float4x4.TRS(occluder.localPosition, occluder.localRotation, occluder.localScale); - component.localTransform = new float3x4(mtx.c0.xyz, mtx.c1.xyz, mtx.c2.xyz, mtx.c3.xyz); - } - - // Add the component to the entity - AddComponent(entity, component); - } - } -} - -#endif diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderBaking.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderBaking.cs.meta deleted file mode 100644 index 2018dfb..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderBaking.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9840ed3a95ec9f141995b60497c56002 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderMesh.cs b/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderMesh.cs deleted file mode 100644 index c582931..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderMesh.cs +++ /dev/null @@ -1,494 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Burst.Intrinsics; -using Unity.Collections; -using Unity.Entities; -using Unity.Mathematics; -using UnityEngine.Rendering; - -namespace Unity.Rendering.Occlusion.Masked.Dots -{ - struct OcclusionMesh : IComponentData - { - const float EPSILON = 1E-12f; - enum ClipPlanes - { - CLIP_PLANE_NONE = 0x00, - CLIP_PLANE_NEAR = 0x01, - CLIP_PLANE_LEFT = 0x02, - CLIP_PLANE_RIGHT = 0x04, - CLIP_PLANE_BOTTOM = 0x08, - CLIP_PLANE_TOP = 0x10, - CLIP_PLANE_SIDES = (CLIP_PLANE_LEFT | CLIP_PLANE_RIGHT | CLIP_PLANE_BOTTOM | CLIP_PLANE_TOP), - CLIP_PLANE_ALL = (CLIP_PLANE_LEFT | CLIP_PLANE_RIGHT | CLIP_PLANE_BOTTOM | CLIP_PLANE_TOP | CLIP_PLANE_NEAR) - }; - - public unsafe void Transform(float4x4 mvp, BatchCullingProjectionType projectionType, float nearClip, - v128* frustumPlanes, float halfWidth, float halfHeight, float pixelCenterX, float pixelCenterY, - float4* transformedVerts, - NativeArray clippedVerts, - NativeArray clippedTriExtents, - ClippedOccluder* clipped - ) - { - clipped->expandedVertexSize = 0; - clipped->screenMin = float.MaxValue; - clipped->screenMax = -float.MaxValue; - - float3* vin = (float3*)vertexData.GetUnsafePtr(); - float4* vout = transformedVerts; - - float clipW = nearClip; - int numVertsBehindNearPlane = 0; - - for (int i = 0; i < vertexCount; ++i, ++vin, ++vout) - { - *vout = math.mul(mvp, new float4(*vin, 1.0f)); - vout->y = -vout->y; - - float4 p = vout->xyzw; - - if (projectionType == BatchCullingProjectionType.Orthographic) - { - p.w = p.z; - } - else - { - if (p.w < clipW) - { - numVertsBehindNearPlane++; - continue; - } - p.xyz /= p.w; - } - p.y *= -1.0f; - clipped->screenMin = math.min(clipped->screenMin, p); - clipped->screenMax = math.max(clipped->screenMax, p); - } - // if all triangles are behind the near plane we can stop it now - if (numVertsBehindNearPlane == vertexCount) - return; - - // compute the expanded data, after transforming the vertices the triangle is check if it faces the camera - vout = transformedVerts; - int* indexPtr = (int*)indexData.GetUnsafePtr(); - - float3x3* vertices = stackalloc float3x3[6];// 1 + 5 planes triangles that can be generated - - const int singleBufferSize = 3 + 5;// 3 vertex + 5 planes = 8 maximum generated vertices per a triangle + 5 clipping planes - const int doubleBufferSize = singleBufferSize * 2; - float3* vertexClipBuffer = stackalloc float3[doubleBufferSize]; - - // Loop over three indices at a time, and clip the resulting triangle - for (int i = 0; i < indexCount; i+=3) - { - int numTriangles = 1; - // Fill out vertices[0][0..3] with the initial unclipped vertices - if (projectionType == BatchCullingProjectionType.Orthographic) - { - // Use the vertices' Z coordinate if the view is orthographic - vertices[0][0] = vout[indexPtr[i]].xyz; - vertices[0][1] = vout[indexPtr[i + 1]].xyz; - vertices[0][2] = vout[indexPtr[i + 2]].xyz; - } - else - { - // Use the vertices' W coordinate if the view is perspective - vertices[0][0] = vout[indexPtr[i]].xyw; - vertices[0][1] = vout[indexPtr[i + 1]].xyw; - vertices[0][2] = vout[indexPtr[i + 2]].xyw; - } - - // buffer group have guardBand that adds extra padding to avoid clipping triangles on the sides - // Test clipping simply checks that the projected triangles are inside the frustum exploiting - // the checks against the w or z depending if it's orthographic or perspective projection - ClippingTestResult clippingTestResult = TestClipping(vertices[0], nearClip, projectionType == BatchCullingProjectionType.Orthographic); - // If the whole triangle is outside the clipping bounds, then it is discarded entirely. We just skip to - // the next triangle. - if (clippingTestResult == ClippingTestResult.Outside) continue; - - // If the triangle is partially inside and partially outside the clipping bounds, then we turn it into a - // polygon entirely contained within the clipping bounds. - if (clippingTestResult == ClippingTestResult.Clipping) - { - numTriangles = 0; - // Load the initial vertices into the clip buffer - vertexClipBuffer[0] = vertices[0][0]; - vertexClipBuffer[1] = vertices[0][1]; - vertexClipBuffer[2] = vertices[0][2]; - - int nClippedVerts = 3; - // The vertex clip buffer is a double buffer, which means that we use half of the buffer for reading - // and the other half for writing. The `bufferSwap` variable controls which half is the read and - // which half is written. After each plane is processed, we toggle it between 0 and 1, i.e. we flip - // the swapchain. - int bufferSwap = 0; - for (int n = 0; n < 5; ++n)//clipping 5 planes - { - // Sutherland-Hodgman polygon clipping algorithm https://mikro.naprvyraz.sk/docs/Coding/2/FRUSTUM.TXT - // swapping buffers from input output, the algorithm works by clipping every plane once at a time - float3* outVtx = vertexClipBuffer + ((bufferSwap ^ 1) * singleBufferSize); - float3* inVtx = vertexClipBuffer + (bufferSwap * singleBufferSize); - float4 plane = new float4(frustumPlanes[n].Float0, frustumPlanes[n].Float1, frustumPlanes[n].Float2, frustumPlanes[n].Float3); - nClippedVerts = ClipPolygon(outVtx, inVtx, plane, nClippedVerts); - // Toggle between 0 and 1 - bufferSwap ^= 1; - } - // nClippedVerts can be lower than 3 and that's why numTriangles is 0 as default, - // from there for every extra vertex a triangle it's added as a fan - /* - * x - * |\ - * | \ - * | \ - * | \ - * x-_ \ - * `-_\ <------if the clipping plane is here it will add 2 vertex producing - * `x - * - * x - * |\ - * | \ - * | \ - * | \ <------X are the new added vertices and the final result will be - * x-_ \ - * `X_X - * `x - * - * x - * |\ - * |.\ - * | :\ - * | \ \ <------X are the new added vertices and the final result will be the extra edges cutting from the the first vertex to the new ones and between the added ones - * x-_\ \ - * `X-X - * - */ - if (nClippedVerts >= 3) - { - // Copy over the clipped vertices to the result array - vertices[0][0] = vertexClipBuffer[bufferSwap * singleBufferSize + 0]; - vertices[0][1] = vertexClipBuffer[bufferSwap * singleBufferSize + 1]; - vertices[0][2] = vertexClipBuffer[bufferSwap * singleBufferSize + 2]; - - numTriangles++; - - for (int n = 2; n < nClippedVerts - 1; n++) - { - // ^ If we have more than 3 vertices after clipping, create a triangle-fan, with the 0th - // vertex shared across all triangles of the fan. - vertices[numTriangles][0] = vertexClipBuffer[bufferSwap * singleBufferSize]; - vertices[numTriangles][1] = vertexClipBuffer[bufferSwap * singleBufferSize + n]; - vertices[numTriangles][2] = vertexClipBuffer[bufferSwap * singleBufferSize + n + 1]; - numTriangles++; - } - } - - } - - for(int n = 0; n < numTriangles; ++n) - { - int2x3 intHomogeneousVertices = int2x3.zero; - float2x3 homogeneousVertices = float2x3.zero; - float2 halfRes = new float2(halfWidth, halfHeight); - float2 pixelCenter = new float2(pixelCenterX, pixelCenterY); - if (projectionType == BatchCullingProjectionType.Orthographic) - { - homogeneousVertices[0] = (vertices[0][0].xy * halfRes) + pixelCenter; - homogeneousVertices[1] = (vertices[0][1].xy * halfRes) + pixelCenter; - homogeneousVertices[2] = (vertices[0][2].xy * halfRes) + pixelCenter; - - homogeneousVertices[0] *= (float)(1 << BufferGroup.FpBits); - homogeneousVertices[1] *= (float)(1 << BufferGroup.FpBits); - homogeneousVertices[2] *= (float)(1 << BufferGroup.FpBits); - - intHomogeneousVertices.c0.x = (int)math.round(homogeneousVertices.c0.x); - intHomogeneousVertices.c1.x = (int)math.round(homogeneousVertices.c1.x); - intHomogeneousVertices.c2.x = (int)math.round(homogeneousVertices.c2.x); - intHomogeneousVertices.c0.y = (int)math.round(homogeneousVertices.c0.y); - intHomogeneousVertices.c1.y = (int)math.round(homogeneousVertices.c1.y); - intHomogeneousVertices.c2.y = (int)math.round(homogeneousVertices.c2.y); - - homogeneousVertices = new float2x3(intHomogeneousVertices) / (float)(1 << BufferGroup.FpBits); - } - else - { - homogeneousVertices[0] = (vertices[0][0].xy * halfRes) / vertices[0][0].z + pixelCenter; - homogeneousVertices[1] = (vertices[0][1].xy * halfRes) / vertices[0][1].z + pixelCenter; - homogeneousVertices[2] = (vertices[0][2].xy * halfRes) / vertices[0][2].z + pixelCenter; - - homogeneousVertices[0] *= (float)(1 << BufferGroup.FpBits); - homogeneousVertices[1] *= (float)(1 << BufferGroup.FpBits); - homogeneousVertices[2] *= (float)(1 << BufferGroup.FpBits); - - intHomogeneousVertices.c0.x = (int)math.round(homogeneousVertices.c0.x); - intHomogeneousVertices.c1.x = (int)math.round(homogeneousVertices.c1.x); - intHomogeneousVertices.c2.x = (int)math.round(homogeneousVertices.c2.x); - intHomogeneousVertices.c0.y = (int)math.round(homogeneousVertices.c0.y); - intHomogeneousVertices.c1.y = (int)math.round(homogeneousVertices.c1.y); - intHomogeneousVertices.c2.y = (int)math.round(homogeneousVertices.c2.y); - - homogeneousVertices = new float2x3(intHomogeneousVertices) / (float)(1 << BufferGroup.FpBits); - } - - // Checkin determinant > 0 more details in: - // Triangle Scan Conversion using 2D Homogeneous Coordinates - // https://www.cs.cmu.edu/afs/cs/academic/class/15869-f11/www/readings/olano97_homogeneous.pdf - // Section 5.2 - // In 3D, a matrix determinant gives twice the signed volume of a tetrahedron.In - // eye space, this is the tetrahedron with the eye at the apex and the - // triangle to be rendered as the base.If all of the 2D w coordinates - // are 1, the determinant is also exactly twice the signed screenspace aren of the triangle. - // If the determinant is zero, either the triangle is degenerate or the view is edge - on. - // Furthermore, for vertices defined by the right-hand rule, the determinant is positive if the triangle - // is front-facing and negative if the triangle is back-facing. - - float area = (homogeneousVertices.c1.x - homogeneousVertices.c2.x) * (homogeneousVertices.c0.y - homogeneousVertices.c2.y) - - (homogeneousVertices.c2.x - homogeneousVertices.c0.x) * (homogeneousVertices.c2.y - homogeneousVertices.c1.y); - - if (area > EPSILON) - { - if (projectionType == BatchCullingProjectionType.Orthographic) - { - homogeneousVertices[0] = vertices[n][0].xy; - homogeneousVertices[1] = vertices[n][1].xy; - homogeneousVertices[2] = vertices[n][2].xy; - } - else - { - homogeneousVertices[0] = vertices[n][0].xy / vertices[n][0].z; - homogeneousVertices[1] = vertices[n][1].xy / vertices[n][1].z; - homogeneousVertices[2] = vertices[n][2].xy / vertices[n][2].z; - } - homogeneousVertices[0].y *= -1.0f; - homogeneousVertices[1].y *= -1.0f; - homogeneousVertices[2].y *= -1.0f; - - clippedTriExtents[clipped->sourceIndexOffset * 2 + clipped->expandedVertexSize / 3] = new float4( - math.min(math.min(homogeneousVertices[0], homogeneousVertices[1]), homogeneousVertices[2]), - math.max(math.max(homogeneousVertices[0], homogeneousVertices[1]), homogeneousVertices[2]) - ); - // Copy final vertex data into output arrays - for (int m = 0; m < 3; ++m) - { - clippedVerts[clipped->sourceIndexOffset * 6 + clipped->expandedVertexSize] = vertices[n][m]; - clipped->expandedVertexSize++; - } - } - } - } - // If fully visible we can skip computing the screen aabb again but with the extra clipping logic - if (numVertsBehindNearPlane == 0 && !(clipped->screenMin.x > 1.0f || clipped->screenMin.y > 1.0f || clipped->screenMax.x < -1.0f || clipped->screenMax.y < -1.0f)) - return; - - if (numVertsBehindNearPlane == 0 || numVertsBehindNearPlane == vertexCount)// if triangles does not cross near plane we can skip the slow path too - return; - - var edges = stackalloc int2[] - { - new int2(0,1), new int2(1,3), new int2(3,2), new int2(2,0), - new int2(4,6), new int2(6,7), new int2(7,5), new int2(5,4), - new int2(4,0), new int2(2,6), new int2(1,5), new int2(7,3) - }; - - float3 minAABB, maxAABB; - maxAABB.x = maxAABB.y = maxAABB.z = -float.MaxValue; - minAABB.x = minAABB.y = minAABB.z = float.MaxValue; - vin = (float3*)vertexData.GetUnsafePtr(); - - for (int i = 0; i < vertexCount; ++i, ++vin) - { - float3 p = vin->xyz; - - minAABB = math.min(minAABB, p); - maxAABB = math.max(maxAABB, p); - } - - AABB aabb; - aabb.Center = (maxAABB + minAABB) * 0.5f; - aabb.Extents = (maxAABB - minAABB) * 0.5f; - - var verts = stackalloc float4[16]; - - float4x2 u = new float4x2(mvp.c0 * aabb.Min.x, mvp.c0 * aabb.Max.x); - float4x2 v = new float4x2(mvp.c1 * aabb.Min.y, mvp.c1 * aabb.Max.y); - float4x2 w = new float4x2(mvp.c2 * aabb.Min.z, mvp.c2 * aabb.Max.z); - - for (int corner = 0; corner < 8; corner++) - { - float4 p = u[corner & 1] + v[(corner & 2) >> 1] + w[(corner & 4) >> 2] + mvp.c3; - p.y = -p.y; - verts[corner] = p; - } - - int internalVertexCount = 8; - for (int i = 0; i < 12; i++) - { - var e = edges[i]; - var a = verts[e.x]; - var b = verts[e.y]; - - if ((a.w < clipW) != (b.w < clipW)) - { - var p = math.lerp(a, b, (clipW - a.w) / (b.w - a.w)); - verts[internalVertexCount++] = p; - } - } - - for (int i = 0; i < internalVertexCount; i++) - { - float4 p = verts[i]; - - if (projectionType == BatchCullingProjectionType.Orthographic) - { - p.w = p.z; - } - else - { - if (p.w < EPSILON) - continue; - - p.xyz /= p.w; - } - p.y *= -1.0f; - clipped->screenMin = math.min(clipped->screenMin, p); - clipped->screenMax = math.max(clipped->screenMax, p); - } - } - - enum ClippingTestResult - { - Inside = 0, - Clipping, - Outside - }; - unsafe ClippingTestResult TestClipping(float3x3 vertices, float NearClip, bool isOrtho) - { - - ClippingTestResult* straddleMask = stackalloc ClippingTestResult[5]; - straddleMask[0] = TestClipPlane(ClipPlanes.CLIP_PLANE_NEAR, vertices, NearClip, isOrtho); - straddleMask[1] = TestClipPlane(ClipPlanes.CLIP_PLANE_LEFT, vertices, NearClip, isOrtho); - straddleMask[2] = TestClipPlane(ClipPlanes.CLIP_PLANE_RIGHT, vertices, NearClip, isOrtho); - straddleMask[3] = TestClipPlane(ClipPlanes.CLIP_PLANE_BOTTOM, vertices, NearClip, isOrtho); - straddleMask[4] = TestClipPlane(ClipPlanes.CLIP_PLANE_TOP, vertices, NearClip, isOrtho); - - if( straddleMask[0] == ClippingTestResult.Inside && - straddleMask[1] == ClippingTestResult.Inside && - straddleMask[2] == ClippingTestResult.Inside && - straddleMask[3] == ClippingTestResult.Inside && - straddleMask[4] == ClippingTestResult.Inside) - { - return ClippingTestResult.Inside; - } - if (straddleMask[0] == ClippingTestResult.Outside || - straddleMask[1] == ClippingTestResult.Outside || - straddleMask[2] == ClippingTestResult.Outside || - straddleMask[3] == ClippingTestResult.Outside || - straddleMask[4] == ClippingTestResult.Outside) - { - return ClippingTestResult.Outside; - } - return ClippingTestResult.Clipping; - } - - // This function checks whether and how a triangle intersects a clipping plane. - // The clipping plane divides 3D space into two parts - one being inside the frustum and one being outside. This - // function returns whether the input triangle is fully on the inside, fully on the ouside, or a bit on both - // sides, i.e. clipping the plane. - unsafe ClippingTestResult TestClipPlane(ClipPlanes clipPlane, float3x3 vertices, float NearClip, bool isOrtho) - { - // Evaluate all 3 vertices against the frustum plane - float* planeDp = stackalloc float[3]; - - if (!isOrtho) - { - for (int i = 0; i < 3; ++i) - { - switch (clipPlane) - { - case ClipPlanes.CLIP_PLANE_LEFT: planeDp[i] = vertices[i].z + vertices[i].x; break; - case ClipPlanes.CLIP_PLANE_RIGHT: planeDp[i] = vertices[i].z - vertices[i].x; break; - case ClipPlanes.CLIP_PLANE_BOTTOM: planeDp[i] = vertices[i].z + vertices[i].y; break; - case ClipPlanes.CLIP_PLANE_TOP: planeDp[i] = vertices[i].z - vertices[i].y; break; - case ClipPlanes.CLIP_PLANE_NEAR: planeDp[i] = vertices[i].z - NearClip; break; - } - } - } - else - { - for (int i = 0; i < 3; ++i) - { - switch (clipPlane) - { - case ClipPlanes.CLIP_PLANE_LEFT: planeDp[i] = 1.0f + vertices[i].x; break; - case ClipPlanes.CLIP_PLANE_RIGHT: planeDp[i] = 1.0f - vertices[i].x; break; - case ClipPlanes.CLIP_PLANE_BOTTOM: planeDp[i] = 1.0f + vertices[i].y; break; - case ClipPlanes.CLIP_PLANE_TOP: planeDp[i] = 1.0f - vertices[i].y; break; - case ClipPlanes.CLIP_PLANE_NEAR: planeDp[i] = 1.0f + NearClip; break; - } - } - } - if (planeDp[0] > EPSILON && planeDp[1] > EPSILON && planeDp[2] > EPSILON) - { - return ClippingTestResult.Inside; - } - - if (planeDp[0] <= EPSILON && planeDp[1] <= EPSILON && planeDp[2] <= EPSILON) - { - return ClippingTestResult.Outside; - } - return ClippingTestResult.Clipping; - } - - unsafe int ClipPolygon(float3* outVtx, float3* inVtx, float4 plane, int n) - { - float3 p0 = inVtx[n - 1]; - float dist0 = math.dot(p0, plane.xyz) + plane.w; - - // Loop over all polygon edges and compute intersection with clip plane (if any) - int nout = 0; - - for (int k = 0; k < n; k++) - { - float3 p1 = inVtx[k]; - float dist1 = math.dot(p1, plane.xyz) + plane.w; - - if (dist0 > EPSILON) - { - outVtx[nout++] = p0; - } - - // Edge intersects the clip plane if dist0 and dist1 have opposing signs - if (math.sign(dist0) != math.sign(dist1)) - { - // Always clip from the positive side to avoid T-junctions - if (dist0 > EPSILON) - { - float t = dist0 / (dist0 - dist1); - outVtx[nout++] = t * (p1 - p0) + p0; - } - else - { - float t = dist1 / (dist1 - dist0); - outVtx[nout++] = t * (p0 - p1) + p1; - } - } - - dist0 = dist1; - p0 = p1; - } - - return nout; - } - - public int vertexCount; - public int indexCount; - - public BlobAssetReference vertexData; - public BlobAssetReference indexData; - - public float3x4 localTransform; - } -} - -#endif diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderMesh.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderMesh.cs.meta deleted file mode 100644 index f45c84d..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OccluderMesh.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2f985747e07da954b9a4050ef16297e1 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OcclusionTest.cs b/Unity.Entities.Graphics/Occlusion/Masked/Dots/OcclusionTest.cs deleted file mode 100644 index 77eda00..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OcclusionTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Entities; -using Unity.Mathematics; - -namespace Unity.Rendering.Occlusion.Masked.Dots -{ - struct OcclusionTest : IComponentData - { - public OcclusionTest(bool enabled) - { - this.enabled = enabled; - screenMin = float.MaxValue; - screenMax = -float.MaxValue; - } - - // this flag is for toggling occlusion testing without having to add a component at runtime. - public bool enabled; - public float4 screenMin, screenMax; - } - - struct ChunkOcclusionTest : IComponentData - { - public float4 screenMin, screenMax; - } -} -#endif diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OcclusionTest.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Dots/OcclusionTest.cs.meta deleted file mode 100644 index b45c6e7..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Dots/OcclusionTest.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e45b6693f020abc498dfd0ed6757d71a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/IntrinsicUtils.cs b/Unity.Entities.Graphics/Occlusion/Masked/IntrinsicUtils.cs deleted file mode 100644 index 9a145c9..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/IntrinsicUtils.cs +++ /dev/null @@ -1,221 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using System.Runtime.CompilerServices; -using Unity.Burst.Intrinsics; -using Unity.Mathematics; - - -namespace Unity.Rendering.Occlusion.Masked -{ - static class IntrinsicUtils - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int _vmovemask_f32(v128 a) - { - if (Arm.Neon.IsNeonSupported) - { - //https://github.com/jratcliff63367/sse2neon/blob/master/SSE2NEON.h#L518 - // TODO: this version should work but need to revisit the callsites and see if we can get rid of it altogether - v128 movemask = new v128(1u, 2u, 4u, 8u); - v128 highbit = new v128(0x80000000u); - - v128 t0 = Arm.Neon.vtstq_u32(a, highbit); - v128 t1 = Arm.Neon.vandq_u32(t0, movemask); - return Arm.Neon.vaddvq_s32(t1); - } - return 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static v128 _vtranspose_s8(v128 a) - { - if (Arm.Neon.IsNeonSupported) - { - v128 v0 = Arm.Neon.vcopyq_laneq_u32(new v128(0), 0, a, 0); - v128 v1 = Arm.Neon.vcopyq_laneq_u32(new v128(0), 0, a, 1); - v128 v2 = Arm.Neon.vcopyq_laneq_u32(new v128(0), 0, a, 2); - v128 v3 = Arm.Neon.vcopyq_laneq_u32(new v128(0), 0, a, 3); - - v128 v4 = Arm.Neon.vzip1q_s8(v0, v1); - v128 v5 = Arm.Neon.vzip1q_s8(v2, v3); - return Arm.Neon.vzip1q_u16(v4, v5); - } - return new v128(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static v128 _vsllv_ones(v128 ishift) - { - if (Arm.Neon.IsNeonSupported) - { - v128 shift = Arm.Neon.vminq_s32(ishift, new v128(32)); - return Arm.Neon.vshlq_s32(new v128(~0), shift); - } - return new v128(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static v128 _vblendq_f32(v128 mask, v128 a, v128 b) - { - if (Arm.Neon.IsNeonSupported) - { - // set 32-bit element according to the sign bit - // to emulate intel blendv behavior - v128 swapMask = Arm.Neon.vcgezq_s32(mask); - return Arm.Neon.vbslq_s8(swapMask, a, b); - } - return new v128(); - } - - // read access - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int getIntLane(v128 vector, uint laneIdx) - { - //Debug.Assert(laneIdx >= 0 && laneIdx < 4); - - // eat the modulo cost to not let it overflow - switch (laneIdx % 4) - { - default: // DS: incorrect, but works with modulo and silences compiler (CS0161) - case 0: { return vector.SInt0; } - case 1: { return vector.SInt1; } - case 2: { return vector.SInt2; } - case 3: { return vector.SInt3; } - } - } - - // read access - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static byte getByteLane(v128 vector, uint laneIdx) - { - //Debug.Assert(laneIdx >= 0 && laneIdx < 4); - - // eat the modulo cost to not let it overflow - switch (laneIdx % 16) - { - default: // DS: incorrect, but works with modulo and silences compiler (CS0161) - case 0: { return vector.Byte0; } - case 1: { return vector.Byte1; } - case 2: { return vector.Byte2; } - case 3: { return vector.Byte3; } - case 4: { return vector.Byte4; } - case 5: { return vector.Byte5; } - case 6: { return vector.Byte6; } - case 7: { return vector.Byte7; } - case 8: { return vector.Byte8; } - case 9: { return vector.Byte9; } - case 10: { return vector.Byte10; } - case 11: { return vector.Byte11; } - case 12: { return vector.Byte12; } - case 13: { return vector.Byte13; } - case 14: { return vector.Byte14; } - case 15: { return vector.Byte15; } - } - } - - // used for "write" access (returns copy, requires assignment afterwards) - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static v128 getCopyWithIntLane(v128 vector, uint laneIdx, int laneVal) - { - //Debug.Assert(laneIdx >= 0 && laneIdx < 4); - - // eat the modulo cost to not let it overflow - switch (laneIdx % 4) - { - default: // DS: incorrect fallthrough, but works with modulo and silences compiler (CS0161) - case 0: { vector.SInt0 = laneVal; break; } - case 1: { vector.SInt1 = laneVal; break; } - case 2: { vector.SInt2 = laneVal; break; } - case 3: { vector.SInt3 = laneVal; break; } - } - - return vector; - } - - // read access - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static float getFloatLane(v128 vector, uint laneIdx) - { - //Debug.Assert(laneIdx >= 0 && laneIdx < 4); - - // eat the modulo cost to not let it overflow - switch (laneIdx % 4) - { - default: // DS: incorrect fallthrough, but works with modulo and silences compiler (CS0161) - case 0: { return vector.Float0; } - case 1: { return vector.Float1; } - case 2: { return vector.Float2; } - case 3: { return vector.Float3; } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static v128 _mmw_fmadd_ps(v128 a, v128 b, v128 c) - { - if (X86.Fma.IsFmaSupported) - return X86.Fma.fmadd_ps(a, b, c); - else if (X86.Sse.IsSseSupported) - return X86.Sse.add_ps(X86.Sse.mul_ps(a, b), c); - return new v128(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static v128 _mmw_fmsub_ps(v128 a, v128 b, v128 c) - { - if (X86.Fma.IsFmaSupported) - return X86.Fma.fmsub_ps(a, b, c); - else if (X86.Sse.IsSseSupported) - return X86.Sse.sub_ps(X86.Sse.mul_ps(a, b), c); - return new v128(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static v128 _mmw_transpose_epi8(v128 a) - { - if (X86.Ssse3.IsSsse3Supported) - { - v128 shuff = X86.Sse2.setr_epi8(0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF); - return X86.Ssse3.shuffle_epi8(a, shuff); - } - return new v128(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static v128 _mmw_sllv_ones(v128 ishift) - { - if (X86.Sse4_1.IsSse41Supported) - { - v128 shift = X86.Sse4_1.min_epi32(ishift, X86.Sse2.set1_epi32(32)); - - // Uses lookup tables and _mm_shuffle_epi8 to perform _mm_sllv_epi32(~0, shift) - v128 byteShiftLUT; - unchecked - { - byteShiftLUT = X86.Sse2.setr_epi8((sbyte)0xFF, (sbyte)0xFE, (sbyte)0xFC, (sbyte)0xF8, (sbyte)0xF0, (sbyte)0xE0, (sbyte)0xC0, (sbyte)0x80, 0, 0, 0, 0, 0, 0, 0, 0); - } - v128 byteShiftOffset = X86.Sse2.setr_epi8(0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24); - v128 byteShiftShuffle = X86.Sse2.setr_epi8(0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x8, 0x8, 0x8, 0x8, 0xC, 0xC, 0xC, 0xC); - - v128 byteShift = X86.Ssse3.shuffle_epi8(shift, byteShiftShuffle); - - // DS: TODO: change once we get Burst fix for X86.Sse2.set1_epi8() - const sbyte val = 8; - byteShift = X86.Sse4_1.min_epi8(X86.Sse2.subs_epu8(byteShift, byteShiftOffset), new v128(val) /*X86.Sse2.set1_epi8(8)*/); - - v128 retMask = X86.Ssse3.shuffle_epi8(byteShiftLUT, byteShift); - - return retMask; - } - return new v128(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ulong find_clear_lsb(ref uint mask) - { - ulong idx = (ulong)math.tzcnt(mask); - mask &= mask - 1; - return idx; - } - } -} -#endif diff --git a/Unity.Entities.Graphics/Occlusion/Masked/IntrinsicUtils.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/IntrinsicUtils.cs.meta deleted file mode 100644 index 204f631..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/IntrinsicUtils.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8265ed83b7038a74591a6f69d8d8f49c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/MeshTransformJob.cs b/Unity.Entities.Graphics/Occlusion/Masked/MeshTransformJob.cs deleted file mode 100644 index 72a9048..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/MeshTransformJob.cs +++ /dev/null @@ -1,61 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Burst; -using Unity.Burst.Intrinsics; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using Unity.Mathematics; -using Unity.Rendering.Occlusion.Masked.Dots; -using Unity.Transforms; -using UnityEngine.Rendering; - -namespace Unity.Rendering.Occlusion.Masked -{ - [BurstCompile(DisableSafetyChecks = true)] - unsafe struct MeshTransformJob : IJobFor - { - [ReadOnly] public float4x4 ViewProjection; - [ReadOnly] public BatchCullingProjectionType ProjectionType; - [ReadOnly] public float NearClip; - [ReadOnly, NativeDisableUnsafePtrRestriction] public v128* FrustumPlanes; - [ReadOnly] public v128 HalfWidth; - [ReadOnly] public v128 HalfHeight; - [ReadOnly] public v128 PixelCenterX; - [ReadOnly] public v128 PixelCenterY; - [ReadOnly] public NativeArray LocalToWorlds; - [ReadOnly] public NativeArray Meshes; - - public NativeArray TransformedVerts; - public int TransformedVertsStride; - public NativeArray ClippedVerts; - public NativeArray ClippedTriExtents; - public NativeArray ClippedOccluders; - - [NativeSetThreadIndex] - internal int m_ThreadIndex; - - public void Execute(int i) - { - var mesh = Meshes[i]; - // We discarded the last row of the occluder matrix to save memory bandwidth because it is always (0, 0, 0,1). - // However, to perform the actual math, we still need a 4x4 matrix. So we reintroduce the row here. - float4x4 occluderMtx = new float4x4( - new float4(mesh.localTransform.c0, 0f), - new float4(mesh.localTransform.c1, 0f), - new float4(mesh.localTransform.c2, 0f), - new float4(mesh.localTransform.c3, 1f) - ); - float4x4 mvp = math.mul(ViewProjection, math.mul(LocalToWorlds[i].Value, occluderMtx)); - - var clipped = (ClippedOccluder*)ClippedOccluders.GetUnsafePtr(); - - float4* transformedVertsPtr = &((float4*)TransformedVerts.GetUnsafePtr())[m_ThreadIndex * TransformedVertsStride]; - - mesh.Transform(mvp, ProjectionType, NearClip, FrustumPlanes, HalfWidth.Float0, HalfHeight.Float0, - PixelCenterX.Float0, PixelCenterY.Float0, transformedVertsPtr, ClippedVerts, ClippedTriExtents, &clipped[i]); - } - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/MeshTransformJob.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/MeshTransformJob.cs.meta deleted file mode 100644 index fe906d2..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/MeshTransformJob.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3359e8ac2abeeab4e89cef513b264a3c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/RasterizeJob.cs b/Unity.Entities.Graphics/Occlusion/Masked/RasterizeJob.cs deleted file mode 100644 index 54a137b..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/RasterizeJob.cs +++ /dev/null @@ -1,1812 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using System.Runtime.CompilerServices; -using Unity.Burst; -using Unity.Burst.Intrinsics; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using Unity.Mathematics; -using UnityEngine; -using Unity.Rendering.Occlusion.Masked.Dots; -using UnityEngine.Rendering; - -namespace Unity.Rendering.Occlusion.Masked -{ - [BurstCompile(DisableSafetyChecks = true)] - unsafe struct RasterizeJob : IJobFor - { - [ReadOnly] public NativeArray ClippedOccluders; - [ReadOnly] public NativeArray ClippedVerts; - [ReadOnly] public NativeArray ClippedTriExtents; - [ReadOnly] public BatchCullingProjectionType ProjectionType; - [ReadOnly] public int NumBuffers; - [ReadOnly] public v128 HalfWidth; - [ReadOnly] public v128 HalfHeight; - [ReadOnly] public v128 PixelCenterX; - [ReadOnly] public v128 PixelCenterY; - [ReadOnly] public v128 PixelCenter; - [ReadOnly] public v128 HalfSize; - [ReadOnly] public v128 ScreenSize; - [ReadOnly] public int BinSize; - [ReadOnly] public int NumPixelsX; - [ReadOnly] public int NumPixelsY; - [ReadOnly] public int NumTilesX; - [ReadOnly] public int NumTilesY; - [ReadOnly] public float NearClip; - [ReadOnly, NativeDisableUnsafePtrRestriction] public v128* FrustumPlanes; - [ReadOnly] public ScissorRect FullScreenScissor; - - [NativeSetThreadIndex] private int WorkerIndex; - - // A bin is a screen area formed by X * Y tiles, a tile is the minimum pixels that we - // process in the system - [ReadOnly] public int TilesPerBinX; - [ReadOnly] public int TilesPerBinY; - // A buffer group contains a bunch of contiguous tile-buffers. This pointer points to the base of the one we're - // rendering to. - [NativeDisableUnsafePtrRestriction] public Tile* TilesBasePtr; - [NativeDisableUnsafePtrRestriction] public float* BinTriangleXBasePtr; - [NativeDisableUnsafePtrRestriction] public float* BinTriangleYBasePtr; - [NativeDisableUnsafePtrRestriction] public float* BinTriangleWBasePtr; - - const int MAX_CLIPPED = 32; - const int SIMD_LANES = 4; - const int SIMD_ALL_LANES_MASK = (1 << SIMD_LANES) - 1; - const int BIG_TRIANGLE = 3; - - #region SSE - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void TraverseScanlineSSE(Tile* tiles, int numRight, int numLeft, int leftOffset, int rightOffset, int tileIdx, int rightEvent, int leftEvent, v128* events, v128 zTriMin, v128 zTriMax, v128 iz0, float zx) - { - if (X86.Sse4_1.IsSse41Supported) - { - v128* right = stackalloc v128[numRight]; - v128* left = stackalloc v128[numLeft]; - - // Floor edge events to integer pixel coordinates (shift out fixed point bits) - v128 eventOffset = new v128(leftOffset << BufferGroup.TileWidthShift); - v128 emptyBitMask = new v128(0); - v128 fullBitMask = new v128(~0); - v128 simdTileWidth = X86.Sse2.set1_epi32(BufferGroup.TileWidth); - - for (int i = 0; i < numRight; ++i) - { - right[i] = X86.Sse4_1.max_epi32(X86.Sse2.sub_epi32(X86.Sse2.srai_epi32(events[rightEvent + i], BufferGroup.FpBits), eventOffset), emptyBitMask); - } - - for (int i = 0; i < numLeft; ++i) - { - left[i] = X86.Sse4_1.max_epi32(X86.Sse2.sub_epi32(X86.Sse2.srai_epi32(events[leftEvent - i], BufferGroup.FpBits), eventOffset), emptyBitMask); - } - - v128 z0 = X86.Sse.add_ps(iz0, X86.Sse.set1_ps(zx * leftOffset)); - int tileIdxEnd = tileIdx + rightOffset; - tileIdx += leftOffset; - - for (; ; ) - { - // Compute zMin for the overlapped layers - v128 mask = tiles[tileIdx].mask; - v128 zMin0 = X86.Sse4_1.blendv_ps(tiles[tileIdx].zMin0, tiles[tileIdx].zMin1, X86.Sse2.cmpeq_epi32(mask, fullBitMask)); - v128 zMin1 = X86.Sse4_1.blendv_ps(tiles[tileIdx].zMin1, tiles[tileIdx].zMin0, X86.Sse2.cmpeq_epi32(mask, emptyBitMask)); - v128 zMinBuf = X86.Sse.min_ps(zMin0, zMin1); - v128 dist0 = X86.Sse.sub_ps(zTriMax, zMinBuf); - - if (X86.Sse.movemask_ps(dist0) != SIMD_ALL_LANES_MASK) - { - // Compute coverage mask for entire 32xN using shift operations - v128 accumulatedMask = IntrinsicUtils._mmw_sllv_ones(left[0]); - - for (int i = 1; i < numLeft; ++i) - { - accumulatedMask = X86.Sse2.and_si128(accumulatedMask, IntrinsicUtils._mmw_sllv_ones(left[i])); - } - - for (int i = 0; i < numRight; ++i) - { - accumulatedMask = X86.Sse2.andnot_si128(IntrinsicUtils._mmw_sllv_ones(right[i]), accumulatedMask); - } - - // Compute interpolated min for each 8x4 subtile and update the masked hierarchical z buffer entry - v128 zSubTileMin = X86.Sse.max_ps(z0, zTriMin); - UpdateTileAccurateSSE(tiles, tileIdx, IntrinsicUtils._mmw_transpose_epi8(accumulatedMask), zSubTileMin); - } - - // Update buffer address, interpolate z and edge events - tileIdx++; - - if (tileIdx >= tileIdxEnd) - { - break; - } - - z0 = X86.Sse.add_ps(z0, X86.Sse.set1_ps(zx)); - - for (int i = 0; i < numRight; ++i) - { - right[i] = X86.Sse2.subs_epu16(right[i], simdTileWidth); // Trick, use sub saturated to avoid checking against < 0 for shift (values should fit in 16 bits) - } - - for (int i = 0; i < numLeft; ++i) - { - left[i] = X86.Sse2.subs_epu16(left[i], simdTileWidth); - } - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void UpdateTileAccurateSSE(Tile* tiles, int tileIdx, v128 coverage, v128 zTriv) - { - if (X86.Sse4_1.IsSse41Supported) - { - v128 zMin0 = tiles[tileIdx].zMin0; - v128 zMin1 = tiles[tileIdx].zMin1; - v128 mask = tiles[tileIdx].mask; - - // Swizzle coverage mask to 8x4 subtiles - v128 rastMask = coverage; - - // Perform individual depth tests with layer 0 & 1 and mask out all failing pixels - v128 sdist0 = X86.Sse.sub_ps(zMin0, zTriv); - v128 sdist1 = X86.Sse.sub_ps(zMin1, zTriv); - v128 sign0 = X86.Sse2.srai_epi32(sdist0, 31); - v128 sign1 = X86.Sse2.srai_epi32(sdist1, 31); - v128 triMask = X86.Sse2.and_si128(rastMask, X86.Sse2.or_si128(X86.Sse2.andnot_si128(mask, sign0), X86.Sse2.and_si128(mask, sign1))); - - // Early out if no pixels survived the depth test (this test is more accurate than - // the early culling test in TraverseScanline()) - v128 t0 = X86.Sse2.cmpeq_epi32(triMask, X86.Sse2.setzero_si128()); - v128 t0inv = /*not_epi32*/ X86.Sse2.xor_si128(t0, X86.Sse2.set1_epi32(~0)); - - if (X86.Sse4_1.testz_si128(t0inv, t0inv) != 0) - { - return; - } - -#if MOC_ENABLE_STATS - STATS_ADD(ref mStats.mOccluders.mNumTilesUpdated, 1); -#endif - - v128 zTri = X86.Sse4_1.blendv_ps(zTriv, zMin0, t0); - - // Test if incoming triangle completely overwrites layer 0 or 1 - v128 layerMask0 = X86.Sse2.andnot_si128(triMask, /*not_epi32*/ X86.Sse2.xor_si128(mask, X86.Sse2.set1_epi32(~0))); - v128 layerMask1 = X86.Sse2.andnot_si128(triMask, mask); - v128 lm0 = X86.Sse2.cmpeq_epi32(layerMask0, X86.Sse2.setzero_si128()); - v128 lm1 = X86.Sse2.cmpeq_epi32(layerMask1, X86.Sse2.setzero_si128()); - v128 z0 = X86.Sse4_1.blendv_ps(zMin0, zTri, lm0); - v128 z1 = X86.Sse4_1.blendv_ps(zMin1, zTri, lm1); - - // Compute distances used for merging heuristic - v128 d0 = /*abs_ps*/ X86.Sse.and_ps(sdist0, X86.Sse2.set1_epi32(0x7FFFFFFF)); - v128 d1 = /*abs_ps*/ X86.Sse.and_ps(sdist1, X86.Sse2.set1_epi32(0x7FFFFFFF)); - v128 d2 = /*abs_ps*/ X86.Sse.and_ps(X86.Sse.sub_ps(z0, z1), X86.Sse2.set1_epi32(0x7FFFFFFF)); - - // Find minimum distance - v128 c01 = X86.Sse.sub_ps(d0, d1); - v128 c02 = X86.Sse.sub_ps(d0, d2); - v128 c12 = X86.Sse.sub_ps(d1, d2); - // Two tests indicating which layer the incoming triangle will merge with or - // overwrite. d0min indicates that the triangle will overwrite layer 0, and - // d1min flags that the triangle will overwrite layer 1. - v128 d0min = X86.Sse2.or_si128(X86.Sse2.and_si128(c01, c02), X86.Sse2.or_si128(lm0, t0)); - v128 d1min = X86.Sse2.andnot_si128(d0min, X86.Sse2.or_si128(c12, lm1)); - - /* Update depth buffer entry. NOTE: we always merge into layer 0, so if the - triangle should be merged with layer 1, we first swap layer 0 & 1 and then - merge into layer 0. */ - - // Update mask based on which layer the triangle overwrites or was merged into - v128 inner = X86.Sse4_1.blendv_ps(triMask, layerMask1, d0min); - - // Update the zMin[0] value. There are four outcomes: overwrite with layer 1, - // merge with layer 1, merge with zTri or overwrite with layer 1 and then merge - // with zTri. - v128 e0 = X86.Sse4_1.blendv_ps(z0, z1, d1min); - v128 e1 = X86.Sse4_1.blendv_ps(z1, zTri, X86.Sse2.or_si128(d1min, d0min)); - - // Update the zMin[1] value. There are three outcomes: keep current value, - // overwrite with zTri, or overwrite with z1 - v128 z1t = X86.Sse4_1.blendv_ps(zTri, z1, d0min); - - tiles[tileIdx].zMin0 = X86.Sse.min_ps(e0, e1); - tiles[tileIdx].zMin1 = X86.Sse4_1.blendv_ps(z1t, z0, d1min); - tiles[tileIdx].mask = X86.Sse4_1.blendv_ps(inner, layerMask0, d1min); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void UpdateTileEventsYSSE(v128* triEventRemainder, v128* triSlopeTileRemainder, v128* triEdgeY, v128* triEvent, v128* triSlopeTileDelta, v128* triSlopeSign, int i) - { - if (X86.Sse2.IsSse2Supported) - { - triEventRemainder[i] = X86.Sse2.sub_epi32(triEventRemainder[i], triSlopeTileRemainder[i]); - v128 overflow = X86.Sse2.srai_epi32(triEventRemainder[i], 31); - triEventRemainder[i] = X86.Sse2.add_epi32(triEventRemainder[i], X86.Sse2.and_si128(overflow, triEdgeY[i])); - triEvent[i] = X86.Sse2.add_epi32(triEvent[i], X86.Sse2.add_epi32(triSlopeTileDelta[i], X86.Sse2.and_si128(overflow, triSlopeSign[i]))); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SortVerticesSSE(v128* vX, v128* vY) - { - if (X86.Sse4_1.IsSse41Supported) - { - // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value - for (int i = 0; i < 2; i++) - { - v128 ey1 = X86.Sse2.sub_epi32(vY[1], vY[0]); - v128 ey2 = X86.Sse2.sub_epi32(vY[2], vY[0]); - v128 swapMask = X86.Sse2.or_si128(X86.Sse2.or_si128(ey1, ey2), X86.Sse2.cmpeq_epi32(ey2, X86.Sse2.setzero_si128())); - - v128 sX = X86.Sse4_1.blendv_ps(vX[2], vX[0], swapMask); - vX[0] = X86.Sse4_1.blendv_ps(vX[0], vX[1], swapMask); - vX[1] = X86.Sse4_1.blendv_ps(vX[1], vX[2], swapMask); - vX[2] = sX; - - v128 sY = X86.Sse4_1.blendv_ps(vY[2], vY[0], swapMask); - vY[0] = X86.Sse4_1.blendv_ps(vY[0], vY[1], swapMask); - vY[1] = X86.Sse4_1.blendv_ps(vY[1], vY[2], swapMask); - vY[2] = sY; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ComputeDepthPlaneSSE(v128* pVtxX, v128* pVtxY, v128* pVtxZ, out v128 zPixelDx, out v128 zPixelDy) - { - if (X86.Sse.IsSseSupported) - { - // Setup z(x,y) = z0 + dx*x + dy*y screen space depth plane equation - v128 x2 = X86.Sse.sub_ps(pVtxX[2], pVtxX[0]); - v128 x1 = X86.Sse.sub_ps(pVtxX[1], pVtxX[0]); - v128 y1 = X86.Sse.sub_ps(pVtxY[1], pVtxY[0]); - v128 y2 = X86.Sse.sub_ps(pVtxY[2], pVtxY[0]); - v128 z1 = X86.Sse.sub_ps(pVtxZ[1], pVtxZ[0]); - v128 z2 = X86.Sse.sub_ps(pVtxZ[2], pVtxZ[0]); - v128 d = X86.Sse.div_ps(X86.Sse.set1_ps(1.0f), IntrinsicUtils._mmw_fmsub_ps(x1, y2, X86.Sse.mul_ps(y1, x2))); - zPixelDx = X86.Sse.mul_ps(IntrinsicUtils._mmw_fmsub_ps(z1, y2, X86.Sse.mul_ps(y1, z2)), d); - zPixelDy = X86.Sse.mul_ps(IntrinsicUtils._mmw_fmsub_ps(x1, z2, X86.Sse.mul_ps(z1, x2)), d); - } - else - { - zPixelDx = new v128(); - zPixelDy = new v128(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ComputeBoundingBoxSSE(v128* vX, v128* vY, ref ScissorRect scissor, out v128 bbminX, out v128 bbminY, out v128 bbmaxX, out v128 bbmaxY) - { - if (X86.Sse4_1.IsSse41Supported) - { - // Find Min/Max vertices - bbminX = X86.Sse2.cvttps_epi32(X86.Sse.min_ps(vX[0], X86.Sse.min_ps(vX[1], vX[2]))); - bbminY = X86.Sse2.cvttps_epi32(X86.Sse.min_ps(vY[0], X86.Sse.min_ps(vY[1], vY[2]))); - bbmaxX = X86.Sse2.cvttps_epi32(X86.Sse.max_ps(vX[0], X86.Sse.max_ps(vX[1], vX[2]))); - bbmaxY = X86.Sse2.cvttps_epi32(X86.Sse.max_ps(vY[0], X86.Sse.max_ps(vY[1], vY[2]))); - - // Clamp to tile boundaries - v128 SimdPadWMask = X86.Sse2.set1_epi32(~(BufferGroup.TileWidth - 1)); - v128 SimdPadHMask = X86.Sse2.set1_epi32(~(BufferGroup.TileHeight - 1)); - bbminX = X86.Sse2.and_si128(bbminX, SimdPadWMask); - bbmaxX = X86.Sse2.and_si128(X86.Sse2.add_epi32(bbmaxX, X86.Sse2.set1_epi32(BufferGroup.TileWidth)), SimdPadWMask); - bbminY = X86.Sse2.and_si128(bbminY, SimdPadHMask); - bbmaxY = X86.Sse2.and_si128(X86.Sse2.add_epi32(bbmaxY, X86.Sse2.set1_epi32(BufferGroup.TileHeight)), SimdPadHMask); - - // Clip to scissor - bbminX = X86.Sse4_1.max_epi32(bbminX, X86.Sse2.set1_epi32(scissor.mMinX)); - bbmaxX = X86.Sse4_1.min_epi32(bbmaxX, X86.Sse2.set1_epi32(scissor.mMaxX)); - bbminY = X86.Sse4_1.max_epi32(bbminY, X86.Sse2.set1_epi32(scissor.mMinY)); - bbmaxY = X86.Sse4_1.min_epi32(bbmaxY, X86.Sse2.set1_epi32(scissor.mMaxY)); - } - else - { - bbminX = new v128(); - bbminY = new v128(); - bbmaxX = new v128(); - bbmaxY = new v128(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void ProjectVerticesSSE(v128* ipVtxX, v128* ipVtxY, v128* pVtxX, v128* pVtxY, v128* pVtxZ, v128* vtxX, v128* vtxY, v128* vtxW) - { - if (X86.Sse2.IsSse2Supported) - { - const float FP_INV = 1f / (1 << BufferGroup.FpBits); - // Project vertices and transform to screen space. Snap to sub-pixel coordinates with BufferGroup.FpBits precision. - for (int i = 0; i < 3; i++) - { - int idx = 2 - i; - v128 rcpW; - - if (ProjectionType == BatchCullingProjectionType.Orthographic) - { - rcpW = IntrinsicUtils._mmw_fmadd_ps(X86.Sse.set1_ps(-1.0f), vtxW[i], X86.Sse.set1_ps(1.0f)); - - v128 screenX = IntrinsicUtils._mmw_fmadd_ps(vtxX[i], HalfWidth, PixelCenterX); - v128 screenY = IntrinsicUtils._mmw_fmadd_ps(vtxY[i], HalfHeight, PixelCenterY); - ipVtxX[idx] = X86.Sse2.cvtps_epi32(X86.Sse.mul_ps(screenX, X86.Sse.set1_ps((float)(1 << BufferGroup.FpBits)))); - ipVtxY[idx] = X86.Sse2.cvtps_epi32(X86.Sse.mul_ps(screenY, X86.Sse.set1_ps((float)(1 << BufferGroup.FpBits)))); - } - else - { - rcpW = X86.Sse.div_ps(X86.Sse.set1_ps(1f), vtxW[i]); - - v128 screenX = IntrinsicUtils._mmw_fmadd_ps(X86.Sse.mul_ps(vtxX[i], HalfWidth), rcpW, PixelCenterX); - v128 screenY = IntrinsicUtils._mmw_fmadd_ps(X86.Sse.mul_ps(vtxY[i], HalfHeight), rcpW, PixelCenterY); - - ipVtxX[idx] = X86.Sse2.cvtps_epi32(X86.Sse.mul_ps(screenX, X86.Sse.set1_ps((float)(1 << BufferGroup.FpBits)))); - ipVtxY[idx] = X86.Sse2.cvtps_epi32(X86.Sse.mul_ps(screenY, X86.Sse.set1_ps((float)(1 << BufferGroup.FpBits)))); - } - - pVtxX[idx] = X86.Sse.mul_ps(X86.Sse2.cvtepi32_ps(ipVtxX[idx]), X86.Sse.set1_ps(FP_INV)); - pVtxY[idx] = X86.Sse.mul_ps(X86.Sse2.cvtepi32_ps(ipVtxY[idx]), X86.Sse.set1_ps(FP_INV)); - pVtxZ[idx] = rcpW; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void RasterizeTriangleBatchSSE(Tile* tiles, v128* ipVtxX, v128* ipVtxY, v128* pVtxX, v128* pVtxY, v128* pVtxZ, uint triMask, ScissorRect scissor) - { - if (X86.Sse4_1.IsSse41Supported) - { - //we are computing the bounding box again when we used it before but there are some use cases after, this check cannot be removed atm - - // Compute bounding box and clamp to tile coordinates - ComputeBoundingBoxSSE(pVtxX, pVtxY, ref scissor, out var bbPixelMinX, out var bbPixelMinY, out var bbPixelMaxX, out var bbPixelMaxY); - - // Clamp bounding box to tiles (it's already padded in computeBoundingBox) - v128 bbTileMinX = X86.Sse2.srai_epi32(bbPixelMinX, BufferGroup.TileWidthShift); - v128 bbTileMinY = X86.Sse2.srai_epi32(bbPixelMinY, BufferGroup.TileHeightShift); - v128 bbTileMaxX = X86.Sse2.srai_epi32(bbPixelMaxX, BufferGroup.TileWidthShift); - v128 bbTileMaxY = X86.Sse2.srai_epi32(bbPixelMaxY, BufferGroup.TileHeightShift); - v128 bbTileSizeX = X86.Sse2.sub_epi32(bbTileMaxX, bbTileMinX); - v128 bbTileSizeY = X86.Sse2.sub_epi32(bbTileMaxY, bbTileMinY); - - // Cull triangles with zero bounding box - v128 bboxSign = X86.Sse2.or_si128(X86.Sse2.sub_epi32(bbTileSizeX, X86.Sse2.set1_epi32(1)), X86.Sse2.sub_epi32(bbTileSizeY, X86.Sse2.set1_epi32(1))); - triMask &= (uint)((~X86.Sse.movemask_ps(bboxSign)) & SIMD_ALL_LANES_MASK); - - if (triMask == 0x0) - { - return; // View-culled - } - - // Set up screen space depth plane - ComputeDepthPlaneSSE(pVtxX, pVtxY, pVtxZ, out var zPixelDx, out var zPixelDy); - - // Compute z value at min corner of bounding box. Offset to make sure z is conservative for all 8x4 subtiles - v128 bbMinXV0 = X86.Sse.sub_ps(X86.Sse2.cvtepi32_ps(bbPixelMinX), pVtxX[0]); - v128 bbMinYV0 = X86.Sse.sub_ps(X86.Sse2.cvtepi32_ps(bbPixelMinY), pVtxY[0]); - v128 zPlaneOffset = IntrinsicUtils._mmw_fmadd_ps(zPixelDx, bbMinXV0, IntrinsicUtils._mmw_fmadd_ps(zPixelDy, bbMinYV0, pVtxZ[0])); - v128 zTileDx = X86.Sse.mul_ps(zPixelDx, X86.Sse.set1_ps(BufferGroup.TileWidth)); - v128 zTileDy = X86.Sse.mul_ps(zPixelDy, X86.Sse.set1_ps(BufferGroup.TileHeight)); - - zPlaneOffset = X86.Sse.add_ps(zPlaneOffset, X86.Sse.min_ps(X86.Sse2.setzero_si128(), X86.Sse.mul_ps(zPixelDx, X86.Sse.set1_ps(BufferGroup.SubTileWidth)))); - zPlaneOffset = X86.Sse.add_ps(zPlaneOffset, X86.Sse.min_ps(X86.Sse2.setzero_si128(), X86.Sse.mul_ps(zPixelDy, X86.Sse.set1_ps(BufferGroup.SubTileHeight)))); - - // Compute Zmin and Zmax for the triangle (used to narrow the range for difficult tiles) - v128 zMin = X86.Sse.min_ps(pVtxZ[0], X86.Sse.min_ps(pVtxZ[1], pVtxZ[2])); - v128 zMax = X86.Sse.max_ps(pVtxZ[0], X86.Sse.max_ps(pVtxZ[1], pVtxZ[2])); - - /* Sort vertices (v0 has lowest Y, and the rest is in winding order) and compute edges. Also find the middle - vertex and compute tile */ - - // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value - SortVerticesSSE(ipVtxX, ipVtxY); - - // Compute edges - v128* edgeX = stackalloc v128[3]; - edgeX[0] = X86.Sse2.sub_epi32(ipVtxX[1], ipVtxX[0]); - edgeX[1] = X86.Sse2.sub_epi32(ipVtxX[2], ipVtxX[1]); - edgeX[2] = X86.Sse2.sub_epi32(ipVtxX[2], ipVtxX[0]); - - v128* edgeY = stackalloc v128[3]; - edgeY[0] = X86.Sse2.sub_epi32(ipVtxY[1], ipVtxY[0]); - edgeY[1] = X86.Sse2.sub_epi32(ipVtxY[2], ipVtxY[1]); - edgeY[2] = X86.Sse2.sub_epi32(ipVtxY[2], ipVtxY[0]); - - // Classify if the middle vertex is on the left or right and compute its position - int midVtxRight = ~X86.Sse.movemask_ps(edgeY[1]); - v128 midPixelX = X86.Sse4_1.blendv_ps(ipVtxX[1], ipVtxX[2], edgeY[1]); - v128 midPixelY = X86.Sse4_1.blendv_ps(ipVtxY[1], ipVtxY[2], edgeY[1]); - v128 midTileY = X86.Sse2.srai_epi32(X86.Sse4_1.max_epi32(midPixelY, X86.Sse2.setzero_si128()), BufferGroup.TileHeightShift + BufferGroup.FpBits); - v128 bbMidTileY = X86.Sse4_1.max_epi32(bbTileMinY, X86.Sse4_1.min_epi32(bbTileMaxY, midTileY)); - - // Compute edge events for the bottom of the bounding box, or for the middle tile in case of - // the edge originating from the middle vertex. - v128* xDiffi = stackalloc v128[2]; - xDiffi[0] = X86.Sse2.sub_epi32(ipVtxX[0], X86.Sse2.slli_epi32(bbPixelMinX, BufferGroup.FpBits)); - xDiffi[1] = X86.Sse2.sub_epi32(midPixelX, X86.Sse2.slli_epi32(bbPixelMinX, BufferGroup.FpBits)); - - v128* yDiffi = stackalloc v128[2]; - yDiffi[0] = X86.Sse2.sub_epi32(ipVtxY[0], X86.Sse2.slli_epi32(bbPixelMinY, BufferGroup.FpBits)); - yDiffi[1] = X86.Sse2.sub_epi32(midPixelY, X86.Sse2.slli_epi32(bbMidTileY, BufferGroup.FpBits + BufferGroup.TileHeightShift)); - - /* Edge slope setup - Note we do not conform to DX/GL rasterization rules */ - - // Potentially flip edge to ensure that all edges have positive Y slope. - edgeX[1] = X86.Sse4_1.blendv_ps(edgeX[1], /*neg_epi32*/ X86.Sse2.sub_epi32(X86.Sse2.set1_epi32(0), edgeX[1]), edgeY[1]); - edgeY[1] = X86.Ssse3.abs_epi32(edgeY[1]); - - // Compute floating point slopes - v128* slope = stackalloc v128[3]; - slope[0] = X86.Sse.div_ps(X86.Sse2.cvtepi32_ps(edgeX[0]), X86.Sse2.cvtepi32_ps(edgeY[0])); - slope[1] = X86.Sse.div_ps(X86.Sse2.cvtepi32_ps(edgeX[1]), X86.Sse2.cvtepi32_ps(edgeY[1])); - slope[2] = X86.Sse.div_ps(X86.Sse2.cvtepi32_ps(edgeX[2]), X86.Sse2.cvtepi32_ps(edgeY[2])); - - // Modify slope of horizontal edges to make sure they mask out pixels above/below the edge. The slope is set to screen - // width to mask out all pixels above or below the horizontal edge. We must also add a small bias to acount for that - // vertices may end up off screen due to clipping. We're assuming that the round off error is no bigger than 1.0 - v128 horizontalSlopeDelta = X86.Sse.set1_ps(2f * (NumPixelsX + 2f * (BufferGroup.GuardBandPixelSize + 1.0f))); - v128 horizontalSlope0 = X86.Sse2.cmpeq_epi32(edgeY[0], X86.Sse2.setzero_si128()); - v128 horizontalSlope1 = X86.Sse2.cmpeq_epi32(edgeY[1], X86.Sse2.setzero_si128()); - slope[0] = X86.Sse4_1.blendv_ps(slope[0], horizontalSlopeDelta, horizontalSlope0); - slope[1] = X86.Sse4_1.blendv_ps(slope[1], /*neg_ps*/ X86.Sse.xor_ps(horizontalSlopeDelta, X86.Sse.set1_ps(-0f)), horizontalSlope1); - - v128* vy = stackalloc v128[3]; - vy[0] = yDiffi[0]; - vy[1] = yDiffi[1]; - vy[2] = yDiffi[0]; - - v128 offset0 = X86.Sse2.and_si128(X86.Sse2.add_epi32(yDiffi[0], X86.Sse2.set1_epi32(BufferGroup.FpHalfPixel - 1)), X86.Sse2.set1_epi32((-1 << BufferGroup.FpBits))); - v128 offset1 = X86.Sse2.and_si128(X86.Sse2.add_epi32(yDiffi[1], X86.Sse2.set1_epi32(BufferGroup.FpHalfPixel - 1)), X86.Sse2.set1_epi32((-1 << BufferGroup.FpBits))); - vy[0] = X86.Sse4_1.blendv_ps(yDiffi[0], offset0, horizontalSlope0); - vy[1] = X86.Sse4_1.blendv_ps(yDiffi[1], offset1, horizontalSlope1); - - // Compute edge events for the bottom of the bounding box, or for the middle tile in case of - // the edge originating from the middle vertex. - v128* slopeSign = stackalloc v128[3]; - v128* absEdgeX = stackalloc v128[3]; - v128* slopeTileDelta = stackalloc v128[3]; - v128* eventStartRemainder = stackalloc v128[3]; - v128* slopeTileRemainder = stackalloc v128[3]; - v128* eventStart = stackalloc v128[3]; - - for (int i = 0; i < 3; i++) - { - // Common, compute slope sign (used to propagate the remainder term when overflowing) is postive or negative x-direction - slopeSign[i] = X86.Sse4_1.blendv_ps(X86.Sse2.set1_epi32(1), X86.Sse2.set1_epi32(-1), edgeX[i]); - absEdgeX[i] = X86.Ssse3.abs_epi32(edgeX[i]); - - // Delta and error term for one vertical tile step. The exact delta is exactDelta = edgeX / edgeY, due to limited precision we - // repersent the delta as delta = qoutient + remainder / edgeY, where quotient = int(edgeX / edgeY). In this case, since we step - // one tile of scanlines at a time, the slope is computed for a tile-sized step. - slopeTileDelta[i] = X86.Sse2.cvttps_epi32(X86.Sse.mul_ps(slope[i], X86.Sse.set1_ps(BufferGroup.FpTileHeight))); - slopeTileRemainder[i] = X86.Sse2.sub_epi32(X86.Sse2.slli_epi32(absEdgeX[i], BufferGroup.FpTileHeightShift), X86.Sse4_1.mullo_epi32(X86.Ssse3.abs_epi32(slopeTileDelta[i]), edgeY[i])); - - // Jump to bottom scanline of tile row, this is the bottom of the bounding box, or the middle vertex of the triangle. - // The jump can be in both positive and negative y-direction due to clipping / offscreen vertices. - v128 tileStartDir = X86.Sse4_1.blendv_ps(slopeSign[i], /*neg_epi32*/ X86.Sse2.sub_epi32(X86.Sse2.set1_epi32(0), slopeSign[i]), vy[i]); - v128 tieBreaker = X86.Sse4_1.blendv_ps(X86.Sse2.set1_epi32(0), X86.Sse2.set1_epi32(1), tileStartDir); - v128 tileStartSlope = X86.Sse2.cvttps_epi32(X86.Sse.mul_ps(slope[i], X86.Sse2.cvtepi32_ps(/*neg_epi32*/ X86.Sse2.sub_epi32(X86.Sse2.set1_epi32(0), vy[i])))); - v128 tileStartRemainder = X86.Sse2.sub_epi32(X86.Sse4_1.mullo_epi32(absEdgeX[i], X86.Ssse3.abs_epi32(vy[i])), X86.Sse4_1.mullo_epi32(X86.Ssse3.abs_epi32(tileStartSlope), edgeY[i])); - - eventStartRemainder[i] = X86.Sse2.sub_epi32(tileStartRemainder, tieBreaker); - v128 overflow = X86.Sse2.srai_epi32(eventStartRemainder[i], 31); - eventStartRemainder[i] = X86.Sse2.add_epi32(eventStartRemainder[i], X86.Sse2.and_si128(overflow, edgeY[i])); - eventStartRemainder[i] = X86.Sse4_1.blendv_ps(eventStartRemainder[i], X86.Sse2.sub_epi32(X86.Sse2.sub_epi32(edgeY[i], eventStartRemainder[i]), X86.Sse2.set1_epi32(1)), vy[i]); - - //eventStart[i] = xDiffi[i & 1] + tileStartSlope + (overflow & tileStartDir) + X86.Sse2.set1_epi32(FP_HALF_PIXEL - 1) + tieBreaker; - eventStart[i] = X86.Sse2.add_epi32(X86.Sse2.add_epi32(xDiffi[i & 1], tileStartSlope), X86.Sse2.and_si128(overflow, tileStartDir)); - eventStart[i] = X86.Sse2.add_epi32(X86.Sse2.add_epi32(eventStart[i], X86.Sse2.set1_epi32(BufferGroup.FpHalfPixel - 1)), tieBreaker); - } - - // Split bounding box into bottom - middle - top region. - v128 bbBottomIdx = X86.Sse2.add_epi32(bbTileMinX, X86.Sse4_1.mullo_epi32(bbTileMinY, X86.Sse2.set1_epi32(NumTilesX))); - v128 bbTopIdx = X86.Sse2.add_epi32(bbTileMinX, X86.Sse4_1.mullo_epi32(X86.Sse2.add_epi32(bbTileMinY, bbTileSizeY), X86.Sse2.set1_epi32(NumTilesX))); - v128 bbMidIdx = X86.Sse2.add_epi32(bbTileMinX, X86.Sse4_1.mullo_epi32(midTileY, X86.Sse2.set1_epi32(NumTilesX))); - - // Loop over non-culled triangle and change SIMD axis to per-pixel - while (triMask != 0) - { - uint triIdx = (uint)IntrinsicUtils.find_clear_lsb(ref triMask); - int triMidVtxRight = (midVtxRight >> (int)triIdx) & 1; - - // Get Triangle Zmin zMax - v128 zTriMax = X86.Sse.set1_ps(IntrinsicUtils.getFloatLane(zMax, triIdx)); - v128 zTriMin = X86.Sse.set1_ps(IntrinsicUtils.getFloatLane(zMin, triIdx)); - - // Setup Zmin value for first set of 8x4 subtiles - v128 SimdSubTileColOffsetF = X86.Sse.setr_ps(0, BufferGroup.SubTileWidth, BufferGroup.SubTileWidth * 2, BufferGroup.SubTileWidth * 3); - v128 z0 = IntrinsicUtils._mmw_fmadd_ps(X86.Sse.set1_ps(IntrinsicUtils.getFloatLane(zPixelDx, triIdx)), - SimdSubTileColOffsetF, - IntrinsicUtils._mmw_fmadd_ps(X86.Sse.set1_ps(IntrinsicUtils.getFloatLane(zPixelDy, triIdx)), - X86.Sse2.setzero_si128(), - X86.Sse.set1_ps(IntrinsicUtils.getFloatLane(zPlaneOffset, triIdx)))); - - float zx = IntrinsicUtils.getFloatLane(zTileDx, triIdx); - float zy = IntrinsicUtils.getFloatLane(zTileDy, triIdx); - - // Get dimension of bounding box bottom, mid & top segments - int bbWidth = IntrinsicUtils.getIntLane(bbTileSizeX, triIdx); - int bbHeight = IntrinsicUtils.getIntLane(bbTileSizeY, triIdx); - int tileRowIdx = IntrinsicUtils.getIntLane(bbBottomIdx, triIdx); - int tileMidRowIdx = IntrinsicUtils.getIntLane(bbMidIdx, triIdx); - int tileEndRowIdx = IntrinsicUtils.getIntLane(bbTopIdx, triIdx); - - if (bbWidth > BIG_TRIANGLE && bbHeight > BIG_TRIANGLE) // For big triangles we use a more expensive but tighter traversal algorithm - { - if (triMidVtxRight != 0) - { - RasterizeTriangleSSE(tiles, true, 1, triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, ref z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); - } - else - { - RasterizeTriangleSSE(tiles, true, 0, triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, ref z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); - } - } - else - { - if (triMidVtxRight != 0) - { - RasterizeTriangleSSE(tiles, false, 1, triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, ref z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); - } - else - { - RasterizeTriangleSSE(tiles, false, 0, triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, ref z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); - } - } - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void RasterizeTriangleSSE( - Tile* tiles, - bool isTightTraversal, - int midVtxRight, - uint triIdx, - int bbWidth, - int tileRowIdx, - int tileMidRowIdx, - int tileEndRowIdx, - v128* eventStart, - v128* slope, - v128* slopeTileDelta, - v128 zTriMin, - v128 zTriMax, - ref v128 z0, - float zx, - float zy, - v128* edgeY, - v128* absEdgeX, - v128* slopeSign, - v128* eventStartRemainder, - v128* slopeTileRemainder) - { - if (X86.Sse4_1.IsSse41Supported) - { - const int LEFT_EDGE_BIAS = -1; - const int RIGHT_EDGE_BIAS = 1; - - v128* triSlopeSign = stackalloc v128[3]; - v128* triSlopeTileDelta = stackalloc v128[3]; - v128* triEdgeY = stackalloc v128[3]; - v128* triSlopeTileRemainder = stackalloc v128[3]; - v128* triEventRemainder = stackalloc v128[3]; - v128* triEvent = stackalloc v128[3]; - - for (int i = 0; i < 3; ++i) - { - triSlopeSign[i] = X86.Sse2.set1_epi32(IntrinsicUtils.getIntLane(slopeSign[i], triIdx)); - triSlopeTileDelta[i] = - X86.Sse2.set1_epi32(IntrinsicUtils.getIntLane(slopeTileDelta[i], triIdx)); - triEdgeY[i] = X86.Sse2.set1_epi32(IntrinsicUtils.getIntLane(edgeY[i], triIdx)); - triSlopeTileRemainder[i] = - X86.Sse2.set1_epi32(IntrinsicUtils.getIntLane(slopeTileRemainder[i], triIdx)); - - v128 triSlope = X86.Sse.set1_ps(IntrinsicUtils.getFloatLane(slope[i], triIdx)); - v128 triAbsEdgeX = X86.Sse2.set1_epi32(IntrinsicUtils.getIntLane(absEdgeX[i], triIdx)); - v128 triStartRemainder = - X86.Sse2.set1_epi32(IntrinsicUtils.getIntLane(eventStartRemainder[i], triIdx)); - v128 triEventStart = X86.Sse2.set1_epi32(IntrinsicUtils.getIntLane(eventStart[i], triIdx)); - - v128 SimdLaneYCoordF = X86.Sse.setr_ps(128f, 384f, 640f, 896f); - v128 scanlineDelta = X86.Sse2.cvttps_epi32(X86.Sse.mul_ps(triSlope, SimdLaneYCoordF)); - v128 SimdLaneYCoordI = X86.Sse2.setr_epi32(128, 384, 640, 896); - v128 scanlineSlopeRemainder = - X86.Sse2.sub_epi32(X86.Sse4_1.mullo_epi32(triAbsEdgeX, SimdLaneYCoordI), - X86.Sse4_1.mullo_epi32(X86.Ssse3.abs_epi32(scanlineDelta), triEdgeY[i])); - - triEventRemainder[i] = X86.Sse2.sub_epi32(triStartRemainder, scanlineSlopeRemainder); - v128 overflow = X86.Sse2.srai_epi32(triEventRemainder[i], 31); - triEventRemainder[i] = - X86.Sse2.add_epi32(triEventRemainder[i], X86.Sse2.and_si128(overflow, triEdgeY[i])); - triEvent[i] = - X86.Sse2.add_epi32(X86.Sse2.add_epi32(triEventStart, scanlineDelta), - X86.Sse2.and_si128(overflow, triSlopeSign[i])); - } - - // For big triangles track start & end tile for each scanline and only traverse the valid region - int startDelta = 0; - int endDelta = 0; - int topDelta = 0; - int startEvent = 0; - int endEvent = 0; - int topEvent = 0; - - if (isTightTraversal) - { - startDelta = IntrinsicUtils.getIntLane(slopeTileDelta[2], triIdx) + LEFT_EDGE_BIAS; - endDelta = IntrinsicUtils.getIntLane(slopeTileDelta[0], triIdx) + RIGHT_EDGE_BIAS; - topDelta = IntrinsicUtils.getIntLane(slopeTileDelta[1], triIdx) + - (midVtxRight != 0 ? RIGHT_EDGE_BIAS : LEFT_EDGE_BIAS); - - // Compute conservative bounds for the edge events over a 32xN tile - startEvent = IntrinsicUtils.getIntLane(eventStart[2], triIdx) + Mathf.Min(0, startDelta); - endEvent = IntrinsicUtils.getIntLane(eventStart[0], triIdx) + Mathf.Max(0, endDelta) + - (BufferGroup.TileWidth << BufferGroup.FpBits); // TODO: (Apoorva) can be spun out into a const - - if (midVtxRight != 0) - { - topEvent = IntrinsicUtils.getIntLane(eventStart[1], triIdx) + Mathf.Max(0, topDelta) + - (BufferGroup.TileWidth << BufferGroup.FpBits); // TODO: (Apoorva) can be spun out into a const - } - else - { - topEvent = IntrinsicUtils.getIntLane(eventStart[1], triIdx) + Mathf.Min(0, topDelta); - } - } - - if (tileRowIdx <= tileMidRowIdx) - { - int tileStopIdx = Mathf.Min(tileEndRowIdx, tileMidRowIdx); - - // Traverse the bottom half of the triangle - while (tileRowIdx < tileStopIdx) - { - int start = 0; - int end = bbWidth; - - if (isTightTraversal) - { - // Compute tighter start and endpoints to avoid traversing empty space - start = Mathf.Max(0, Mathf.Min(bbWidth - 1, startEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); // TODO: (Apoorva) can be spun out into a const - end = Mathf.Min(bbWidth, ((int)endEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); // TODO: (Apoorva) can be spun out into a const - - startEvent += startDelta; - endEvent += endDelta; - } - - // Traverse the scanline and update the masked hierarchical z buffer - TraverseScanlineSSE(tiles, 1, 1, start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, z0, - zx); - - // move to the next scanline of tiles, update edge events and interpolate z - tileRowIdx += NumTilesX; - z0 = X86.Sse.add_ps(z0, X86.Sse.set1_ps(zy)); - - UpdateTileEventsYSSE(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, 0); - UpdateTileEventsYSSE(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, 2); - } - - // Traverse the middle scanline of tiles. We must consider all three edges only in this region - if (tileRowIdx < tileEndRowIdx) - { - int start = 0; - int end = bbWidth; - - if (isTightTraversal) - { - // Compute tighter start and endpoints to avoid traversing lots of empty space - start = Mathf.Max(0, Mathf.Min(bbWidth - 1, startEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); // TODO: (Apoorva) can be spun out into a const - end = Mathf.Min(bbWidth, ((int)endEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); // TODO: (Apoorva) can be spun out into a const - - // Switch the traversal start / end to account for the upper side edge - endEvent = midVtxRight != 0 ? topEvent : endEvent; - endDelta = midVtxRight != 0 ? topDelta : endDelta; - startEvent = midVtxRight != 0 ? startEvent : topEvent; - startDelta = midVtxRight != 0 ? startDelta : topDelta; - - startEvent += startDelta; - endEvent += endDelta; - } - - // Traverse the scanline and update the masked hierarchical z buffer. - if (midVtxRight != 0) - { - TraverseScanlineSSE(tiles, 2, 1, start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, - z0, zx); - } - else - { - TraverseScanlineSSE(tiles, 1, 2, start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, - z0, zx); - } - - tileRowIdx += NumTilesX; - } - - // Traverse the top half of the triangle - if (tileRowIdx < tileEndRowIdx) - { - // move to the next scanline of tiles, update edge events and interpolate z - z0 = X86.Sse.add_ps(z0, X86.Sse.set1_ps(zy)); - int i0 = midVtxRight + 0; - int i1 = midVtxRight + 1; - - UpdateTileEventsYSSE(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i0); - UpdateTileEventsYSSE(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i1); - - for (; ; ) - { - int start = 0; - int end = bbWidth; - - if (isTightTraversal) - { - // Compute tighter start and endpoints to avoid traversing lots of empty space - start = Mathf.Max(0, Mathf.Min(bbWidth - 1, startEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - end = Mathf.Min(bbWidth, (endEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - - startEvent += startDelta; - endEvent += endDelta; - } - - // Traverse the scanline and update the masked hierarchical z buffer - TraverseScanlineSSE(tiles, 1, 1, start, end, tileRowIdx, midVtxRight + 0, - midVtxRight + 1, triEvent, zTriMin, zTriMax, z0, zx); - - // move to the next scanline of tiles, update edge events and interpolate z - tileRowIdx += NumTilesX; - if (tileRowIdx >= tileEndRowIdx) - { - break; - } - - z0 = X86.Sse.add_ps(z0, X86.Sse.set1_ps(zy)); - - UpdateTileEventsYSSE(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i0); - UpdateTileEventsYSSE(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i1); - } - } - } - else - { - if (isTightTraversal) - { - // For large triangles, switch the traversal start / end to account for the upper side edge - endEvent = midVtxRight != 0 ? topEvent : endEvent; - endDelta = midVtxRight != 0 ? topDelta : endDelta; - startEvent = midVtxRight != 0 ? startEvent : topEvent; - startDelta = midVtxRight != 0 ? startDelta : topDelta; - } - - // Traverse the top half of the triangle - if (tileRowIdx < tileEndRowIdx) - { - int i0 = midVtxRight + 0; - int i1 = midVtxRight + 1; - - for (; ; ) - { - int start = 0; - int end = bbWidth; - - if (isTightTraversal) - { - // Compute tighter start and endpoints to avoid traversing lots of empty space - start = Mathf.Max(0, Mathf.Min(bbWidth - 1, startEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - end = Mathf.Min(bbWidth, (endEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - - startEvent += startDelta; - endEvent += endDelta; - } - - // Traverse the scanline and update the masked hierarchical z buffer - TraverseScanlineSSE(tiles, 1, 1, start, end, tileRowIdx, midVtxRight + 0, - midVtxRight + 1, triEvent, zTriMin, zTriMax, z0, zx); - - // move to the next scanline of tiles, update edge events and interpolate z - tileRowIdx += NumTilesX; - if (tileRowIdx >= tileEndRowIdx) - { - break; - } - - z0 = X86.Sse.add_ps(z0, X86.Sse.set1_ps(zy)); - - UpdateTileEventsYSSE(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i0); - UpdateTileEventsYSSE(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i1); - } - } - } - } - } - #endregion - - #region Neon - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void TraverseScanlineNEON(Tile* tiles, int numRight, int numLeft, int leftOffset, int rightOffset, int tileIdx, int rightEvent, int leftEvent, v128* events, v128 zTriMin, v128 zTriMax, v128 iz0, float zx) - { - if (Arm.Neon.IsNeonSupported) - { - v128* right = stackalloc v128[numRight]; - v128* left = stackalloc v128[numLeft]; - - // Floor edge events to integer pixel coordinates (shift out fixed point bits) - v128 eventOffset = new v128(leftOffset << BufferGroup.TileWidthShift); - v128 emptyBitMask = new v128(0); - v128 fullBitMask = new v128(~0); - v128 highbit = new v128(0x80000000u); - v128 simdTileWidth = new v128(BufferGroup.TileWidth); - - for (int i = 0; i < numRight; ++i) - { - right[i] = Arm.Neon.vmaxq_s32(Arm.Neon.vsubq_s32(Arm.Neon.vshrq_n_s32(events[rightEvent + i], BufferGroup.FpBits), eventOffset), emptyBitMask); - - } - - for (int i = 0; i < numLeft; ++i) - { - left[i] = Arm.Neon.vmaxq_s32(Arm.Neon.vsubq_s32(Arm.Neon.vshrq_n_s32(events[leftEvent - i], BufferGroup.FpBits), eventOffset), emptyBitMask); - } - - v128 z0 = Arm.Neon.vaddq_f32(iz0, new v128(zx * leftOffset)); - int tileIdxEnd = tileIdx + rightOffset; - tileIdx += leftOffset; - - for (; ; ) - { - // Compute zMin for the overlapped layers - v128 mask = tiles[tileIdx].mask; - v128 zMin0 = IntrinsicUtils._vblendq_f32(Arm.Neon.vceqq_s32(mask, fullBitMask), tiles[tileIdx].zMin0, tiles[tileIdx].zMin1); - v128 zMin1 = IntrinsicUtils._vblendq_f32(Arm.Neon.vceqq_s32(mask, emptyBitMask), tiles[tileIdx].zMin1, tiles[tileIdx].zMin0); - v128 zMinBuf = Arm.Neon.vminq_f32(zMin0, zMin1); - v128 comp = Arm.Neon.vcltq_f32(zTriMax, zMinBuf); - // https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon - // Instead of movemask_ps - v64 compPacked = Arm.Neon.vshrn_n_s32(comp, 16); - - if (compPacked.ULong0 != 0xfffffffffffffffful) - { - // Compute coverage mask for entire 32xN using shift operations - v128 accumulatedMask = IntrinsicUtils._vsllv_ones(left[0]); - - for (int i = 1; i < numLeft; ++i) - { - accumulatedMask = Arm.Neon.vandq_s8(accumulatedMask, IntrinsicUtils._vsllv_ones(left[i])); - } - - for (int i = 0; i < numRight; ++i) - { - accumulatedMask = Arm.Neon.vbicq_s8(accumulatedMask, IntrinsicUtils._vsllv_ones(right[i])); - } - - // Compute interpolated min for each 8x4 subtile and update the masked hierarchical z buffer entry - v128 zSubTileMin = Arm.Neon.vmaxq_f32(z0, zTriMin); - UpdateTileAccurateNEON(tiles, tileIdx, IntrinsicUtils._vtranspose_s8(accumulatedMask), zSubTileMin); - } - - // Update buffer address, interpolate z and edge events - tileIdx++; - - if (tileIdx >= tileIdxEnd) - { - break; - } - - z0 = Arm.Neon.vaddq_f32(z0, new v128(zx)); - - for (int i = 0; i < numRight; ++i) - { - // Trick, use sub saturated to avoid checking against < 0 for shift (values should fit in 16 bits) - right[i] = Arm.Neon.vqsubq_u16(right[i], simdTileWidth); - } - - for (int i = 0; i < numLeft; ++i) - { - // Trick, use sub saturated to avoid checking against < 0 for shift (values should fit in 16 bits) - left[i] = Arm.Neon.vqsubq_u16(left[i], simdTileWidth); - } - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void UpdateTileAccurateNEON(Tile* tiles, int tileIdx, v128 coverage, v128 zTriv) - { - if (Arm.Neon.IsNeonSupported) - { - v128 zMin0 = tiles[tileIdx].zMin0; - v128 zMin1 = tiles[tileIdx].zMin1; - v128 mask = tiles[tileIdx].mask; - - // Swizzle coverage mask to 8x4 subtiles - v128 rastMask = coverage; - - // Perform individual depth tests with layer 0 & 1 and mask out all failing pixels - v128 sdist0 = Arm.Neon.vsubq_f32(zMin0, zTriv); - v128 sdist1 = Arm.Neon.vsubq_f32(zMin1, zTriv); - v128 sign0 = Arm.Neon.vshrq_n_s32(sdist0, 31); - v128 sign1 = Arm.Neon.vshrq_n_s32(sdist1, 31); - v128 triMask = Arm.Neon.vandq_s8(rastMask, Arm.Neon.vorrq_s8(Arm.Neon.vbicq_s8(sign0, mask), Arm.Neon.vandq_s8(mask, sign1))); - // Early out if no pixels survived the depth test - // (this test is more accurate than the early culling test in TraverseScanline()) - v64 narrowSaturatedMask = Arm.Neon.vqmovn_u64(triMask); - if (narrowSaturatedMask.ULong0 == 0ul) - { - return; - } - -#if MOC_ENABLE_STATS - STATS_ADD(ref mStats.mOccluders.mNumTilesUpdated, 1); -#endif - v128 t0 = Arm.Neon.vceqzq_s32(triMask); - v128 zTri = IntrinsicUtils._vblendq_f32(t0, zTriv, zMin0); - - // Test if incoming triangle completely overwrites layer 0 or 1 - v128 layerMask0 = Arm.Neon.vbicq_s8(Arm.Neon.vmvnq_s32(mask), triMask); - v128 layerMask1 = Arm.Neon.vbicq_s8(mask, triMask); - v128 lm0 = Arm.Neon.vceqzq_s32(layerMask0); - v128 lm1 = Arm.Neon.vceqzq_s32(layerMask1); - v128 z0 = IntrinsicUtils._vblendq_f32(lm0, zMin0, zTri); - v128 z1 = IntrinsicUtils._vblendq_f32(lm1, zMin1, zTri); - - // Compute distances used for merging heuristic - v128 d0 = Arm.Neon.vabsq_f32(sdist0); - v128 d1 = Arm.Neon.vabsq_f32(sdist1); - v128 d2 = Arm.Neon.vabdq_f32(z0, z1); - - // Find minimum distance - v128 c01 = Arm.Neon.vsubq_f32(d0, d1); - v128 c02 = Arm.Neon.vsubq_f32(d0, d2); - v128 c12 = Arm.Neon.vsubq_f32(d1, d2); - - // Two tests indicating which layer the incoming triangle will merge with or - // overwrite. d0min indicates that the triangle will overwrite layer 0, and - // d1min flags that the triangle will overwrite layer 1. - v128 d0min = Arm.Neon.vorrq_s8(Arm.Neon.vandq_s8(c01, c02), Arm.Neon.vorrq_s8(lm0, t0)); - v128 d1min = Arm.Neon.vbicq_s8(Arm.Neon.vorrq_s8(c12, lm1), d0min); - - /* Update depth buffer entry. NOTE: we always merge into layer 0, so if the - triangle should be merged with layer 1, we first swap layer 0 & 1 and then - merge into layer 0. */ - - // Update mask based on which layer the triangle overwrites or was merged into - v128 inner = IntrinsicUtils._vblendq_f32(d0min, triMask, layerMask1); - - // Update the zMin[0] value. There are four outcomes: overwrite with layer 1, - // merge with layer 1, merge with zTri or overwrite with layer 1 and then merge - // with zTri. - v128 e0 = IntrinsicUtils._vblendq_f32(d1min, z0, z1); - v128 e1 = IntrinsicUtils._vblendq_f32(Arm.Neon.vorrq_s8(d1min, d0min), z1, zTri); - - // Update the zMin[1] value. There are three outcomes: keep current value, - // overwrite with zTri, or overwrite with z1 - v128 z1t = IntrinsicUtils._vblendq_f32(d0min, zTri, z1); - - tiles[tileIdx].zMin0 = Arm.Neon.vminq_f32(e0, e1); - tiles[tileIdx].zMin1 = IntrinsicUtils._vblendq_f32(d1min, z1t, z0); - tiles[tileIdx].mask = IntrinsicUtils._vblendq_f32(d1min, inner, layerMask0); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void UpdateTileEventsYNEON(v128* triEventRemainder, v128* triSlopeTileRemainder, v128* triEdgeY, v128* triEvent, v128* triSlopeTileDelta, v128* triSlopeSign, int i) - { - if (Arm.Neon.IsNeonSupported) - { - triEventRemainder[i] = Arm.Neon.vsubq_s32(triEventRemainder[i], triSlopeTileRemainder[i]); - v128 overflow = Arm.Neon.vshrq_n_s32(triEventRemainder[i], 31); - triEventRemainder[i] = Arm.Neon.vaddq_s32(triEventRemainder[i], Arm.Neon.vandq_s8(overflow, triEdgeY[i])); - triEvent[i] = Arm.Neon.vaddq_s32(triEvent[i], Arm.Neon.vaddq_s32(triSlopeTileDelta[i], Arm.Neon.vandq_s8(overflow, triSlopeSign[i]))); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SortVerticesNEON(v128* vX, v128* vY) - { - if (Arm.Neon.IsNeonSupported) - { - // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value - for (int i = 0; i < 2; i++) - { - v128 ey1 = Arm.Neon.vsubq_s32(vY[1], vY[0]); - v128 ey2 = Arm.Neon.vsubq_s32(vY[2], vY[0]); - v128 swapMask = Arm.Neon.vorrq_s8(Arm.Neon.vorrq_s8(ey1, ey2), Arm.Neon.vceqzq_s32(ey2)); - - v128 sX = IntrinsicUtils._vblendq_f32(swapMask, vX[2], vX[0]); - vX[0] = IntrinsicUtils._vblendq_f32(swapMask, vX[0], vX[1]); - vX[1] = IntrinsicUtils._vblendq_f32(swapMask, vX[1], vX[2]); - vX[2] = sX; - - v128 sY = IntrinsicUtils._vblendq_f32(swapMask, vY[2], vY[0]); - vY[0] = IntrinsicUtils._vblendq_f32(swapMask, vY[0], vY[1]); - vY[1] = IntrinsicUtils._vblendq_f32(swapMask, vY[1], vY[2]); - vY[2] = sY; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ComputeDepthPlaneNEON(v128* pVtxX, v128* pVtxY, v128* pVtxZ, out v128 zPixelDx, out v128 zPixelDy) - { - if (Arm.Neon.IsNeonSupported) - { - // Setup z(x,y) = z0 + dx*x + dy*y screen space depth plane equation - v128 x2 = Arm.Neon.vsubq_f32(pVtxX[2], pVtxX[0]); - v128 x1 = Arm.Neon.vsubq_f32(pVtxX[1], pVtxX[0]); - v128 y1 = Arm.Neon.vsubq_f32(pVtxY[1], pVtxY[0]); - v128 y2 = Arm.Neon.vsubq_f32(pVtxY[2], pVtxY[0]); - v128 z1 = Arm.Neon.vsubq_f32(pVtxZ[1], pVtxZ[0]); - v128 z2 = Arm.Neon.vsubq_f32(pVtxZ[2], pVtxZ[0]); - v128 d = Arm.Neon.vdivq_f32(new v128(1.0f), Arm.Neon.vmlsq_f32(Arm.Neon.vmulq_f32(x1, y2), y1, x2)); - zPixelDx = Arm.Neon.vmulq_f32(Arm.Neon.vmlsq_f32(Arm.Neon.vmulq_f32(z1, y2), y1, z2), d); - zPixelDy = Arm.Neon.vmulq_f32(Arm.Neon.vmlsq_f32(Arm.Neon.vmulq_f32(x1, z2), z1, x2), d); - } - else - { - zPixelDx = new v128(); - zPixelDy = new v128(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ComputeBoundingBoxNEON(v128* vX, v128* vY, ref ScissorRect scissor, out v128 bbminX, out v128 bbminY, out v128 bbmaxX, out v128 bbmaxY) - { - if (Arm.Neon.IsNeonSupported) - { - // Find Min/Max vertices - bbminX = Arm.Neon.vcvtnq_s32_f32(Arm.Neon.vminq_f32(vX[0], Arm.Neon.vminq_f32(vX[1], vX[2]))); - bbminY = Arm.Neon.vcvtnq_s32_f32(Arm.Neon.vminq_f32(vY[0], Arm.Neon.vminq_f32(vY[1], vY[2]))); - bbmaxX = Arm.Neon.vcvtnq_s32_f32(Arm.Neon.vmaxq_f32(vX[0], Arm.Neon.vmaxq_f32(vX[1], vX[2]))); - bbmaxY = Arm.Neon.vcvtnq_s32_f32(Arm.Neon.vmaxq_f32(vY[0], Arm.Neon.vmaxq_f32(vY[1], vY[2]))); - - // Clamp to tile boundaries - v128 SimdPadWMask = new v128(~(BufferGroup.TileWidth - 1)); - v128 SimdPadHMask = new v128(~(BufferGroup.TileHeight - 1)); - bbminX = Arm.Neon.vandq_s8(bbminX, SimdPadWMask); - bbmaxX = Arm.Neon.vandq_s8(Arm.Neon.vaddq_s32(bbmaxX, new v128(BufferGroup.TileWidth)), SimdPadWMask); - bbminY = Arm.Neon.vandq_s8(bbminY, SimdPadHMask); - bbmaxY = Arm.Neon.vandq_s8(Arm.Neon.vaddq_s32(bbmaxY, new v128(BufferGroup.TileHeight)), SimdPadHMask); - - // Clip to scissor - bbminX = Arm.Neon.vmaxq_s32(bbminX, new v128(scissor.mMinX)); - bbmaxX = Arm.Neon.vminq_s32(bbmaxX, new v128(scissor.mMaxX)); - bbminY = Arm.Neon.vmaxq_s32(bbminY, new v128(scissor.mMinY)); - bbmaxY = Arm.Neon.vminq_s32(bbmaxY, new v128(scissor.mMaxY)); - } - else - { - bbminX = new v128(); - bbminY = new v128(); - bbmaxX = new v128(); - bbmaxY = new v128(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void ProjectVerticesNEON(v128* ipVtxX, v128* ipVtxY, v128* pVtxX, v128* pVtxY, v128* pVtxZ, v128* vtxX, v128* vtxY, v128* vtxW) - { - if (Arm.Neon.IsNeonSupported) - { - const float FP_INV = 1f / (1 << BufferGroup.FpBits); - // Project vertices and transform to screen space. Snap to sub-pixel coordinates with BufferGroup.FpBits precision. - for (int i = 0; i < 3; i++) - { - int idx = 2 - i; - v128 rcpW; - - if (ProjectionType == BatchCullingProjectionType.Orthographic) - { - rcpW = Arm.Neon.vmlaq_f32(new v128(1.0f), vtxW[i], new v128(-1.0f)); - - v128 screenX = Arm.Neon.vmlaq_f32(PixelCenterX, vtxX[i], HalfWidth); - v128 screenY = Arm.Neon.vmlaq_f32(PixelCenterY, vtxY[i], HalfHeight); - ipVtxX[idx] = Arm.Neon.vcvtnq_s32_f32(Arm.Neon.vmulq_f32(screenX, new v128((float)(1 << BufferGroup.FpBits)))); - ipVtxY[idx] = Arm.Neon.vcvtnq_s32_f32(Arm.Neon.vmulq_f32(screenY, new v128((float)(1 << BufferGroup.FpBits)))); - - } - else - { - rcpW = Arm.Neon.vdivq_f32(new v128(1.0f), vtxW[i]); - - v128 screenX = Arm.Neon.vmlaq_f32(PixelCenterX, Arm.Neon.vmulq_f32(vtxX[i], HalfWidth), rcpW); - v128 screenY = Arm.Neon.vmlaq_f32(PixelCenterY, Arm.Neon.vmulq_f32(vtxY[i], HalfHeight), rcpW); - - ipVtxX[idx] = Arm.Neon.vcvtnq_s32_f32(Arm.Neon.vmulq_f32(screenX, new v128((float)(1 << BufferGroup.FpBits)))); - ipVtxY[idx] = Arm.Neon.vcvtnq_s32_f32(Arm.Neon.vmulq_f32(screenY, new v128((float)(1 << BufferGroup.FpBits)))); - } - - pVtxX[idx] = Arm.Neon.vmulq_f32(Arm.Neon.vcvtq_f32_s32(ipVtxX[idx]), new v128(FP_INV)); - pVtxY[idx] = Arm.Neon.vmulq_f32(Arm.Neon.vcvtq_f32_s32(ipVtxY[idx]), new v128(FP_INV)); - pVtxZ[idx] = rcpW; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void RasterizeTriangleBatchNEON(Tile* tiles, v128* ipVtxX, v128* ipVtxY, v128* pVtxX, v128* pVtxY, v128* pVtxZ, uint triMask, ScissorRect scissor) - { - if (Arm.Neon.IsNeonSupported) - { - v128 one = new v128(1); - v128 zero = new v128(0); - //we are computing the bounding box again when we used it before but there are some use cases after, this check cannot be removed atm - - // Compute bounding box and clamp to tile coordinates - ComputeBoundingBoxNEON(pVtxX, pVtxY, ref scissor, out var bbPixelMinX, out var bbPixelMinY, out var bbPixelMaxX, out var bbPixelMaxY); - - // Clamp bounding box to tiles (it's already padded in computeBoundingBox) - v128 bbTileMinX = Arm.Neon.vshrq_n_s32(bbPixelMinX, BufferGroup.TileWidthShift); - v128 bbTileMinY = Arm.Neon.vshrq_n_s32(bbPixelMinY, BufferGroup.TileHeightShift); - v128 bbTileMaxX = Arm.Neon.vshrq_n_s32(bbPixelMaxX, BufferGroup.TileWidthShift); - v128 bbTileMaxY = Arm.Neon.vshrq_n_s32(bbPixelMaxY, BufferGroup.TileHeightShift); - v128 bbTileSizeX = Arm.Neon.vsubq_s32(bbTileMaxX, bbTileMinX); - v128 bbTileSizeY = Arm.Neon.vsubq_s32(bbTileMaxY, bbTileMinY); - - // Cull triangles with zero bounding box - v128 bboxSign = Arm.Neon.vorrq_s8(Arm.Neon.vsubq_s32(bbTileSizeX, one), Arm.Neon.vsubq_s32(bbTileSizeY, one)); - triMask &= (uint)((~IntrinsicUtils._vmovemask_f32(bboxSign)) & SIMD_ALL_LANES_MASK); - - if (triMask == 0x0) - { - return; // View-culled - } - - // Set up screen space depth plane - ComputeDepthPlaneNEON(pVtxX, pVtxY, pVtxZ, out var zPixelDx, out var zPixelDy); - - // Compute z value at min corner of bounding box. Offset to make sure z is conservative for all 8x4 subtiles - v128 bbMinXV0 = Arm.Neon.vsubq_f32(Arm.Neon.vcvtq_f32_s32(bbPixelMinX), pVtxX[0]); - v128 bbMinYV0 = Arm.Neon.vsubq_f32(Arm.Neon.vcvtq_f32_s32(bbPixelMinY), pVtxY[0]); - v128 zPlaneOffset = Arm.Neon.vmlaq_f32( - Arm.Neon.vmlaq_f32(pVtxZ[0], zPixelDy, bbMinYV0), - zPixelDx, - bbMinXV0); - v128 zTileDx = Arm.Neon.vmulq_f32(zPixelDx, new v128((float)BufferGroup.TileWidth)); - v128 zTileDy = Arm.Neon.vmulq_f32(zPixelDy, new v128((float)BufferGroup.TileHeight)); - - zPlaneOffset = Arm.Neon.vaddq_f32(zPlaneOffset, Arm.Neon.vminq_f32(zero, Arm.Neon.vmulq_f32(zPixelDx, new v128((float)BufferGroup.SubTileWidth)))); - zPlaneOffset = Arm.Neon.vaddq_f32(zPlaneOffset, Arm.Neon.vminq_f32(zero, Arm.Neon.vmulq_f32(zPixelDy, new v128((float)BufferGroup.SubTileHeight)))); - - // Compute Zmin and Zmax for the triangle (used to narrow the range for difficult tiles) - v128 zMin = Arm.Neon.vminq_f32(pVtxZ[0], Arm.Neon.vminq_f32(pVtxZ[1], pVtxZ[2])); - v128 zMax = Arm.Neon.vmaxq_f32(pVtxZ[0], Arm.Neon.vmaxq_f32(pVtxZ[1], pVtxZ[2])); - - /* Sort vertices (v0 has lowest Y, and the rest is in winding order) and compute edges. Also find the middle - vertex and compute tile */ - - // Rotate the triangle in the winding order until v0 is the vertex with lowest Y value - SortVerticesNEON(ipVtxX, ipVtxY); - - // Compute edges - v128* edgeX = stackalloc v128[3]; - edgeX[0] = Arm.Neon.vsubq_s32(ipVtxX[1], ipVtxX[0]); - edgeX[1] = Arm.Neon.vsubq_s32(ipVtxX[2], ipVtxX[1]); - edgeX[2] = Arm.Neon.vsubq_s32(ipVtxX[2], ipVtxX[0]); - - v128* edgeY = stackalloc v128[3]; - edgeY[0] = Arm.Neon.vsubq_s32(ipVtxY[1], ipVtxY[0]); - edgeY[1] = Arm.Neon.vsubq_s32(ipVtxY[2], ipVtxY[1]); - edgeY[2] = Arm.Neon.vsubq_s32(ipVtxY[2], ipVtxY[0]); - - // Classify if the middle vertex is on the left or right and compute its position - int midVtxRight = ~IntrinsicUtils._vmovemask_f32(edgeY[1]); - v128 midPixelX = IntrinsicUtils._vblendq_f32(edgeY[1], ipVtxX[1], ipVtxX[2]); - v128 midPixelY = IntrinsicUtils._vblendq_f32(edgeY[1], ipVtxY[1], ipVtxY[2]); - v128 midTileY = Arm.Neon.vshrq_n_s32(Arm.Neon.vmaxq_s32(midPixelY, zero), BufferGroup.TileHeightShift + BufferGroup.FpBits); - v128 bbMidTileY = Arm.Neon.vmaxq_s32(bbTileMinY, Arm.Neon.vminq_s32(bbTileMaxY, midTileY)); - - // Compute edge events for the bottom of the bounding box, or for the middle tile in case of - // the edge originating from the middle vertex. - v128* xDiffi = stackalloc v128[2]; - xDiffi[0] = Arm.Neon.vsubq_s32(ipVtxX[0], Arm.Neon.vshlq_n_s32(bbPixelMinX, BufferGroup.FpBits)); - xDiffi[1] = Arm.Neon.vsubq_s32(midPixelX, Arm.Neon.vshlq_n_s32(bbPixelMinX, BufferGroup.FpBits)); - - v128* yDiffi = stackalloc v128[2]; - yDiffi[0] = Arm.Neon.vsubq_s32(ipVtxY[0], Arm.Neon.vshlq_n_s32(bbPixelMinY, BufferGroup.FpBits)); - yDiffi[1] = Arm.Neon.vsubq_s32(midPixelY, Arm.Neon.vshlq_n_s32(bbMidTileY, BufferGroup.FpBits + BufferGroup.TileHeightShift)); - - /* Edge slope setup - Note we do not conform to DX/GL rasterization rules */ - - // Potentially flip edge to ensure that all edges have positive Y slope. - edgeX[1] = IntrinsicUtils._vblendq_f32(edgeY[1], edgeX[1], Arm.Neon.vnegq_s32(edgeX[1])); - edgeY[1] = Arm.Neon.vabsq_s32(edgeY[1]); - - // Compute floating point slopes - v128* slope = stackalloc v128[3]; - slope[0] = Arm.Neon.vdivq_f32(Arm.Neon.vcvtq_f32_s32(edgeX[0]), Arm.Neon.vcvtq_f32_s32(edgeY[0])); - slope[1] = Arm.Neon.vdivq_f32(Arm.Neon.vcvtq_f32_s32(edgeX[1]), Arm.Neon.vcvtq_f32_s32(edgeY[1])); - slope[2] = Arm.Neon.vdivq_f32(Arm.Neon.vcvtq_f32_s32(edgeX[2]), Arm.Neon.vcvtq_f32_s32(edgeY[2])); - - // Modify slope of horizontal edges to make sure they mask out pixels above/below the edge. The slope is set to screen - // width to mask out all pixels above or below the horizontal edge. We must also add a small bias to acount for that - // vertices may end up off screen due to clipping. We're assuming that the round off error is no bigger than 1.0 - v128 horizontalSlopeDelta = new v128(2f * (NumPixelsX + 2f * (BufferGroup.GuardBandPixelSize + 1.0f))); - v128 horizontalSlope0 = Arm.Neon.vceqzq_s32(edgeY[0]); - v128 horizontalSlope1 = Arm.Neon.vceqzq_s32(edgeY[1]); - slope[0] = IntrinsicUtils._vblendq_f32(horizontalSlope0, slope[0], horizontalSlopeDelta); - slope[1] = IntrinsicUtils._vblendq_f32(horizontalSlope1, slope[1], Arm.Neon.vnegq_f32(horizontalSlopeDelta)); - - v128* vy = stackalloc v128[3]; - vy[0] = yDiffi[0]; - vy[1] = yDiffi[1]; - vy[2] = yDiffi[0]; - - v128 offset0 = Arm.Neon.vandq_s8(Arm.Neon.vaddq_s32(yDiffi[0], new v128(BufferGroup.FpHalfPixel - 1)), new v128((-1 << BufferGroup.FpBits))); - v128 offset1 = Arm.Neon.vandq_s8(Arm.Neon.vaddq_s32(yDiffi[1], new v128(BufferGroup.FpHalfPixel - 1)), new v128((-1 << BufferGroup.FpBits))); - vy[0] = IntrinsicUtils._vblendq_f32(horizontalSlope0, yDiffi[0], offset0); - vy[1] = IntrinsicUtils._vblendq_f32(horizontalSlope1, yDiffi[1], offset1); - - // Compute edge events for the bottom of the bounding box, or for the middle tile in case of - // the edge originating from the middle vertex. - v128* slopeSign = stackalloc v128[3]; - v128* absEdgeX = stackalloc v128[3]; - v128* slopeTileDelta = stackalloc v128[3]; - v128* eventStartRemainder = stackalloc v128[3]; - v128* slopeTileRemainder = stackalloc v128[3]; - v128* eventStart = stackalloc v128[3]; - - for (int i = 0; i < 3; i++) - { - // Common, compute slope sign (used to propagate the remainder term when overflowing) is postive or negative x-direction - slopeSign[i] = IntrinsicUtils._vblendq_f32(edgeX[i], new v128(1), new v128(-1)); - absEdgeX[i] = Arm.Neon.vabsq_s32(edgeX[i]); - - // Delta and error term for one vertical tile step. The exact delta is exactDelta = edgeX / edgeY, due to limited precision we - // repersent the delta as delta = qoutient + remainder / edgeY, where quotient = int(edgeX / edgeY). In this case, since we step - // one tile of scanlines at a time, the slope is computed for a tile-sized step. - slopeTileDelta[i] = Arm.Neon.vcvtnq_s32_f32(Arm.Neon.vmulq_f32(slope[i], new v128((float)BufferGroup.FpTileHeight))); - slopeTileRemainder[i] = Arm.Neon.vsubq_s32(Arm.Neon.vshlq_n_s32(absEdgeX[i], BufferGroup.FpTileHeightShift), Arm.Neon.vmulq_u32(Arm.Neon.vabsq_s32(slopeTileDelta[i]), edgeY[i])); - - // Jump to bottom scanline of tile row, this is the bottom of the bounding box, or the middle vertex of the triangle. - // The jump can be in both positive and negative y-direction due to clipping / offscreen vertices. - v128 tileStartDir = IntrinsicUtils._vblendq_f32(vy[i], slopeSign[i], Arm.Neon.vnegq_s32(slopeSign[i])); - v128 tieBreaker = IntrinsicUtils._vblendq_f32(tileStartDir, zero, one); - v128 tileStartSlope = Arm.Neon.vcvtnq_s32_f32(Arm.Neon.vmulq_f32(slope[i], Arm.Neon.vcvtq_f32_s32(Arm.Neon.vnegq_s32(vy[i])))); - v128 tileStartRemainder = Arm.Neon.vsubq_s32(Arm.Neon.vmulq_u32(absEdgeX[i], Arm.Neon.vabsq_s32(vy[i])), Arm.Neon.vmulq_u32(Arm.Neon.vabsq_s32(tileStartSlope), edgeY[i])); - - eventStartRemainder[i] = Arm.Neon.vsubq_s32(tileStartRemainder, tieBreaker); - v128 overflow = Arm.Neon.vshrq_n_s32(eventStartRemainder[i], 31); - eventStartRemainder[i] = Arm.Neon.vaddq_s32(eventStartRemainder[i], Arm.Neon.vandq_s8(overflow, edgeY[i])); - eventStartRemainder[i] = IntrinsicUtils._vblendq_f32(vy[i], eventStartRemainder[i], Arm.Neon.vsubq_s32(Arm.Neon.vsubq_s32(edgeY[i], eventStartRemainder[i]), one)); - - //eventStart[i] = xDiffi[i & 1] + tileStartSlope + (overflow & tileStartDir) + X86.Sse2.set1_epi32(FP_HALF_PIXEL - 1) + tieBreaker; - eventStart[i] = Arm.Neon.vaddq_s32(Arm.Neon.vaddq_s32(xDiffi[i & 1], tileStartSlope), Arm.Neon.vandq_s8(overflow, tileStartDir)); - eventStart[i] = Arm.Neon.vaddq_s32(Arm.Neon.vaddq_s32(eventStart[i], new v128(BufferGroup.FpHalfPixel - 1)), tieBreaker); - } - - // Split bounding box into bottom - middle - top region. - v128 bbBottomIdx = Arm.Neon.vaddq_s32(bbTileMinX, Arm.Neon.vmulq_u32(bbTileMinY, new v128(NumTilesX))); - v128 bbTopIdx = Arm.Neon.vaddq_s32(bbTileMinX, Arm.Neon.vmulq_u32(Arm.Neon.vaddq_s32(bbTileMinY, bbTileSizeY), new v128(NumTilesX))); - v128 bbMidIdx = Arm.Neon.vaddq_s32(bbTileMinX, Arm.Neon.vmulq_u32(midTileY, new v128(NumTilesX))); - - // Loop over non-culled triangle and change SIMD axis to per-pixel - while (triMask != 0) - { - uint triIdx = (uint)IntrinsicUtils.find_clear_lsb(ref triMask); - int triMidVtxRight = (midVtxRight >> (int)triIdx) & 1; - - // Get Triangle Zmin zMax - v128 zTriMax = new v128(IntrinsicUtils.getFloatLane(zMax, triIdx)); - v128 zTriMin = new v128(IntrinsicUtils.getFloatLane(zMin, triIdx)); - - // Setup Zmin value for first set of 8x4 subtiles - v128 SimdSubTileColOffsetF = new v128(0f, BufferGroup.SubTileWidth, BufferGroup.SubTileWidth * 2, BufferGroup.SubTileWidth * 3); - v128 z0 = Arm.Neon.vmlaq_f32(Arm.Neon.vmlaq_f32( - new v128(IntrinsicUtils.getFloatLane(zPlaneOffset, triIdx)), - new v128(IntrinsicUtils.getFloatLane(zPixelDy, triIdx)), - zero), - new v128(IntrinsicUtils.getFloatLane(zPixelDx, triIdx)), - SimdSubTileColOffsetF); - - float zx = IntrinsicUtils.getFloatLane(zTileDx, triIdx); - float zy = IntrinsicUtils.getFloatLane(zTileDy, triIdx); - - // Get dimension of bounding box bottom, mid & top segments - int bbWidth = IntrinsicUtils.getIntLane(bbTileSizeX, triIdx); - int bbHeight = IntrinsicUtils.getIntLane(bbTileSizeY, triIdx); - int tileRowIdx = IntrinsicUtils.getIntLane(bbBottomIdx, triIdx); - int tileMidRowIdx = IntrinsicUtils.getIntLane(bbMidIdx, triIdx); - int tileEndRowIdx = IntrinsicUtils.getIntLane(bbTopIdx, triIdx); - - if (bbWidth > BIG_TRIANGLE && bbHeight > BIG_TRIANGLE) // For big triangles we use a more expensive but tighter traversal algorithm - { - if (triMidVtxRight != 0) - { - RasterizeTriangleNEON(tiles, true, 1, triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, ref z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); - } - else - { - RasterizeTriangleNEON(tiles, true, 0, triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, ref z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); - } - } - else - { - if (triMidVtxRight != 0) - { - RasterizeTriangleNEON(tiles, false, 1, triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, ref z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); - } - else - { - RasterizeTriangleNEON(tiles, false, 0, triIdx, bbWidth, tileRowIdx, tileMidRowIdx, tileEndRowIdx, eventStart, slope, slopeTileDelta, zTriMin, zTriMax, ref z0, zx, zy, edgeY, absEdgeX, slopeSign, eventStartRemainder, slopeTileRemainder); - } - } - } - } - } - - // RasterizeMesh now gets as input all triangles that already passed backface culling, clipping and an early z test - // this should make it even easier for the system to keep using the full simd words and have better simd occupancy - // instead of removing part of the work based on the mask - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void RasterizeMesh(Tile* tiles, float* binTriangleX, float* binTriangleY, float* binTriangleW, int numVert, ScissorRect screenScissor) - { - X86.MXCSRBits OldBits = X86.MXCSRBits.RoundToNearest; - if (X86.Sse2.IsSse2Supported) - { - // Intel implementation needs a rounding workaround - OldBits = X86.MXCSR; - // DS: TODO: UNITY BURST FIX - //using (var roundingMode = new X86.RoundingScope(X86.MXCSRBits.RoundToNearest)) - const X86.MXCSRBits roundingMode = X86.MXCSRBits.RoundToNearest; - X86.MXCSR = (OldBits & ~X86.MXCSRBits.RoundingControlMask) | roundingMode; - } - - int vertexIndex = 0; - - v128* vtxX_prealloc = stackalloc v128[3]; - v128* vtxY_prealloc = stackalloc v128[3]; - v128* vtxW_prealloc = stackalloc v128[3]; - - v128* pVtxX_prealloc = stackalloc v128[3]; - v128* pVtxY_prealloc = stackalloc v128[3]; - v128* pVtxZ_prealloc = stackalloc v128[3]; - - v128* ipVtxX_prealloc = stackalloc v128[3]; - v128* ipVtxY_prealloc = stackalloc v128[3]; - - while (vertexIndex < numVert) - { - v128* vtxX = vtxX_prealloc; - v128* vtxY = vtxY_prealloc; - v128* vtxW = vtxW_prealloc; - - int numLanes = math.min(SIMD_LANES, numVert - vertexIndex); - uint triMask = (1u << numLanes) - 1; - - for (int i = 0; i < 3; i++) - { - if (X86.Sse2.IsSse2Supported) - { - vtxX[i] = X86.Sse.load_ps(&binTriangleX[vertexIndex + i * 4]); - vtxY[i] = X86.Sse.load_ps(&binTriangleY[vertexIndex + i * 4]); - vtxW[i] = X86.Sse.load_ps(&binTriangleW[vertexIndex + i * 4]); - } - else if (Arm.Neon.IsNeonSupported) - { - vtxX[i] = Arm.Neon.vld1q_f32(&binTriangleX[vertexIndex + i * 4]); - vtxY[i] = Arm.Neon.vld1q_f32(&binTriangleY[vertexIndex + i * 4]); - vtxW[i] = Arm.Neon.vld1q_f32(&binTriangleW[vertexIndex + i * 4]); - } - } - - vertexIndex += SIMD_LANES * 3; - - if (triMask == 0x0) - { - continue; - } - - /* Project and transform to screen space. Note - that we use z = 1.0 / vtx.w for depth, which means that z = 0 is far and - z = 1/m_near is near. For ortho projection, we do z = (z * -1) + 1 to go from z = 0 for far and z = 2 for near - - We must also use a greater than depth test, and in effect - everything is reversed compared to regular z implementations. */ - - v128* pVtxX = pVtxX_prealloc; - v128* pVtxY = pVtxY_prealloc; - v128* pVtxZ = pVtxZ_prealloc; - - v128* ipVtxX = ipVtxX_prealloc; - v128* ipVtxY = ipVtxY_prealloc; - if (X86.Sse2.IsSse2Supported) - { - ProjectVerticesSSE(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); - } - else if (Arm.Neon.IsNeonSupported) - { - ProjectVerticesNEON(ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, vtxX, vtxY, vtxW); - } - - /* Setup and rasterize a SIMD batch of triangles */ - if (X86.Sse2.IsSse2Supported) - { - RasterizeTriangleBatchSSE(tiles, ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, triMask, screenScissor); - // Don't forget to restore the rounding mode - // DS: TODO: UNITY BURST FIX - X86.MXCSR = OldBits; - } - else if (Arm.Neon.IsNeonSupported) - { - RasterizeTriangleBatchNEON(tiles, ipVtxX, ipVtxY, pVtxX, pVtxY, pVtxZ, triMask, screenScissor); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void RasterizeTriangleNEON( - Tile* tiles, - bool isTightTraversal, - int midVtxRight, - uint triIdx, - int bbWidth, - int tileRowIdx, - int tileMidRowIdx, - int tileEndRowIdx, - v128* eventStart, - v128* slope, - v128* slopeTileDelta, - v128 zTriMin, - v128 zTriMax, - ref v128 z0, - float zx, - float zy, - v128* edgeY, - v128* absEdgeX, - v128* slopeSign, - v128* eventStartRemainder, - v128* slopeTileRemainder) - { - if (Arm.Neon.IsNeonSupported) - { - const int LEFT_EDGE_BIAS = -1; - const int RIGHT_EDGE_BIAS = 1; - - v128* triSlopeSign = stackalloc v128[3]; - v128* triSlopeTileDelta = stackalloc v128[3]; - v128* triEdgeY = stackalloc v128[3]; - v128* triSlopeTileRemainder = stackalloc v128[3]; - v128* triEventRemainder = stackalloc v128[3]; - v128* triEvent = stackalloc v128[3]; - - for (int i = 0; i < 3; ++i) - { - triSlopeSign[i] = new v128(IntrinsicUtils.getIntLane(slopeSign[i], triIdx)); - triSlopeTileDelta[i] = new v128(IntrinsicUtils.getIntLane(slopeTileDelta[i], triIdx)); - triEdgeY[i] = new v128(IntrinsicUtils.getIntLane(edgeY[i], triIdx)); - triSlopeTileRemainder[i] = new v128(IntrinsicUtils.getIntLane(slopeTileRemainder[i], triIdx)); - - v128 triSlope = new v128(IntrinsicUtils.getFloatLane(slope[i], triIdx)); - v128 triAbsEdgeX = new v128(IntrinsicUtils.getIntLane(absEdgeX[i], triIdx)); - v128 triStartRemainder = new v128(IntrinsicUtils.getIntLane(eventStartRemainder[i], triIdx)); - v128 triEventStart = new v128(IntrinsicUtils.getIntLane(eventStart[i], triIdx)); - - v128 SimdLaneYCoordF = new v128(128f, 384f, 640f, 896f); - v128 scanlineDelta = Arm.Neon.vcvtnq_s32_f32(Arm.Neon.vmulq_f32(triSlope, SimdLaneYCoordF)); - v128 SimdLaneYCoordI = new v128(128, 384, 640, 896); - v128 scanlineSlopeRemainder = Arm.Neon.vsubq_s32(Arm.Neon.vmulq_u32(triAbsEdgeX, SimdLaneYCoordI), Arm.Neon.vmulq_u32(Arm.Neon.vabsq_s32(scanlineDelta), triEdgeY[i])); - - triEventRemainder[i] = Arm.Neon.vsubq_s32(triStartRemainder, scanlineSlopeRemainder); - v128 overflow = Arm.Neon.vshrq_n_s32(triEventRemainder[i], 31); - triEventRemainder[i] = Arm.Neon.vaddq_s32(triEventRemainder[i], Arm.Neon.vandq_s8(overflow, triEdgeY[i])); - triEvent[i] = Arm.Neon.vaddq_s32(Arm.Neon.vaddq_s32(triEventStart, scanlineDelta), Arm.Neon.vandq_s8(overflow, triSlopeSign[i])); - } - - // For big triangles track start & end tile for each scanline and only traverse the valid region - int startDelta = 0; - int endDelta = 0; - int topDelta = 0; - int startEvent = 0; - int endEvent = 0; - int topEvent = 0; - - if (isTightTraversal) - { - startDelta = IntrinsicUtils.getIntLane(slopeTileDelta[2], triIdx) + LEFT_EDGE_BIAS; - endDelta = IntrinsicUtils.getIntLane(slopeTileDelta[0], triIdx) + RIGHT_EDGE_BIAS; - topDelta = IntrinsicUtils.getIntLane(slopeTileDelta[1], triIdx) + - (midVtxRight != 0 ? RIGHT_EDGE_BIAS : LEFT_EDGE_BIAS); - - // Compute conservative bounds for the edge events over a 32xN tile - startEvent = IntrinsicUtils.getIntLane(eventStart[2], triIdx) + Mathf.Min(0, startDelta); - endEvent = IntrinsicUtils.getIntLane(eventStart[0], triIdx) + Mathf.Max(0, endDelta) + - (BufferGroup.TileWidth << BufferGroup.FpBits); // TODO: (Apoorva) can be spun out into a const - - if (midVtxRight != 0) - { - topEvent = IntrinsicUtils.getIntLane(eventStart[1], triIdx) + Mathf.Max(0, topDelta) + - (BufferGroup.TileWidth << BufferGroup.FpBits); // TODO: (Apoorva) can be spun out into a const - } - else - { - topEvent = IntrinsicUtils.getIntLane(eventStart[1], triIdx) + Mathf.Min(0, topDelta); - } - } - - if (tileRowIdx <= tileMidRowIdx) - { - int tileStopIdx = Mathf.Min(tileEndRowIdx, tileMidRowIdx); - - // Traverse the bottom half of the triangle - while (tileRowIdx < tileStopIdx) - { - int start = 0; - int end = bbWidth; - - if (isTightTraversal) - { - // Compute tighter start and endpoints to avoid traversing empty space - start = Mathf.Max(0, Mathf.Min(bbWidth - 1, startEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - end = Mathf.Min(bbWidth, (endEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - - startEvent += startDelta; - endEvent += endDelta; - } - - // Traverse the scanline and update the masked hierarchical z buffer - TraverseScanlineNEON(tiles, 1, 1, start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, z0, zx); - - // move to the next scanline of tiles, update edge events and interpolate z - tileRowIdx += NumTilesX; - z0 = Arm.Neon.vaddq_f32(z0, new v128(zy)); - - UpdateTileEventsYNEON(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, 0); - UpdateTileEventsYNEON(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, 2); - } - - // Traverse the middle scanline of tiles. We must consider all three edges only in this region - if (tileRowIdx < tileEndRowIdx) - { - int start = 0; - int end = bbWidth; - - if (isTightTraversal) - { - // Compute tighter start and endpoints to avoid traversing lots of empty space - start = Mathf.Max(0, Mathf.Min(bbWidth - 1, startEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - end = Mathf.Min(bbWidth, (endEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - - // Switch the traversal start / end to account for the upper side edge - endEvent = midVtxRight != 0 ? topEvent : endEvent; - endDelta = midVtxRight != 0 ? topDelta : endDelta; - startEvent = midVtxRight != 0 ? startEvent : topEvent; - startDelta = midVtxRight != 0 ? startDelta : topDelta; - - startEvent += startDelta; - endEvent += endDelta; - } - - // Traverse the scanline and update the masked hierarchical z buffer. - if (midVtxRight != 0) - { - TraverseScanlineNEON(tiles, 2, 1, start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, - z0, zx); - } - else - { - TraverseScanlineNEON(tiles, 1, 2, start, end, tileRowIdx, 0, 2, triEvent, zTriMin, zTriMax, - z0, zx); - } - - tileRowIdx += NumTilesX; - } - - // Traverse the top half of the triangle - if (tileRowIdx < tileEndRowIdx) - { - // move to the next scanline of tiles, update edge events and interpolate z - z0 = Arm.Neon.vaddq_f32(z0, new v128(zy)); - int i0 = midVtxRight + 0; - int i1 = midVtxRight + 1; - - UpdateTileEventsYNEON(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i0); - UpdateTileEventsYNEON(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i1); - - for (; ; ) - { - int start = 0; - int end = bbWidth; - - if (isTightTraversal) - { - // Compute tighter start and endpoints to avoid traversing lots of empty space - start = Mathf.Max(0, Mathf.Min(bbWidth - 1, startEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - end = Mathf.Min(bbWidth, (endEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - - startEvent += startDelta; - endEvent += endDelta; - } - - // Traverse the scanline and update the masked hierarchical z buffer - TraverseScanlineNEON(tiles, 1, 1, start, end, tileRowIdx, midVtxRight + 0, - midVtxRight + 1, triEvent, zTriMin, zTriMax, z0, zx); - - // move to the next scanline of tiles, update edge events and interpolate z - tileRowIdx += NumTilesX; - if (tileRowIdx >= tileEndRowIdx) - { - break; - } - - z0 = Arm.Neon.vaddq_f32(z0, new v128(zy)); - - UpdateTileEventsYNEON(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i0); - UpdateTileEventsYNEON(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i1); - } - } - } - else - { - if (isTightTraversal) - { - // For large triangles, switch the traversal start / end to account for the upper side edge - endEvent = midVtxRight != 0 ? topEvent : endEvent; - endDelta = midVtxRight != 0 ? topDelta : endDelta; - startEvent = midVtxRight != 0 ? startEvent : topEvent; - startDelta = midVtxRight != 0 ? startDelta : topDelta; - } - - // Traverse the top half of the triangle - if (tileRowIdx < tileEndRowIdx) - { - int i0 = midVtxRight + 0; - int i1 = midVtxRight + 1; - - for (; ; ) - { - int start = 0; - int end = bbWidth; - - if (isTightTraversal) - { - // Compute tighter start and endpoints to avoid traversing lots of empty space - start = Mathf.Max(0, Mathf.Min(bbWidth - 1, startEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - end = Mathf.Min(bbWidth, (endEvent >> (BufferGroup.TileWidthShift + BufferGroup.FpBits))); - - startEvent += startDelta; - endEvent += endDelta; - } - - // Traverse the scanline and update the masked hierarchical z buffer - TraverseScanlineNEON(tiles, 1, 1, start, end, tileRowIdx, midVtxRight + 0, - midVtxRight + 1, triEvent, zTriMin, zTriMax, z0, zx); - - // move to the next scanline of tiles, update edge events and interpolate z - tileRowIdx += NumTilesX; - if (tileRowIdx >= tileEndRowIdx) - { - break; - } - - z0 = Arm.Neon.vaddq_f32(z0, new v128(zy)); - - UpdateTileEventsYNEON(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i0); - UpdateTileEventsYNEON(triEventRemainder, triSlopeTileRemainder, triEdgeY, triEvent, - triSlopeTileDelta, triSlopeSign, i1); - } - } - } - } - } - #endregion - - public void Execute(int i) - { - var tiles = &TilesBasePtr[0]; - ScissorRect scissorRect = new ScissorRect(); - - int2 pixelsPerTile = new int2(NumPixelsX / NumTilesX, NumPixelsY / NumTilesY); - - float* temp_stack_x = stackalloc float[12]; - float* temp_stack_y = stackalloc float[12]; - float* temp_stack_w = stackalloc float[12]; - int tempStackSize = 0; - - int countOfTilesX = NumTilesX / TilesPerBinX; - scissorRect.mMinX = (i % countOfTilesX) * pixelsPerTile.x * TilesPerBinX; - scissorRect.mMaxX = scissorRect.mMinX + pixelsPerTile.x * TilesPerBinX; - scissorRect.mMinY = (i / countOfTilesX) * pixelsPerTile.y * TilesPerBinY; - scissorRect.mMaxY = scissorRect.mMinY + pixelsPerTile.y * TilesPerBinY; - - float4 clipRect = new float4(scissorRect.mMinX, scissorRect.mMinY, scissorRect.mMaxX, scissorRect.mMaxY); - clipRect = (2 * clipRect.xyzw / (new float2(NumPixelsX, NumPixelsY).xyxy) - 1); - - int bufferIndex = ( WorkerIndex - 1 ); - float* binTriangleX = (float*)BinTriangleXBasePtr + BinSize * bufferIndex; - float* binTriangleY = (float*)BinTriangleYBasePtr + BinSize * bufferIndex; - float* binTriangleW = (float*)BinTriangleWBasePtr + BinSize * bufferIndex; - - // For each mesh - // if the mesh aabb is inside the bin aabb - // check all each triangle and test against the bin aabb - // if inside the bin, add in, once the bin is full render it - // once the loop finish, render the remaining triangles in the bin - int internalBinSize = 0; - for (int m = 0; m < ClippedOccluders.Length; m += 1) - { - float2 max = ClippedOccluders[m].screenMax.xy; - float2 min = ClippedOccluders[m].screenMin.xy; - - if (math.any(min > clipRect.zw) || math.any(max < clipRect.xy)) - continue; - - ClippedOccluder clipped = ClippedOccluders[m]; - - int k = 0; - for (int j = 0; j < clipped.expandedVertexSize; j += 3, ++k) - { - float4 triExtents = ClippedTriExtents[clipped.sourceIndexOffset * 2 + k]; - min = triExtents.xy; - max = triExtents.zw; - - if (math.any(min > clipRect.zw) || math.any(max < clipRect.xy)) - continue; - - for (int n = 0; n < 3; ++n) - { - float3 vert = ClippedVerts[clipped.sourceIndexOffset * 6 + j + n]; - temp_stack_x[tempStackSize] = vert.x; - temp_stack_y[tempStackSize] = vert.y; - temp_stack_w[tempStackSize] = vert.z; - tempStackSize++; - } - - if (tempStackSize == 12) - { - for (int n = 0; n < 3; ++n) - { - for (int p = 0; p < 4; ++p) - { - binTriangleX[internalBinSize + p + n * 4] = temp_stack_x[n + p * 3]; - binTriangleY[internalBinSize + p + n * 4] = temp_stack_y[n + p * 3]; - binTriangleW[internalBinSize + p + n * 4] = temp_stack_w[n + p * 3]; - } - } - internalBinSize += 12; - tempStackSize = 0; - } - if (internalBinSize == BinSize) - { - RasterizeMesh(tiles, binTriangleX, binTriangleY, binTriangleW, internalBinSize, scissorRect); - internalBinSize = 0; - } - } - } - if (tempStackSize > 0) - { - for (int n = 0; n < 3; ++n) - { - for (int p = 0; p < 4; ++p) - { - binTriangleX[internalBinSize + p + n * 4] = temp_stack_x[n + p * 3]; - binTriangleY[internalBinSize + p + n * 4] = temp_stack_y[n + p * 3]; - binTriangleW[internalBinSize + p + n * 4] = temp_stack_w[n + p * 3]; - } - } - internalBinSize += tempStackSize; - tempStackSize = 0; - } - if (internalBinSize > 0) - { - RasterizeMesh(tiles, binTriangleX, binTriangleY, binTriangleW, internalBinSize, scissorRect); - } - } - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/RasterizeJob.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/RasterizeJob.cs.meta deleted file mode 100644 index d06ff30..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/RasterizeJob.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5a589b429ed544146a99a8800d8f8e10 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/TestJob.cs b/Unity.Entities.Graphics/Occlusion/Masked/TestJob.cs deleted file mode 100644 index fa7a41f..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/TestJob.cs +++ /dev/null @@ -1,463 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using System.Runtime.CompilerServices; -using Unity.Burst; -using Unity.Burst.Intrinsics; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Entities; -using Unity.Jobs; -using Unity.Mathematics; -using UnityEngine.Rendering; -using Unity.Rendering.Occlusion.Masked.Dots; - -namespace Unity.Rendering.Occlusion.Masked -{ - [BurstCompile] - unsafe struct TestJob : IJobParallelForDefer - { - public IndirectList VisibilityItems; - [ReadOnly] public ComponentTypeHandle ChunkHeader; - [ReadOnly] public ComponentTypeHandle OcclusionTest; - [ReadOnly] public ComponentTypeHandle ChunkOcclusionTest; - [ReadOnly] public ComponentTypeHandle EntitiesGraphicsChunkInfo; - [ReadOnly] public BatchCullingProjectionType ProjectionType; - [ReadOnly] public int NumTilesX; - [ReadOnly] public v128 HalfSize; - [ReadOnly] public v128 PixelCenter; - [ReadOnly] public v128 ScreenSize; - [ReadOnly] public BatchCullingViewType ViewType; - [ReadOnly] public int SplitIndex; - [ReadOnly, NativeDisableUnsafePtrRestriction] public Tile* Tiles; - [ReadOnly] public bool DisplayOnlyOccluded; - - public void Execute(int index) - { - var visibilityItem = VisibilityItems.ElementAt(index); - var chunk = visibilityItem.Chunk; - var chunkVisibility = visibilityItem.Visibility; - - if (!chunk.HasChunkComponent(ref ChunkOcclusionTest)) - { - /* Because this is not a IJobChunk job, it will not filter by archetype. So there is no guarantee that - the current chunk has any occlusion test jobs on it. */ - return; - } - - var hybridChunkInfo = chunk.GetChunkComponentData(ref EntitiesGraphicsChunkInfo); - if (!hybridChunkInfo.Valid) - return; - - var anyLodEnabled = ( - hybridChunkInfo.CullingData.InstanceLodEnableds.Enabled[0] | - hybridChunkInfo.CullingData.InstanceLodEnableds.Enabled[1] - ) != 0; - - if (!anyLodEnabled) - { - /* Cull the whole chunk if no LODs are enabled */ - chunkVisibility->VisibleEntities[0] = 0; - chunkVisibility->VisibleEntities[1] = 0; - return; - } - - var chunkTest = chunk.GetChunkComponentData(ref ChunkOcclusionTest); - CullingResult chunkCullingResult = TestRect( - chunkTest.screenMin.xy, - chunkTest.screenMax.xy, - chunkTest.screenMin.w, - Tiles, - ProjectionType, - NumTilesX, - ScreenSize, - HalfSize, - PixelCenter - ); - - bool chunkVisible = (chunkCullingResult == CullingResult.VISIBLE); - /* If we want to invert occlusion for debug purposes, we want to draw _only_ occluded entities. For this, we - want to run occlusion on every chunk, regardless of that chunk's test. A clearer but branch-ey way to - write this is: - - if (DisplayOnlyOccluded) { - chunkVisible = true; - } */ - chunkVisible |= DisplayOnlyOccluded; - if (!chunkVisible) - { - /* The chunk's bounding box fails the visibility test, which means that it's either frustum culled or - occlusion culled. Cull the whole chunk and early out. */ - if (ViewType != BatchCullingViewType.Light) - { - // When culling light views, we don't zero out the visible entities - // because the entity might be visible in a different split. This is - // not the case for other view types, so we must zero them out here. - chunkVisibility->VisibleEntities[0] = 0; - chunkVisibility->VisibleEntities[1] = 0; - } - return; - } - - var tests = chunk.GetNativeArray(ref OcclusionTest); - - /* Each chunk is guaranteed to have no more than 128 entities. So the Entities Graphics package uses `VisibleEntities`, - which is an array of two 64-bit integers to indicate whether each of these entities is visible. */ - for (int j = 0; j < 2; j++) - { - /* The pending bitfield indicates which incoming entities are to be occlusion-tested. - - If a bit is zero, the corresponding entity is already not drawn by a previously run system; e.g. it - might be frustum culled. So there's no need to process it further. - - If a bit is one, the corresponding entity needs to be occlusion-tested. */ - var pendingBitfield = chunkVisibility->VisibleEntities[j]; - ulong newBitfield = 0; - - /* Once the whole pending bitfield is zero, we don't need to do any more occlusion tests */ - while (pendingBitfield != 0) - { - /* Get the index of the first visible entity using tzcnt. For example: - - pendingBitfield = ...0000 0000 0000 1010 0000 - ▲ ▲ ▲ - │ │ │ - `leading zeros │ `trailing zeros - `tzcount = 5 - - Then add (j << 6) to it, which adds 64 if we're in the second bitfield, i.e. if we're covering - entities [65, 128]. - */ - var tzIndex = math.tzcnt(pendingBitfield); - var entityIndex = (j << 6) + tzIndex; - - /* If the view type is a light, then we check to see whether the current entity is already culled - in the current split. If the view type is not a light, we ignore the split mask and proceed to - occlusion cull. */ - bool entityAlreadyFrustumCulled = - ViewType == BatchCullingViewType.Light && - ((chunkVisibility->SplitMasks[entityIndex] & (1 << SplitIndex)) == 0); - - bool entityVisible = false; - if (!entityAlreadyFrustumCulled) - { - CullingResult result = TestRect( - tests[entityIndex].screenMin.xy, - tests[entityIndex].screenMax.xy, - tests[entityIndex].screenMin.w, - Tiles, - ProjectionType, - NumTilesX, - ScreenSize, - HalfSize, - PixelCenter - ); - - entityVisible = (result == CullingResult.VISIBLE); - } - - /* This effectively XORs the two booleans, and only flips visible when the inversion boolean is true. A - clearer but branch-ey way to write this is: - - if (DisplayOnlyOccluded) { - entityVisible = !entityVisible; - } */ - entityVisible = (entityVisible != DisplayOnlyOccluded); - /* Set the index we just processed to zero, indicating that it's not pending any more */ - pendingBitfield ^= 1ul << tzIndex; - /* Set entity's visibility according to our occlusion test */ - newBitfield |= (entityVisible ? 1UL : 0) << tzIndex; - - if (!entityAlreadyFrustumCulled && !entityVisible) - { - /* Set the current split's bit to zero */ - chunkVisibility->SplitMasks[entityIndex] &= (byte)~(1 << SplitIndex); - } - } - - if (ViewType != BatchCullingViewType.Light) - { - chunkVisibility->VisibleEntities[j] = newBitfield; - } - else - { - /* TODO: This will incur a bit of extra work later down the line in case of lights. It won't be too - much work because the splitMasks will indicate whether any split contains the entity. - This code will change once we handle all splits in the same job */ - chunkVisibility->VisibleEntities[j] |= newBitfield; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static CullingResult TestRectSSE( - float2 min, - float2 max, - float wmin, - Tile* tiles, - BatchCullingProjectionType projectionType, - int NumTilesX, - v128 ScreenSize, - v128 HalfSize, - v128 PixelCenter - ) - { - if (X86.Sse4_1.IsSse41Supported) - { - // Compute screen space bounding box and guard for out of bounds - v128 pixelBBox = IntrinsicUtils._mmw_fmadd_ps(X86.Sse.setr_ps(min.x, max.x, max.y, min.y), HalfSize, PixelCenter); - v128 pixelBBoxi = X86.Sse2.cvttps_epi32(pixelBBox); - pixelBBoxi = X86.Sse4_1.max_epi32(X86.Sse2.setzero_si128(), X86.Sse4_1.min_epi32(ScreenSize, pixelBBoxi)); - - // Pad bounding box to (32xN) tiles. Tile BB is used for looping / traversal - v128 SimdTilePad = X86.Sse2.setr_epi32(0, BufferGroup.TileWidth, 0, BufferGroup.TileHeight); - v128 SimdTilePadMask = X86.Sse2.setr_epi32( - ~(BufferGroup.TileWidth - 1), - ~(BufferGroup.TileWidth - 1), - ~(BufferGroup.TileHeight - 1), - ~(BufferGroup.TileHeight - 1) - ); - v128 tileBBoxi = X86.Sse2.and_si128(X86.Sse2.add_epi32(pixelBBoxi, SimdTilePad), SimdTilePadMask); - - int txMin = tileBBoxi.SInt0 >> BufferGroup.TileWidthShift; - int txMax = tileBBoxi.SInt1 >> BufferGroup.TileWidthShift; - int tileRowIdx = (tileBBoxi.SInt2 >> BufferGroup.TileHeightShift) * NumTilesX; - int tileRowIdxEnd = (tileBBoxi.SInt3 >> BufferGroup.TileHeightShift) * NumTilesX; - - // Pad bounding box to (8x4) subtiles. Skip SIMD lanes outside the subtile BB - v128 SimdSubTilePad = X86.Sse2.setr_epi32(0, BufferGroup.SubTileWidth, 0, BufferGroup.SubTileHeight); - v128 SimdSubTilePadMask = X86.Sse2.setr_epi32( - ~(BufferGroup.SubTileWidth - 1), - ~(BufferGroup.SubTileWidth - 1), - ~(BufferGroup.SubTileHeight - 1), - ~(BufferGroup.SubTileHeight - 1) - ); - v128 subTileBBoxi = X86.Sse2.and_si128(X86.Sse2.add_epi32(pixelBBoxi, SimdSubTilePad), SimdSubTilePadMask); - - v128 stxmin = X86.Sse2.set1_epi32(subTileBBoxi.SInt0 - 1); // - 1 to be able to use GT test - v128 stymin = X86.Sse2.set1_epi32(subTileBBoxi.SInt2 - 1); // - 1 to be able to use GT test - v128 stxmax = X86.Sse2.set1_epi32(subTileBBoxi.SInt1); - v128 stymax = X86.Sse2.set1_epi32(subTileBBoxi.SInt3); - - // Setup pixel coordinates used to discard lanes outside subtile BB - v128 SimdSubTileColOffset = X86.Sse2.setr_epi32( - 0, - BufferGroup.SubTileWidth, - BufferGroup.SubTileWidth * 2, - BufferGroup.SubTileWidth * 3 - ); - v128 startPixelX = X86.Sse2.add_epi32(SimdSubTileColOffset, X86.Sse2.set1_epi32(tileBBoxi.SInt0)); - // TODO: (Apoorva) LHS is zero. We can just use the RHS directly. - v128 pixelY = X86.Sse2.add_epi32(X86.Sse2.setzero_si128(), X86.Sse2.set1_epi32(tileBBoxi.SInt2)); - - // Compute z from w. Note that z is reversed order, 0 = far, 1/near = near, which - // means we use a greater than test, so zMax is used to test for visibility. (z goes from 0 = far to 2 = near for ortho) - - v128 zMax; - if (projectionType == BatchCullingProjectionType.Orthographic) - { - zMax = IntrinsicUtils._mmw_fmadd_ps(X86.Sse.set1_ps(-1.0f), X86.Sse.set1_ps(wmin), X86.Sse.set1_ps(1.0f)); - } - else - { - zMax = X86.Sse.div_ps(X86.Sse.set1_ps(1f), X86.Sse.set1_ps(wmin)); - } - - for (; tileRowIdx < tileRowIdxEnd; tileRowIdx += NumTilesX) - { - v128 pixelX = startPixelX; - - for (int tx = txMin; tx < txMax; tx++) - { - int tileIdx = tileRowIdx + tx; - - // Fetch zMin from masked hierarchical Z buffer - v128 mask = tiles[tileIdx].mask; - v128 zMin0 = X86.Sse4_1.blendv_ps(tiles[tileIdx].zMin0, tiles[tileIdx].zMin1, X86.Sse2.cmpeq_epi32(mask, X86.Sse2.set1_epi32(~0))); - v128 zMin1 = X86.Sse4_1.blendv_ps(tiles[tileIdx].zMin1, tiles[tileIdx].zMin0, X86.Sse2.cmpeq_epi32(mask, X86.Sse2.setzero_si128())); - v128 zBuf = X86.Sse.min_ps(zMin0, zMin1); - - // Perform conservative greater than test against hierarchical Z buffer (zMax >= zBuf means the subtile is visible) - v128 zPass = X86.Sse.cmpge_ps(zMax, zBuf); //zPass = zMax >= zBuf ? ~0 : 0 - - // Mask out lanes corresponding to subtiles outside the bounding box - v128 bboxTestMin = X86.Sse2.and_si128(X86.Sse2.cmpgt_epi32(pixelX, stxmin), X86.Sse2.cmpgt_epi32(pixelY, stymin)); - v128 bboxTestMax = X86.Sse2.and_si128(X86.Sse2.cmpgt_epi32(stxmax, pixelX), X86.Sse2.cmpgt_epi32(stymax, pixelY)); - v128 boxMask = X86.Sse2.and_si128(bboxTestMin, bboxTestMax); - zPass = X86.Sse2.and_si128(zPass, boxMask); - - // If not all tiles failed the conservative z test we can immediately terminate the test - if (X86.Sse4_1.testz_si128(zPass, zPass) == 0) - { - return CullingResult.VISIBLE; - } - - pixelX = X86.Sse2.add_epi32(pixelX, X86.Sse2.set1_epi32(BufferGroup.TileWidth)); - } - - pixelY = X86.Sse2.add_epi32(pixelY, X86.Sse2.set1_epi32(BufferGroup.TileHeight)); - } - - return CullingResult.OCCLUDED; - } - else - { - return CullingResult.VISIBLE; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static CullingResult TestRectNEON( - float2 min, - float2 max, - float wmin, - Tile* tiles, - BatchCullingProjectionType projectionType, - int NumTilesX, - v128 ScreenSize, - v128 HalfSize, - v128 PixelCenter - ) - { - if (Arm.Neon.IsNeonSupported) - { - v128 zero = new v128(0); - v128 oneF = new v128(1.0f); - v128 negOneF = new v128(-1.0f); - v128 fullMask = new v128(~0); - v128 wideTileWidth = new v128(BufferGroup.TileWidth); - v128 wideTileHeight = new v128(BufferGroup.TileHeight); - - // Compute screen space bounding box and guard for out of bounds - v128 pixelBBox = Arm.Neon.vmlaq_f32(PixelCenter, new v128(min.x, max.x, max.y, min.y), HalfSize); - v128 pixelBBoxi = Arm.Neon.vcvtnq_s32_f32(pixelBBox); - pixelBBoxi = Arm.Neon.vmaxq_s32(zero, Arm.Neon.vminq_s32(ScreenSize, pixelBBoxi)); - - // Pad bounding box to (32xN) tiles. Tile BB is used for looping / traversal - v128 SimdTilePad = new v128(0, BufferGroup.TileWidth, 0, BufferGroup.TileHeight); - v128 SimdTilePadMask = new v128( - ~(BufferGroup.TileWidth - 1), - ~(BufferGroup.TileWidth - 1), - ~(BufferGroup.TileHeight - 1), - ~(BufferGroup.TileHeight - 1) - ); - v128 tileBBoxi = Arm.Neon.vandq_s8(Arm.Neon.vaddq_s32(pixelBBoxi, SimdTilePad), SimdTilePadMask); - - int txMin = tileBBoxi.SInt0 >> BufferGroup.TileWidthShift; - int txMax = tileBBoxi.SInt1 >> BufferGroup.TileWidthShift; - int tileRowIdx = (tileBBoxi.SInt2 >> BufferGroup.TileHeightShift) * NumTilesX; - int tileRowIdxEnd = (tileBBoxi.SInt3 >> BufferGroup.TileHeightShift) * NumTilesX; - - // Pad bounding box to (8x4) subtiles. Skip SIMD lanes outside the subtile BB - v128 SimdSubTilePad = new v128(0, BufferGroup.SubTileWidth, 0, BufferGroup.SubTileHeight); - v128 SimdSubTilePadMask = new v128( - ~(BufferGroup.SubTileWidth - 1), - ~(BufferGroup.SubTileWidth - 1), - ~(BufferGroup.SubTileHeight - 1), - ~(BufferGroup.SubTileHeight - 1) - ); - v128 subTileBBoxi = Arm.Neon.vandq_s8(Arm.Neon.vaddq_s32(pixelBBoxi, SimdSubTilePad), SimdSubTilePadMask); - - v128 stxmin = new v128(subTileBBoxi.SInt0 - 1); // - 1 to be able to use GT test - v128 stymin = new v128(subTileBBoxi.SInt2 - 1); // - 1 to be able to use GT test - v128 stxmax = new v128(subTileBBoxi.SInt1); - v128 stymax = new v128(subTileBBoxi.SInt3); - - // Setup pixel coordinates used to discard lanes outside subtile BB - v128 SimdSubTileColOffset = new v128( - 0, - BufferGroup.SubTileWidth, - BufferGroup.SubTileWidth * 2, - BufferGroup.SubTileWidth * 3 - ); - v128 startPixelX = Arm.Neon.vaddq_s32(SimdSubTileColOffset, new v128(tileBBoxi.SInt0)); - // TODO: (Apoorva) LHS is zero. We can just use the RHS directly. - v128 pixelY = Arm.Neon.vaddq_s32(zero, new v128(tileBBoxi.SInt2)); - - // Compute z from w. Note that z is reversed order, 0 = far, 1/near = near, which - // means we use a greater than test, so zMax is used to test for visibility. (z goes from 0 = far to 2 = near for ortho) - - v128 zMax; - v128 wMin = new v128(wmin); - if (projectionType == BatchCullingProjectionType.Orthographic) - { - zMax = Arm.Neon.vmlaq_f32(oneF, negOneF, wMin); - } - else - { - zMax = Arm.Neon.vdivq_f32(oneF, wMin); - } - - for (; tileRowIdx < tileRowIdxEnd; tileRowIdx += NumTilesX) - { - v128 pixelX = startPixelX; - - for (int tx = txMin; tx < txMax; tx++) - { - int tileIdx = tileRowIdx + tx; - - // Fetch zMin from masked hierarchical Z buffer - v128 mask = tiles[tileIdx].mask; - v128 zMin0 = IntrinsicUtils._vblendq_f32(Arm.Neon.vceqq_s32(mask, fullMask), tiles[tileIdx].zMin0, tiles[tileIdx].zMin1); - v128 zMin1 = IntrinsicUtils._vblendq_f32(Arm.Neon.vceqq_s32(mask, zero), tiles[tileIdx].zMin1, tiles[tileIdx].zMin0); - v128 zBuf = Arm.Neon.vminq_f32(zMin0, zMin1); - - // Perform conservative greater than test against hierarchical Z buffer (zMax >= zBuf means the subtile is visible) - v128 zPass = Arm.Neon.vcgeq_f32(zMax, zBuf); //zPass = zMax >= zBuf ? ~0 : 0 - - // Mask out lanes corresponding to subtiles outside the bounding box - v128 bboxTestMin = Arm.Neon.vandq_s8(Arm.Neon.vcgtq_s32(pixelX, stxmin), Arm.Neon.vcgtq_s32(pixelY, stymin)); - v128 bboxTestMax = Arm.Neon.vandq_s8(Arm.Neon.vcgtq_s32(stxmax, pixelX), Arm.Neon.vcgtq_s32(stymax, pixelY)); - v128 boxMask = Arm.Neon.vandq_s8(bboxTestMin, bboxTestMax); - zPass = Arm.Neon.vandq_s8(zPass, boxMask); - - // If not all tiles failed the conservative z test we can immediately terminate the test - v64 zTestResult = Arm.Neon.vqmovn_u64(zPass); - if (zTestResult.ULong0 != 0ul) - { - return CullingResult.VISIBLE; - } - - pixelX = Arm.Neon.vaddq_s32(pixelX, wideTileWidth); - } - - pixelY = Arm.Neon.vaddq_s32(pixelY, wideTileHeight); - } - - return CullingResult.OCCLUDED; - } - else - { - return CullingResult.VISIBLE; - } - } - - public static CullingResult TestRect( - float2 min, - float2 max, - float wmin, - Tile* tiles, - BatchCullingProjectionType projectionType, - int NumTilesX, - v128 ScreenSize, - v128 HalfSize, - v128 PixelCenter - ) - { - if (min.x > 1.0f || min.y > 1.0f || max.x < -1.0f || max.y < -1.0f) - { - return CullingResult.VIEW_CULLED; - } - - if (X86.Sse4_1.IsSse41Supported) - { - return TestRectSSE( min, max, wmin, tiles, projectionType, NumTilesX, ScreenSize, HalfSize, PixelCenter ); - } - else if (Arm.Neon.IsNeonSupported) - { - return TestRectNEON( min, max, wmin, tiles, projectionType, NumTilesX, ScreenSize, HalfSize, PixelCenter ); - } - - return CullingResult.VISIBLE; - } - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/TestJob.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/TestJob.cs.meta deleted file mode 100644 index c292122..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/TestJob.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8d6949e79a8777c479ab01b6fb4a913a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Types.cs b/Unity.Entities.Graphics/Occlusion/Masked/Types.cs deleted file mode 100644 index fd9eb9e..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Types.cs +++ /dev/null @@ -1,29 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Burst.Intrinsics; - -namespace Unity.Rendering.Occlusion.Masked -{ - enum CullingResult - { - VISIBLE = 0x0, - OCCLUDED = 0x1, - VIEW_CULLED = 0x3 - } - - struct Tile - { - public v128 zMin0; - public v128 zMin1; - public v128 mask; - } - - struct ScissorRect - { - public int mMinX; //!< Screen space X coordinate for left side of scissor rect, inclusive and must be a multiple of 32 - public int mMinY; //!< Screen space Y coordinate for bottom side of scissor rect, inclusive and must be a multiple of 8 - public int mMaxX; //!< Screen space X coordinate for right side of scissor rect, non inclusive and must be a multiple of 32 - public int mMaxY; //!< Screen space Y coordinate for top side of scissor rect, non inclusive and must be a multiple of 8 - } -} -#endif diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Types.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Types.cs.meta deleted file mode 100644 index 415b14a..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Types.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a20d2018e9b8ebd4a8596a1c06a49b39 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization.meta b/Unity.Entities.Graphics/Occlusion/Masked/Visualization.meta deleted file mode 100644 index fa8f3dd..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 0d5a27072eec390429c8b3a88f0f8d56 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugSettings.cs b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugSettings.cs deleted file mode 100644 index 5cd4129..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugSettings.cs +++ /dev/null @@ -1,172 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) -// This class contains the debug settings exposed to the rendering debugger window -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; -using UnityEngine.Rendering; -#if UNITY_EDITOR -using UnityEditor; -#endif - -namespace Unity.Rendering.Occlusion.Masked.Visualization -{ - enum DebugRenderMode - { - None = 0, - Depth = 1, - Test = 2, - Mesh = 3, - Bounds = 4, - Inverted = 5 - } - - class DebugSettings : IDebugData - { - public bool freezeOcclusion; - public DebugRenderMode debugRenderMode; - - GUIContent[] m_ViewNames; - ulong[] m_ViewIDs; - int[] m_ViewIndices; - int m_SelectedViewIndex; - DebugUI.Widget[] m_Widgets; - - readonly string k_PanelName = "Culling"; - - void Reset() - { - freezeOcclusion = false; - debugRenderMode = DebugRenderMode.None; - m_ViewNames = new GUIContent[]{new GUIContent("None")}; - m_ViewIDs = new ulong[]{0}; - m_ViewIndices = new int[]{0}; - m_SelectedViewIndex = 0; - } - - Action IDebugData.GetReset() => Reset; - - public DebugSettings() - { - Reset(); - } - - public ulong? GetPinnedViewID() - { - return (m_SelectedViewIndex == 0) ? null : m_ViewIDs[m_SelectedViewIndex]; - } - - public void Register() - { -#if PLATFORM_ANDROID - // FK: No support for this feature on ARM platform with 32Bit since Neon Intrinsics aren't supported - // Yury: Android is the only 32-bit Arm platform we support - bool is32Bit = System.IntPtr.Size == 4; - if (is32Bit) - { - return; - } -#endif - var widgetList = new List(); - widgetList.Add(new DebugUI.Container - { - displayName = "Occlusion Culling", - children = - { - new DebugUI.BoolField - { - nameAndTooltip = new() - { - name = "Freeze Occlusion", - tooltip = "Enable to pause updating the occlusion while freely moving the camera." - }, - getter = () => freezeOcclusion, - setter = value => { freezeOcclusion = value; }, - }, - new DebugUI.EnumField - { - nameAndTooltip = new() - { - name = "Pinned View", - tooltip = - "Use the drop-down to pin a view. All scene views and game views will cull objects from the pinned view's perspective." - }, - getter = () => m_SelectedViewIndex, - setter = value => { m_SelectedViewIndex = value; }, - enumNames = m_ViewNames, - enumValues = m_ViewIndices, - getIndex = () => m_SelectedViewIndex, - setIndex = value => { m_SelectedViewIndex = value; } - }, - new DebugUI.EnumField - { - nameAndTooltip = new() - { - name = "Debug Mode", - tooltip = - "Use the drop-down to select a rendering mode to display as an overlay on the screen." - }, - getter = () => (int)debugRenderMode, - setter = value => { debugRenderMode = (DebugRenderMode)value; }, - getIndex = () => (int)debugRenderMode, - setIndex = value => { debugRenderMode = (DebugRenderMode)value; }, - autoEnum = typeof(DebugRenderMode), - } - } - }); - - var panel = DebugManager.instance.GetPanel(k_PanelName, true); - m_Widgets = widgetList.ToArray(); - panel.children.Add(m_Widgets); - - DebugManager.instance.RegisterData(this); - } - - public void RefreshViews(Dictionary bufferGroups) - { -#if UNITY_EDITOR - var ids = new List(bufferGroups.Count); - var names = new List(); - ids.Add(0); - names.Add(new GUIContent("None")); - - foreach (var pair in bufferGroups) - { - var instanceID = (int)(pair.Key & 0xffffffff); - var splitIndex = (int)(pair.Key >> 32); - var viewType = pair.Value.ViewType; - var obj = (Behaviour) EditorUtility.InstanceIDToObject(instanceID); - if (!obj || !obj.gameObject.activeInHierarchy) - { - continue; - } - - var label = $"{obj.name}"; - if (viewType != BatchCullingViewType.Camera) - { - label += $", Split {splitIndex}"; - } - - names.Add(new GUIContent(label)); - ids.Add(pair.Key); - } - - m_SelectedViewIndex = 0; - m_ViewNames = names.ToArray(); - m_ViewIndices = Enumerable.Range(0, m_ViewNames.Count()).ToArray(); - m_ViewIDs = ids.ToArray(); - - Unregister(); - Register(); -#endif - } - - public void Unregister() - { - var panel = DebugManager.instance.GetPanel(k_PanelName); - panel?.children.Remove(m_Widgets); - } - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugSettings.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugSettings.cs.meta deleted file mode 100644 index 60403db..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugSettings.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 06174c0a65cb5a04698ace71a469a1c0 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugView.cs b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugView.cs deleted file mode 100644 index 8884da0..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugView.cs +++ /dev/null @@ -1,376 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Entities; -using Unity.Jobs; -using Unity.Mathematics; -using Unity.Profiling; -using UnityEngine; -using UnityEngine.Rendering; -using Object = UnityEngine.Object; -using Unity.Rendering.Occlusion.Masked.Dots; -using Unity.Transforms; - -#if UNITY_EDITOR -using UnityEditor; -#endif - -namespace Unity.Rendering.Occlusion.Masked.Visualization -{ - /* This class stores the resources needed to draw debug visualizations for any view that uses occlusion culling. - Views can be cameras, lights' shadowmaps, reflection probes, or anything else that renders the scene. - - A debug view is allocated only when a debug visualization is requested by the user. Otherwise no memory is - allocated for debug purposes. */ - unsafe class DebugView - { - static readonly int s_DepthPropertyID = Shader.PropertyToID("_Depth"); - static readonly int s_OverlayPropertyID = Shader.PropertyToID("_Overlay"); - static readonly int s_YFlipPropertyID = Shader.PropertyToID("_YFlip"); - static readonly int s_TransformPropertyID = Shader.PropertyToID("_Transform"); - static readonly int s_OnlyOverlayPropertyID = Shader.PropertyToID("_OnlyOverlay"); - static readonly int s_OnlyDepthPropertyID = Shader.PropertyToID("_OnlyDepth"); - static readonly Shader s_CompositeShader = Shader.Find("Hidden/OcclusionDebugComposite"); - static readonly Shader s_OccluderShader = Shader.Find("Hidden/OcclusionDebugOccluders"); - - static readonly ProfilerMarker s_MaskedDepthToPixelDepth = new ProfilerMarker("Occlusion.Debug.RenderView.MaskedDepthToPixelDepth"); - static readonly ProfilerMarker s_OcclusionTests = new ProfilerMarker("Occlusion.Debug.RenderView.OcclusionTests"); - static readonly ProfilerMarker s_GenerateAABBMesh = new ProfilerMarker("Occlusion.Debug.RenderView.GenerateAABBMesh"); - static readonly ProfilerMarker s_GenerateOutlineMesh = new ProfilerMarker("Occlusion.Debug.RenderView.GenerateOutlineMesh"); - static readonly ProfilerMarker s_GenerateOccluderMesh = new ProfilerMarker("Occlusion.Debug.RenderView.GenerateOccluderMesh"); - - static readonly CommandBuffer s_CmdLayers = new CommandBuffer() { name = "Occlusion debug layers" }; - Material s_OccludeeAABBsMaterial; - - // Memory used to upload CPU depth to GPU - NativeArray m_CPUDepth; - public Texture2D gpuDepth; - // Debug texture drawn to the screen as an overlay - RenderTexture m_Overlay; - // Single mesh containing AABBs of all culled occludees - Mesh m_OccludeeAabBsMesh; - Material m_OccluderMaterial; - // Single mesh containing all the occluder meshes - Mesh m_OccludersMesh; - VertexAttributeDescriptor[] m_OccluderVertexLayout; - // Final composite - Material m_CompositeMaterial; - - private bool AnyMeshOrMaterialNull() - { - return m_OccludeeAabBsMesh == null || m_OccluderMaterial == null || m_OccludersMesh == null || m_CompositeMaterial == null || s_OccludeeAABBsMaterial == null; - } - private void CreateMeshAndMaterials() - { - m_OccludeeAabBsMesh = new Mesh(); - m_OccludersMesh = new Mesh(); - m_OccluderMaterial = new Material(s_OccluderShader); - m_OccluderMaterial.SetPass(0); - m_CompositeMaterial = new Material(s_CompositeShader); - m_CompositeMaterial.SetPass(0); - m_OccluderVertexLayout = new[] - { - new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 4), - new VertexAttributeDescriptor(VertexAttribute.Color, VertexAttributeFormat.UNorm8, 4, stream: 1) - }; - s_OccludeeAABBsMaterial = new Material(Shader.Find("Hidden/OccludeeScreenSpaceAABB")); - } - public DebugView() - { - } - - public void Dispose() - { - m_CPUDepth.Dispose(); - Object.DestroyImmediate(gpuDepth); - Object.DestroyImmediate(m_Overlay); - } - - // Ensure that the member resources fit the dimensions of the view - public void ReallocateIfNeeded(int viewWidth, int viewHeight) - { - if (AnyMeshOrMaterialNull()) - { - CreateMeshAndMaterials(); - } - - // Reallocate depth texture if needed - if (!gpuDepth || gpuDepth.width != viewWidth || gpuDepth.height != viewHeight) - { - if (gpuDepth) - { - Object.DestroyImmediate(gpuDepth); - } - gpuDepth = new Texture2D(viewWidth, viewHeight, TextureFormat.RFloat, false); - gpuDepth.filterMode = FilterMode.Point; - m_CompositeMaterial.SetTexture(s_DepthPropertyID, gpuDepth); - } - // Reallocate overlay texture if needed - if (!m_Overlay || m_Overlay.width != viewWidth || m_Overlay.height != viewHeight) - { - if (m_Overlay) - { - Object.DestroyImmediate(m_Overlay); - } - m_Overlay = new RenderTexture(viewWidth, viewHeight, 1, RenderTextureFormat.RFloat, 0); - m_CompositeMaterial.SetTexture(s_OverlayPropertyID, m_Overlay); - } - // Reallocate cpu-side debug depth buffer if needed - if (!m_CPUDepth.IsCreated || m_CPUDepth.Length != viewWidth * viewHeight) - { - if (m_CPUDepth.IsCreated) - { - m_CPUDepth.Dispose(); - } - m_CPUDepth = new NativeArray(viewWidth * viewHeight, Allocator.Persistent); - } - } - - // Render to the depth buffer and the overlay texture - public void RenderToTextures( - EntityQuery testQuery, - EntityQuery meshQuery, - BufferGroup bufferGroup, - DebugRenderMode mode -#if UNITY_EDITOR - , bool isOcclusionBrowseWindowVisible -#endif - ) - { - if (AnyMeshOrMaterialNull()) - { - CreateMeshAndMaterials(); - } - s_CmdLayers.Clear(); - // Write the CPU-rasterized depth buffer to a GPU texture, and then blit it to the overlay - if (mode == DebugRenderMode.Depth || - mode == DebugRenderMode.Test -#if UNITY_EDITOR - || isOcclusionBrowseWindowVisible -#endif - ) - { - s_MaskedDepthToPixelDepth.Begin(); - int width = bufferGroup.NumPixelsX; - int height = bufferGroup.NumPixelsY; - int numTilesX = bufferGroup.NumTilesX; - int numTilesY = bufferGroup.NumTilesY; - var job = new DecodeMaskedDepthJob() - { - // In - NumPixelsX = width, - NumPixelsY = height, - NumTilesX = numTilesX, - Tiles = (Tile*)bufferGroup.Tiles.GetUnsafeReadOnlyPtr(), - // Out - DecodedZBuffer = m_CPUDepth, - }; - job.Schedule((numTilesX * numTilesY), 64).Complete(); - - gpuDepth.SetPixelData(m_CPUDepth, 0); - gpuDepth.Apply(); - s_MaskedDepthToPixelDepth.End(); - } - - if (mode == DebugRenderMode.Test) - { - s_OcclusionTests.Begin(); - - // Extract AABBs which are tested and culled due to occlusion culling - NativeArray allTests = testQuery.ToComponentDataArray(Allocator.TempJob); - var culledTestsQueue = new NativeQueue(Allocator.TempJob); - - var testJob = new FilterOccludedTestJob() - { - // In - ProjectionType = bufferGroup.ProjectionType, - NumTilesX = bufferGroup.NumTilesX, - HalfSize = bufferGroup.HalfSize, - PixelCenter = bufferGroup.PixelCenter, - ScreenSize = bufferGroup.ScreenSize, - Tiles = (Tile*)bufferGroup.Tiles.GetUnsafeReadOnlyPtr(), - AllTests = allTests, - // Out - culledTestsQueue = culledTestsQueue.AsParallelWriter(), - }; - testJob.Schedule(allTests.Length, 1).Complete(); - var culledTests = culledTestsQueue.ToArray(Allocator.TempJob); - culledTestsQueue.Dispose(); - allTests.Dispose(); - s_OcclusionTests.End(); - - s_GenerateAABBMesh.Begin(); - // Create a mesh with an AABB for every culled occludee - { - var dataArray = Mesh.AllocateWritableMeshData(1); - var data = dataArray[0]; - // We need 4 verts and 6 indices per AABB - data.SetVertexBufferParams(4 * culledTests.Length, new VertexAttributeDescriptor(VertexAttribute.Position)); - data.SetIndexBufferParams(6 * culledTests.Length, IndexFormat.UInt16); - NativeArray verts = data.GetVertexData(); - NativeArray indices = data.GetIndexData(); - // Fill the mesh data in a job - var job = new OccludeeAABBJob() - { - // In - CulledTests = culledTests, - // Out - Verts = verts, - Indices = indices, - }; - job.Schedule(culledTests.Length, 32).Complete(); - data.subMeshCount = 1; - data.SetSubMesh(0, new SubMeshDescriptor(0, indices.Length)); - // Create the mesh and apply data to it: - Mesh.ApplyAndDisposeWritableMeshData(dataArray, m_OccludeeAabBsMesh); - m_OccludeeAabBsMesh.RecalculateBounds(); - } - - // Draw to the overlay - s_CmdLayers.SetRenderTarget(m_Overlay); - s_CmdLayers.ClearRenderTarget(false, true, Color.clear); - s_CmdLayers.DrawMesh(m_OccludeeAabBsMesh, Matrix4x4.identity, s_OccludeeAABBsMaterial); - culledTests.Dispose(); - s_GenerateAABBMesh.End(); - } - else if (mode == DebugRenderMode.Bounds) - { - s_GenerateOutlineMesh.Begin(); - // Create a mesh with an AABB for each occludee, regardless of whether it is culled or not - { - NativeArray allTests = testQuery.ToComponentDataArray(Allocator.TempJob); - - var dataArray = Mesh.AllocateWritableMeshData(1); - var data = dataArray[0]; - // We need 4 verts and 6 indices per AABB - data.SetVertexBufferParams(16 * allTests.Length, new VertexAttributeDescriptor(VertexAttribute.Position)); - data.SetIndexBufferParams(24 * allTests.Length, IndexFormat.UInt32); - NativeArray verts = data.GetVertexData(); - NativeArray indices = data.GetIndexData(); - // Fill the mesh data in a job - var job = new OccludeeOutlineJob() - { - // In - InvResolution = new float2(1f / m_Overlay.width, 1f / m_Overlay.height), - AllTests = allTests, - // Out - Verts = verts, - Indices = indices, - }; - job.Schedule(allTests.Length, 32).Complete(); - data.subMeshCount = 1; - data.SetSubMesh(0, new SubMeshDescriptor(0, indices.Length)); - // Create the mesh and apply data to it: - Mesh.ApplyAndDisposeWritableMeshData(dataArray, m_OccludeeAabBsMesh); - m_OccludeeAabBsMesh.RecalculateBounds(); - allTests.Dispose(); - } - s_CmdLayers.SetRenderTarget(m_Overlay); - s_CmdLayers.ClearRenderTarget(false, true, Color.clear); - s_CmdLayers.DrawMesh(m_OccludeeAabBsMesh, Matrix4x4.identity, s_OccludeeAABBsMaterial); - s_GenerateOutlineMesh.End(); - } - else if (mode == DebugRenderMode.Mesh) - { - s_GenerateOccluderMesh.Begin(); - NativeArray meshes = meshQuery.ToComponentDataArray(Allocator.TempJob); - NativeArray localToWorlds = meshQuery.ToComponentDataArray(Allocator.TempJob); - // To parallelize mesh aggregation, we first need to find the total number of vertices and indices - // across all occluder meshes. We also need to precompute the offsets of each occluder mesh in the - // aggregated vertex and index buffers. - var vertOffsets = new NativeArray(meshes.Length, Allocator.TempJob); - var indexOffsets = new NativeArray(meshes.Length, Allocator.TempJob); - int numVerts = 0; - int numIndices = 0; - { - for (int i = 0; i < meshes.Length; i++) - { - vertOffsets[i] = numVerts; - numVerts += meshes[i].vertexCount; - } - - for (int i = 0; i < meshes.Length; i++) - { - indexOffsets[i] = numIndices; - numIndices += meshes[i].indexCount; - } - } - - // Prepare a single mesh containing all of the occluders - { - var dataArray = Mesh.AllocateWritableMeshData(1); - var data = dataArray[0]; - data.SetVertexBufferParams(numVerts, m_OccluderVertexLayout); - // We use a 32-bit index buffer to handle large meshes. - data.SetIndexBufferParams(numIndices, IndexFormat.UInt32); - NativeArray verts = data.GetVertexData(); - NativeArray colors = data.GetVertexData(stream: 1); - NativeArray indices = data.GetIndexData(); - // Fill the mesh data in a job - new MeshAggregationJob() - { - // In - Meshes = meshes, - LocalToWorlds = localToWorlds, - VertOffsets = vertOffsets, - IndexOffsets = indexOffsets, - // Out - Verts = verts, - Colors = colors, - Indices = indices, - }.Schedule(meshes.Length, 4) - .Complete(); - data.subMeshCount = 1; - data.SetSubMesh(0, new SubMeshDescriptor(0, indices.Length)); - // Create the mesh and apply data to it: - Mesh.ApplyAndDisposeWritableMeshData(dataArray, m_OccludersMesh); - // the vertices are already in screenspace and perspective projected - m_OccludersMesh.bounds = new Bounds(new Vector3(-1000, -1000, -1000), new Vector3(10000, 10000, 1000)); - } - - vertOffsets.Dispose(); - indexOffsets.Dispose(); - meshes.Dispose(); - localToWorlds.Dispose(); - s_GenerateOccluderMesh.End(); - } - Graphics.ExecuteCommandBuffer(s_CmdLayers); - } - - public void RenderToCamera(DebugRenderMode renderMode, Camera camera, CommandBuffer cmd, Mesh fullScreenQuad, Matrix4x4 cullingMatrix) - { - if (renderMode == DebugRenderMode.None) - return; - - float yFlip = 0f; -#if UNITY_EDITOR - if(camera.cameraType == CameraType.Preview) - { - yFlip = 1f; - } - if (Camera.current != null && camera == SceneView.currentDrawingSceneView.camera) - { - yFlip = 1f; - } -#endif // UNITY_EDITOR - - if (renderMode == DebugRenderMode.Mesh) - { - var material = m_OccluderMaterial; - material.SetFloat(s_YFlipPropertyID, yFlip); - material.SetMatrix(s_TransformPropertyID, cullingMatrix); - cmd.DrawMesh(m_OccludersMesh, Matrix4x4.identity, material); - } - else - { - m_CompositeMaterial.SetFloat(s_YFlipPropertyID, yFlip); - m_CompositeMaterial.SetFloat(s_OnlyOverlayPropertyID, (renderMode == DebugRenderMode.Bounds) ? 1f : 0f); - m_CompositeMaterial.SetFloat(s_OnlyDepthPropertyID, (renderMode == DebugRenderMode.Depth) ? 1f : 0f); - cmd.DrawMesh(fullScreenQuad, Matrix4x4.identity, m_CompositeMaterial); - } - } - } -} - -#endif diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugView.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugView.cs.meta deleted file mode 100644 index 872b1e0..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DebugView.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 7907546ec8bf0544ca55d4bf82b6694f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DecodeMaskedDepthJob.cs b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DecodeMaskedDepthJob.cs deleted file mode 100644 index 973ccbd..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DecodeMaskedDepthJob.cs +++ /dev/null @@ -1,96 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Burst; -using Unity.Burst.Intrinsics; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; - -namespace Unity.Rendering.Occlusion.Masked.Visualization -{ - /* Unpack the CPU-rasterized masked depth buffer into a human-readable depth buffer. */ - [BurstCompile] - unsafe struct DecodeMaskedDepthJob : IJobParallelFor - { - [ReadOnly] public int NumPixelsX; - [ReadOnly] public int NumPixelsY; - [ReadOnly] public int NumTilesX; - [ReadOnly, NativeDisableUnsafePtrRestriction] public Tile* Tiles; - - [WriteOnly] public NativeArray DecodedZBuffer; - - // i => tile index - public void Execute(int i) - { - float* zBuffer = (float*)DecodedZBuffer.GetUnsafePtr(); - - // this is a 32x4 tile - var tile = Tiles[i]; - - int numTilesX = NumPixelsX / BufferGroup.TileWidth; - int numTilesY = NumPixelsY / BufferGroup.TileHeight; - - int tx = i % numTilesX; - int ty = i / numTilesX; - - // iterate over the four 8x4 subtiles - for (int j = 0; j < 4; j++) - { - // prepare two vectors of zMin0 and zMin1 - // splat j's element - var subTilez0 = new v128(IntrinsicUtils.getFloatLane(tile.zMin0, (uint)j)); - var subTilez1 = new v128(IntrinsicUtils.getFloatLane(tile.zMin1, (uint)j)); - - var testMask = new v128(1, 2, 4, 8); - - // the mask is 32 bit, 8x4 bits - // iterate over each byte - for (int k = 0; k < 4; k++) - { - // extract mask for the subtile - byte subTileMask = IntrinsicUtils.getByteLane(tile.mask, (uint)(j * 4 + k)); - - // now, make low and high half-bytes into a int32x4 mask for blending - // high - int highHalfByte = subTileMask >> 4; - var highMask = new v128(highHalfByte); - // low - int lowHalfByte = subTileMask & 15; - var lowMask = new v128(lowHalfByte); - - if (Arm.Neon.IsNeonSupported) - { - var blendMaskHigh = Arm.Neon.vtstq_s32(highMask, testMask); - var zResultHigh = Arm.Neon.vbslq_s8(blendMaskHigh, subTilez1, subTilez0); - - var blendMaskLow = Arm.Neon.vtstq_s32(lowMask, testMask); - var zResultLow = Arm.Neon.vbslq_s8(blendMaskLow, subTilez1, subTilez0); - - int index = ((NumPixelsY - (BufferGroup.TileHeight * ty + k)) * NumPixelsX + BufferGroup.TileWidth * tx + BufferGroup.SubTileWidth * j); - - // save to DecodedZBuffer - // this generates STP which is most efficient - Arm.Neon.vst1q_f32(zBuffer + index, zResultLow); - Arm.Neon.vst1q_f32(zBuffer + index + 4, zResultHigh); - } - else if (X86.Sse4_1.IsSse41Supported) - { - var invBlendMaskHigh = X86.Sse2.cmpeq_epi32(X86.Sse2.and_si128(highMask, testMask), X86.Sse2.setzero_si128()); - var zResultHigh = X86.Sse4_1.blendv_ps(subTilez1, subTilez0, invBlendMaskHigh); - - var invBlendMaskLow = X86.Sse2.cmpeq_epi32(X86.Sse2.and_si128(lowMask, testMask), X86.Sse2.setzero_si128()); - var zResultLow = X86.Sse4_1.blendv_ps(subTilez1, subTilez0, invBlendMaskLow); - - int index = ((NumPixelsY - (BufferGroup.TileHeight * ty + k)) * NumPixelsX + BufferGroup.TileWidth * tx + BufferGroup.SubTileWidth * j); - - v128* zBufferSimd = (v128*)zBuffer; - zBufferSimd[index / 4] = zResultLow; - zBufferSimd[index / 4 + 1] = zResultHigh; - } - } - } - } - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DecodeMaskedDepthJob.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DecodeMaskedDepthJob.cs.meta deleted file mode 100644 index 3b52da4..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/DecodeMaskedDepthJob.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 336c766ce50905f418cde3a31b9359e3 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/FilterOccludedTestJob.cs b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/FilterOccludedTestJob.cs deleted file mode 100644 index 0f9457d..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/FilterOccludedTestJob.cs +++ /dev/null @@ -1,52 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Burst; -using Unity.Burst.Intrinsics; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using Unity.Rendering.Occlusion.Masked.Dots; -using UnityEngine.Rendering; - -namespace Unity.Rendering.Occlusion.Masked.Visualization -{ - /* Take in all tests (i.e. occludees), and only return the ones which the occlusion system identifies as being fully - occluded by other geometry. */ - [BurstCompile] - unsafe struct FilterOccludedTestJob : IJobParallelFor - { - [ReadOnly] public BatchCullingProjectionType ProjectionType; - [ReadOnly] public int NumTilesX; - [ReadOnly] public v128 HalfSize; - [ReadOnly] public v128 PixelCenter; - [ReadOnly] public v128 ScreenSize; - [ReadOnly, NativeDisableUnsafePtrRestriction] public Tile* Tiles; - [ReadOnly] public NativeArray AllTests; - - [WriteOnly] public NativeQueue.ParallelWriter culledTestsQueue; - - public void Execute(int i) - { - OcclusionTest test = AllTests[i]; - - CullingResult cullingResult = TestJob.TestRect( - test.screenMin.xy, - test.screenMax.xy, - test.screenMin.w, - Tiles, - ProjectionType, - NumTilesX, - ScreenSize, - HalfSize, - PixelCenter - ); - - if (cullingResult == CullingResult.OCCLUDED) - { - culledTestsQueue.Enqueue(test); - } - } - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/FilterOccludedTestJob.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/FilterOccludedTestJob.cs.meta deleted file mode 100644 index 7b7126d..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/FilterOccludedTestJob.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8a1d8335f7892884d8e4078545785e2e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/MeshAggregationJob.cs b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/MeshAggregationJob.cs deleted file mode 100644 index 806318b..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/MeshAggregationJob.cs +++ /dev/null @@ -1,72 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Burst; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using Unity.Mathematics; -using UnityEngine; -using Random = Unity.Mathematics.Random; -using Unity.Rendering.Occlusion.Masked.Dots; -using Unity.Transforms; - -namespace Unity.Rendering.Occlusion.Masked.Visualization -{ - /* Return a single mesh containing all of the input meshes, with a stable random color assigned to each source mesh. */ - [BurstCompile] - unsafe struct MeshAggregationJob : IJobParallelFor - { - [ReadOnly] public NativeArray Meshes; - [ReadOnly] public NativeArray LocalToWorlds; - [ReadOnly] public NativeArray VertOffsets; - [ReadOnly] public NativeArray IndexOffsets; - - [WriteOnly, NativeDisableContainerSafetyRestriction] public NativeArray Verts; - [WriteOnly, NativeDisableContainerSafetyRestriction] public NativeArray Colors; - [WriteOnly, NativeDisableContainerSafetyRestriction] public NativeArray Indices; - - public void Execute(int m) - { - OcclusionMesh mesh = Meshes[m]; - var srcVerts = (float3*) mesh.vertexData.GetUnsafePtr(); - - // Create a random fully saturated color from the mesh index - Color32 col; - { - float hue = Random.CreateFromIndex((uint) m).NextFloat(); - float h6 = 6f * hue; - float3 c = math.saturate( - new float3( - 2f - math.abs(h6 - 4f), - math.abs(h6 - 3f) - 1f, - 2f - math.abs(h6 - 2f) - ) - ); - col = new Color32((byte) (c.x * 255), (byte) (c.y * 255), (byte) (c.z * 255), 255); - } - // Copy over all the vertices, transforming them into world space. Also assign a random color. - for (int i = 0; i < mesh.vertexCount; i++) - { - // We discarded the last row of the occluder matrix to save memory bandwidth because it is always (0, 0, 0,1). - // However, to perform the actual math, we still need a 4x4 matrix. So we reintroduce the row here. - float4x4 occluderMtx = new float4x4( - new float4(mesh.localTransform.c0, 0f), - new float4(mesh.localTransform.c1, 0f), - new float4(mesh.localTransform.c2, 0f), - new float4(mesh.localTransform.c3, 1f) - ); - Verts[VertOffsets[m] + i] = math.mul(math.mul(LocalToWorlds[m].Value, occluderMtx), new float4(srcVerts[i], 1.0f)); - Colors[VertOffsets[m] + i] = col; - } - - // Copy over all the indices, while adding an offset - var srcIndices = (int*) mesh.indexData.GetUnsafePtr(); - for (int i = 0; i < mesh.indexCount; i++) - { - Indices[IndexOffsets[m] + i] = VertOffsets[m] + srcIndices[i]; - } - } - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/MeshAggregationJob.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/MeshAggregationJob.cs.meta deleted file mode 100644 index 4c4eeca..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/MeshAggregationJob.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 0467e4e78d9234b4f9ff3863971e4841 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeAABBJob.cs b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeAABBJob.cs deleted file mode 100644 index 30059e2..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeAABBJob.cs +++ /dev/null @@ -1,44 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Burst; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using UnityEngine; -using Unity.Rendering.Occlusion.Masked.Dots; - -namespace Unity.Rendering.Occlusion.Masked.Visualization -{ - /* Return a mesh with one quad for each test (i.e. occludee). */ - [BurstCompile] - struct OccludeeAABBJob : IJobParallelFor - { - [ReadOnly] public NativeArray CulledTests; - - [WriteOnly, NativeDisableContainerSafetyRestriction] public NativeArray Verts; - [WriteOnly, NativeDisableContainerSafetyRestriction] public NativeArray Indices; - - public void Execute(int i) - { - var test = CulledTests[i]; - - int vertBase = 4 * i; - int indexBase = 6 * i; - - Verts[vertBase] = new Vector3(test.screenMin.x, 0f - test.screenMin.y, 0.5f); - Verts[vertBase + 1] = new Vector3(test.screenMax.x, 0f - test.screenMin.y, 0.5f); - Verts[vertBase + 2] = new Vector3(test.screenMin.x, 0f - test.screenMax.y, 0.5f); - Verts[vertBase + 3] = new Vector3(test.screenMax.x, 0f - test.screenMax.y, 0.5f); - - Indices[indexBase] = (ushort) vertBase; - Indices[indexBase + 1] = (ushort) (vertBase + 1); - Indices[indexBase + 2] = (ushort) (vertBase + 2); - Indices[indexBase + 3] = (ushort) (vertBase + 2); - Indices[indexBase + 4] = (ushort) (vertBase + 1); - Indices[indexBase + 5] = (ushort) (vertBase + 3); - } - - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeAABBJob.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeAABBJob.cs.meta deleted file mode 100644 index 78a5a38..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeAABBJob.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6d7a80ad4bf37204f8eb6e7385261773 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeOutlineJob.cs b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeOutlineJob.cs deleted file mode 100644 index 5cf32a5..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeOutlineJob.cs +++ /dev/null @@ -1,92 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Burst; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using Unity.Mathematics; -using UnityEngine; -using Unity.Rendering.Occlusion.Masked.Dots; - -namespace Unity.Rendering.Occlusion.Masked.Visualization -{ - /* Return a mesh with four quad forming an outline for each test (i.e. occludee). */ - [BurstCompile] - struct OccludeeOutlineJob : IJobParallelFor - { - [ReadOnly] public float2 InvResolution; - [ReadOnly] public NativeArray AllTests; - - [WriteOnly, NativeDisableContainerSafetyRestriction] public NativeArray Verts; - [WriteOnly, NativeDisableContainerSafetyRestriction] public NativeArray Indices; - - public void Execute(int i) - { - var test = AllTests[i]; - - int vertBase = 16 * i; - int indexBase = 24 * i; - - float px = 2f * InvResolution.x; // Size of 1 pixel in texture space - float py = 2f * InvResolution.y; - float xmin = test.screenMin.x; - float ymin = test.screenMin.y; - float xmax = test.screenMax.x; - float ymax = test.screenMax.y; - - // Left edge - Verts[vertBase] = new Vector3(xmin, -ymin, 0.5f); - Verts[vertBase + 1] = new Vector3(xmin + px, -ymin, 0.5f); - Verts[vertBase + 2] = new Vector3(xmin, -ymax, 0.5f); - Verts[vertBase + 3] = new Vector3(xmin + px, -ymax, 0.5f); - - // Right edge - Verts[vertBase + 4] = new Vector3(xmax - px, -ymin, 0.5f); - Verts[vertBase + 5] = new Vector3(xmax, -ymin, 0.5f); - Verts[vertBase + 6] = new Vector3(xmax - px, -ymax, 0.5f); - Verts[vertBase + 7] = new Vector3(xmax, -ymax, 0.5f); - - // Top edge - Verts[vertBase + 8] = new Vector3(xmin + px, -ymin, 0.5f); - Verts[vertBase + 9] = new Vector3(xmax - px, -ymin, 0.5f); - Verts[vertBase + 10] = new Vector3(xmin + px, -(ymin + py), 0.5f); - Verts[vertBase + 11] = new Vector3(xmax - px, -(ymin + py), 0.5f); - - // Bottom edge - Verts[vertBase + 12] = new Vector3(xmin + px, -(ymax - py), 0.5f); - Verts[vertBase + 13] = new Vector3(xmax - px, -(ymax - py), 0.5f); - Verts[vertBase + 14] = new Vector3(xmin + px, -ymax, 0.5f); - Verts[vertBase + 15] = new Vector3(xmax - px, -ymax, 0.5f); - - Indices[indexBase] = (uint) vertBase; - Indices[indexBase + 1] = (uint) (vertBase + 1); - Indices[indexBase + 2] = (uint) (vertBase + 2); - Indices[indexBase + 3] = (uint) (vertBase + 2); - Indices[indexBase + 4] = (uint) (vertBase + 1); - Indices[indexBase + 5] = (uint) (vertBase + 3); - - Indices[indexBase + 6] = (uint) (vertBase + 4); - Indices[indexBase + 7] = (uint) (vertBase + 5); - Indices[indexBase + 8] = (uint) (vertBase + 6); - Indices[indexBase + 9] = (uint) (vertBase + 6); - Indices[indexBase + 10] = (uint) (vertBase + 5); - Indices[indexBase + 11] = (uint) (vertBase + 7); - - Indices[indexBase + 12] = (uint) (vertBase + 8); - Indices[indexBase + 13] = (uint) (vertBase + 9); - Indices[indexBase + 14] = (uint) (vertBase + 10); - Indices[indexBase + 15] = (uint) (vertBase + 10); - Indices[indexBase + 16] = (uint) (vertBase + 9); - Indices[indexBase + 17] = (uint) (vertBase + 11); - - Indices[indexBase + 18] = (uint) (vertBase + 12); - Indices[indexBase + 19] = (uint) (vertBase + 13); - Indices[indexBase + 20] = (uint) (vertBase + 14); - Indices[indexBase + 21] = (uint) (vertBase + 14); - Indices[indexBase + 22] = (uint) (vertBase + 13); - Indices[indexBase + 23] = (uint) (vertBase + 15); - } - } -} - -#endif // ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) diff --git a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeOutlineJob.cs.meta b/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeOutlineJob.cs.meta deleted file mode 100644 index 3316cc1..0000000 --- a/Unity.Entities.Graphics/Occlusion/Masked/Visualization/OccludeeOutlineJob.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 76adbdb6ae6e0764ba17e582d8c440a0 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/Occluder.cs b/Unity.Entities.Graphics/Occlusion/Occluder.cs deleted file mode 100644 index a5bb491..0000000 --- a/Unity.Entities.Graphics/Occlusion/Occluder.cs +++ /dev/null @@ -1,73 +0,0 @@ -using UnityEngine; -using UnityEngine.Serialization; - -namespace Unity.Rendering.Occlusion -{ - /// - /// Specifies which entities are occluders and configures occluder settings. - /// - public class Occluder : MonoBehaviour - { - /// - /// The mesh to use for occlusion culling calculations. - /// - [FormerlySerializedAs("Mesh")] public Mesh mesh; - - /// - /// The position offset to apply to the occluder mesh. - /// - [FormerlySerializedAs("relativePosition")] - public Vector3 localPosition = Vector3.zero; - - /// - /// The rotation offset to apply to the occluder mesh. - /// - [FormerlySerializedAs("relativeRotation")] - public Quaternion localRotation = Quaternion.identity; - - /// - /// The scale offset to apply to the occluder mesh. - /// - [FormerlySerializedAs("relativeScale")] - public Vector3 localScale = Vector3.one; - -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - void Reset() - { - if (gameObject.TryGetComponent(out var meshFilter)) - { - mesh = meshFilter.sharedMesh; - } - - localPosition = Vector3.zero; - localRotation = Quaternion.identity; - localScale = Vector3.one; - } - - private void OnDrawGizmos() - { - DrawGizmos(false); - } - - private void OnDrawGizmosSelected() - { - DrawGizmos(true); - } - - private void DrawGizmos(bool selected) - { - if (mesh == null || mesh.vertexCount == 0) - { - return; - } - - Gizmos.color = selected ? Color.yellow : Color.white; - Matrix4x4 mtx = Gizmos.matrix; - Gizmos.matrix = transform.localToWorldMatrix * Matrix4x4.TRS(localPosition, localRotation, localScale); - Gizmos.DrawWireMesh(mesh); - Gizmos.matrix = mtx; - } -#endif - } -} - diff --git a/Unity.Entities.Graphics/Occlusion/Occluder.cs.meta b/Unity.Entities.Graphics/Occlusion/Occluder.cs.meta deleted file mode 100644 index d37343a..0000000 --- a/Unity.Entities.Graphics/Occlusion/Occluder.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 7c66d943f01e53542add2ad628dc91ae -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/OccluderInspector.cs b/Unity.Entities.Graphics/Occlusion/OccluderInspector.cs deleted file mode 100644 index 1ae5b4f..0000000 --- a/Unity.Entities.Graphics/Occlusion/OccluderInspector.cs +++ /dev/null @@ -1,61 +0,0 @@ -#if UNITY_EDITOR && ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using UnityEditor; -using UnityEngine; - -namespace Unity.Rendering.Occlusion -{ - [CustomEditor(typeof(Occluder))] - [CanEditMultipleObjects] - class OccluderInspector : Editor - { - - class Contents - { - public GUIContent meshContent = EditorGUIUtility.TrTextContent("Mesh", "The occluder mesh"); - public GUIContent positionContent = EditorGUIUtility.TrTextContent("Position", "The position of this occluder relative to transform."); - public GUIContent rotationContent = EditorGUIUtility.TrTextContent("Rotation", "The rotation of this occluder relative to transform."); - public GUIContent scaleContent = EditorGUIUtility.TrTextContent("Scale", "The scale of this occluder relative to transform."); - } - static Contents s_Contents; - - SerializedProperty m_Mesh; - SerializedProperty m_Position; - SerializedProperty m_Rotation; - SerializedProperty m_Scale; - - public void OnEnable() - { - m_Mesh = serializedObject.FindProperty("mesh"); - m_Position = serializedObject.FindProperty("localPosition"); - m_Rotation = serializedObject.FindProperty("localRotation"); - m_Scale = serializedObject.FindProperty("localScale"); - } - - public override void OnInspectorGUI() - { - if (s_Contents == null) - s_Contents = new Contents(); - - if (!EditorGUIUtility.wideMode) - { - EditorGUIUtility.wideMode = true; - EditorGUIUtility.labelWidth = EditorGUIUtility.currentViewWidth - 212; - } - - serializedObject.Update(); - - EditorGUILayout.PropertyField(m_Mesh, s_Contents.meshContent); - EditorGUILayout.LabelField("Local Transform"); - EditorGUI.indentLevel++; - EditorGUILayout.PropertyField(m_Position, s_Contents.positionContent); - EditorGUILayout.PropertyField(m_Rotation, s_Contents.rotationContent); - EditorGUILayout.PropertyField(m_Scale, s_Contents.scaleContent); - EditorGUI.indentLevel--; - - serializedObject.ApplyModifiedProperties(); - } - } -} - -#endif diff --git a/Unity.Entities.Graphics/Occlusion/OccluderInspector.cs.meta b/Unity.Entities.Graphics/Occlusion/OccluderInspector.cs.meta deleted file mode 100644 index 16bd680..0000000 --- a/Unity.Entities.Graphics/Occlusion/OccluderInspector.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 577b398f4c8b4374e8960ead06a9aa5f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.cs b/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.cs deleted file mode 100644 index 3a270ad..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.cs +++ /dev/null @@ -1,184 +0,0 @@ -#if UNITY_EDITOR && ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using UnityEditor; -using UnityEngine; -using UnityEngine.UIElements; -using Unity.Entities; -using System.Collections.Generic; -using System; -using UnityEngine.Rendering; -using Unity.Rendering.Occlusion.Masked; -using System.Linq; - -namespace Unity.Rendering.Occlusion -{ - class OcclusionBrowseWindow : EditorWindow - { - public static bool IsVisible; - private static OcclusionBrowseWindow Window; - - OcclusionBrowserView occlusionBrowserView; - int selectedIndex = 0; - List>> ViewSlices = new List>>(); - - [MenuItem("Occlusion/Browse")] - public static void ShowMenuItem() - { - OcclusionBrowseWindow wnd = GetWindow(); - wnd.titleContent = new GUIContent("Occlusion Browser"); - } - - internal StyleSheet StyleSheet - { - get - { - return AssetDatabase.LoadAssetAtPath("Packages/com.unity.entities.graphics/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uss"); - } - } - - public void OnEnable() - { - autoRepaintOnSceneChange = true; - rootVisualElement.Clear(); - CreateGUI(); - } - - public void OnHierarchyChange() - { - rootVisualElement.Clear(); - CreateGUI(); - - if (selectedIndex < ViewSlices.Count) - { - var (obj, bufferGroups) = ViewSlices[selectedIndex]; - OnSelectionChanged(obj, bufferGroups); - } - } - - private void OnBecameVisible() - { - IsVisible = true; - Window = GetWindow(); - } - - private void OnBecameInvisible() - { - IsVisible = false; - Window = null; - } - - public static void Refresh() - { - if (IsVisible && Window != null) - { - Window.OnHierarchyChange(); - } - } - - public void CreateGUI() - { - var root = this.rootVisualElement; - root.Clear(); - - if (World.DefaultGameObjectInjectionWorld == null) - { - return; - } - var entitiesGraphicsSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged(); - - if (entitiesGraphicsSystem.OcclusionCulling != null && entitiesGraphicsSystem.OcclusionCulling.IsEnabled) - { - var bufferGroups = entitiesGraphicsSystem.OcclusionCulling.BufferGroups; - var slices = new Dictionary>(); - - foreach (var pair in bufferGroups) - { - var id = pair.Key & 0xffffffff; - var slice = (int)(pair.Key >> 32); - - var obj = (Behaviour)EditorUtility.InstanceIDToObject((int)id); - if (obj == null) - { - // can happen if we're updating while deleting a view. - selectedIndex = 0; - continue; - } - - if (obj is Camera camera && camera.cameraType == CameraType.SceneView) - { - // always include the scene camera - } - else - { - var viewSettings = obj.gameObject.GetComponent(); - if (viewSettings == null - || !viewSettings.OcclusionEnabled - || !viewSettings.enabled - || !viewSettings.gameObject.activeInHierarchy) - { - // otherwise, ignore views without settings and views that disable occlusion - continue; - } - } - - if (!slices.TryGetValue(obj, out var list)) - { - list = new List(); - } - list.Add(pair.Value); - slices[obj] = list; - } - - ViewSlices = slices.ToList(); - } - else - { - ViewSlices = new List>>(); - } - - occlusionBrowserView = new OcclusionBrowserView(); - - selectedIndex = Math.Max(0, Math.Min(selectedIndex, ViewSlices.Count - 1)); - occlusionBrowserView.ViewList.itemsSource = ViewSlices.ConvertAll(pair => pair.Key.name); - - occlusionBrowserView.ViewList.makeItem = - () => new Label(); - occlusionBrowserView.ViewList.bindItem = - (e, i) => (e as Label).text = ViewSlices[i].Key.name; - occlusionBrowserView.ViewList.selectedIndicesChanged += - idx => - { - selectedIndex = idx.First(); - if (selectedIndex < ViewSlices.Count) - { - // make sure we don't throw when the scene changes - var (obj, bufferGroups) = ViewSlices[selectedIndex]; - OnSelectionChanged(obj, bufferGroups); - } - }; - - occlusionBrowserView.ViewList.selectedIndex = selectedIndex; - root.Add(occlusionBrowserView); - occlusionBrowserView.StretchToParentSize(); - - } - - void OnSelectionChanged(Behaviour obj, List groups) - { - BufferGroup bufferGroup = groups.First(); - - var tex = bufferGroup.GetVisualizationTexture(); - occlusionBrowserView.CurrentSliceTexture = tex; - - var infoText = $"Type: {bufferGroup.ViewType} " - + $"Size: {tex?.width}x{tex?.height} " - + $"Slice count: {groups.Count}"; - occlusionBrowserView.InfoText.text = infoText; - occlusionBrowserView.SetBufferGroups(groups); - occlusionBrowserView.SetupGUI(); - - Repaint(); - } - } -} -#endif diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.cs.meta b/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.cs.meta deleted file mode 100644 index 305b930..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: daa77134635263d42a4f0b5d3e465885 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uss b/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uss deleted file mode 100644 index eaa3f31..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uss +++ /dev/null @@ -1,94 +0,0 @@ -.horizontalContainer { - margin: 50px; - flex-direction: row; -} - -#boxesContainer { - padding: 10px; - background-color: rgb(128, 128, 128); - align-self: flex-start; - flex-shrink: 0; -} - -#boxesContainer > VisualElement { - width: 100px; - height: 100px; -} - -#TwoPlusOneContainer { - height: 100px; - flex-shrink: 0; -} - -#large { - flex: 0.7; - background-color: rgb(255, 0, 0); -} - -#small { - flex: 0.3; - background-color: rgb(0, 0, 255); -} - -#wrapContainer { - flex-wrap: wrap; -} - -#wrapContainer > VisualElement { - width: 20px; - height: 20px; - margin: 5px; - background-color: #0000FF; -} - -.editorControlDisplayer { - flex-direction: row; -} - -.editorControlDisplayer .extraField { - flex-direction: row; - justify-content: space-between; - flex: 1; -} - -.editorControlDisplayer .controlField { - flex: 1; -} - -.focusButton { - max-height: 20px; -} - -.unity-list-view { - --unity-item-height: 20; -} - -TreeView { - --unity-item-height: 15; -} - -.split-container { - flex: 1; - padding: 10px; -} - -.inner-container { - flex: 1; - overflow: hidden; -} - -.element { - flex: 1 1 100px; - background-color: #555555; - border-bottom-width: 1px; - border-color: white; - justify-content: center; - align-items: center; -} - -.element Label { - background-color: #000000; - color: #999999; - -unity-text-align: middle-center; - flex: 0 0 auto; -} diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uss.meta b/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uss.meta deleted file mode 100644 index 4e6bb46..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uss.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6bd483c2ca1589a43ad4872f7c97a9e4 -ScriptedImporter: - internalIDToNameTable: [] - externalObjects: {} - serializedVersion: 2 - userData: - assetBundleName: - assetBundleVariant: - script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} - disableValidation: 0 diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uxml b/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uxml deleted file mode 100644 index f49966b..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uxml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uxml.meta b/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uxml.meta deleted file mode 100644 index 4e62106..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionBrowseWindow.uxml.meta +++ /dev/null @@ -1,10 +0,0 @@ -fileFormatVersion: 2 -guid: 2804fd14938e5e04b95bf1b7b80a717e -ScriptedImporter: - internalIDToNameTable: [] - externalObjects: {} - serializedVersion: 2 - userData: - assetBundleName: - assetBundleVariant: - script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0} diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionDebugRenderSystem.cs b/Unity.Entities.Graphics/Occlusion/OcclusionDebugRenderSystem.cs deleted file mode 100644 index 919e557..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionDebugRenderSystem.cs +++ /dev/null @@ -1,129 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using Unity.Collections; -using Unity.Entities; -using Unity.Mathematics; -using UnityEngine; -using UnityEngine.Rendering; -using Unity.Rendering.Occlusion.Masked; -using Unity.Rendering.Occlusion.Masked.Visualization; - -namespace Unity.Rendering.Occlusion -{ - - /* This system renders debug visualizations for occlusion culling. - For each debug view: - 1. It unpacks the CPU-rasterized masked depth buffer into a human-readable depth buffer - 2. It renders the visualization (such as culled occludees, or occluder meshes) into an overlay texture - 3. At the end of every frame, it composites these textures to create the final debug visualization */ - [WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)] - [UpdateInGroup(typeof(PresentationSystemGroup))] - [UpdateAfter(typeof(EntitiesGraphicsSystem))] - partial class OcclusionDebugRenderSystem : SystemBase - { - private EntitiesGraphicsSystem entitiesGraphicsSystem; - private CommandBuffer cmdComposite = new CommandBuffer() { name = "Occlusion debug composite" }; - private Mesh fullScreenQuad; - - struct QuadVertex - { - public float4 pos; - public float2 uv; - } - - protected void SetFullScreenQuad() - { - - var layout = new[] - { - new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 4), - new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2), - }; - - fullScreenQuad = new Mesh(); - fullScreenQuad.SetVertexBufferParams(4, layout); - - var verts = new NativeArray(4, Allocator.Temp); - - verts[0] = new QuadVertex() { pos = new float4(-1f, -1f, 1, 1), uv = new float2(0, 1) }; - verts[1] = new QuadVertex() { pos = new float4(1f, -1f, 1, 1), uv = new float2(1, 1) }; - verts[2] = new QuadVertex() { pos = new float4(1f, 1f, 1, 1), uv = new float2(1, 0) }; - verts[3] = new QuadVertex() { pos = new float4(-1f, 1f, 1, 1), uv = new float2(0, 0) }; - - fullScreenQuad.SetVertexBufferData(verts, 0, 0, 4); - verts.Dispose(); - - var tris = new int[6] { 0, 1, 2, 0, 2, 3 }; - fullScreenQuad.SetIndices(tris, MeshTopology.Triangles, 0); - fullScreenQuad.bounds = new Bounds(Vector3.zero, new Vector3(10000, 10000, 1000)); - } - - /// - protected override void OnCreate() - { - // Create full-screen quad - SetFullScreenQuad(); - - RenderPipelineManager.endFrameRendering += RenderComposite; - } - - /// - protected override void OnDestroy() - { - RenderPipelineManager.endFrameRendering -= RenderComposite; - } - - /// - protected override void OnUpdate() - { - - } - - // At the end of every frame, composite the depth and overlay texture for each view to create the final debug - // visualization - void RenderComposite(ScriptableRenderContext ctx, Camera[] cameras) - { - if (World.DefaultGameObjectInjectionWorld == null) - { - return; - } - - if (entitiesGraphicsSystem == null) - { - entitiesGraphicsSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged(); - } - - OcclusionCulling oc = entitiesGraphicsSystem.OcclusionCulling; - - if (!oc.IsEnabled || - oc.debugSettings.debugRenderMode == DebugRenderMode.None || - oc.debugSettings.debugRenderMode == DebugRenderMode.Inverted) - return; - - // This happens when reloading scenes - if(fullScreenQuad == null) - { - SetFullScreenQuad(); - } - - cmdComposite.Clear(); - - ulong? pinnedViewID = oc.debugSettings.GetPinnedViewID(); - - foreach (Camera camera in cameras) - { - ulong id = pinnedViewID.HasValue ? - // A view is pinned. Pick its ID instead of the current camera's ID. - pinnedViewID.Value : - // No view is pinned. Pick the current camera's ID. - (ulong) ((uint)camera.GetInstanceID()); - if (!oc.BufferGroups.TryGetValue(id, out BufferGroup bufferGroup)) - continue; - bufferGroup.RenderToCamera(oc.debugSettings.debugRenderMode, camera, cmdComposite, fullScreenQuad); - } - Graphics.ExecuteCommandBuffer(cmdComposite); - } - } -} - -#endif diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionDebugRenderSystem.cs.meta b/Unity.Entities.Graphics/Occlusion/OcclusionDebugRenderSystem.cs.meta deleted file mode 100644 index 3130934..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionDebugRenderSystem.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 24d768ae8edcfe341b46baf92c520e75 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionMenu.cs b/Unity.Entities.Graphics/Occlusion/OcclusionMenu.cs deleted file mode 100644 index 3c3dd74..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionMenu.cs +++ /dev/null @@ -1,149 +0,0 @@ -#if UNITY_EDITOR && ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using UnityEditor; -using UnityEngine; -using Unity.Rendering.Occlusion; -using System.Collections.Generic; -using System; -using System.Linq; -using Unity.Entities; -using Object = UnityEngine.Object; -using UnityEditor.SceneManagement; - -namespace Unity.Rendering.Occlusion -{ - static class OcclusionCommands - { - const string kOcclusionMenu = "Occlusion/"; - - const string kOcclusionToolsSubMenu = "Tools/"; - - const string kOcclusionDebugSubMenu = kOcclusionMenu + "Debug/"; - - const string kDebugNone = kOcclusionDebugSubMenu + "None"; - const string kDebugDepth = kOcclusionDebugSubMenu + "Depth buffer"; - const string kDebugShowMeshes = kOcclusionDebugSubMenu + "Show occluder meshes"; - const string kDebugShowBounds = kOcclusionDebugSubMenu + "Show occludee bounds"; - const string kDebugShowTest = kOcclusionDebugSubMenu + "Show depth test"; - - const string kOcclusionEnable = kOcclusionMenu + "Enable"; - const string kOcclusionDisplayOccluded = "Occlusion/DisplayOccluded"; - const string kOcclusionParallel = kOcclusionMenu + "Parallel Rasterization"; - - [MenuItem(kOcclusionEnable, false)] - static void ToggleOcclusionEnable() - { - if (World.DefaultGameObjectInjectionWorld != null) - { - var occlusionCulling = World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged().OcclusionCulling; - occlusionCulling.IsEnabled = !occlusionCulling.IsEnabled; - - OcclusionBrowseWindow.Refresh(); - } - } - - [MenuItem(kOcclusionEnable, true)] - static bool ValidateOcclusionEnable() - { - if (World.DefaultGameObjectInjectionWorld != null) - { - var occlusionCulling = World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged().OcclusionCulling; - Menu.SetChecked(kOcclusionEnable, occlusionCulling.IsEnabled); - } - return true; - } - - static void AddComponentIfNeeded(this MeshRenderer meshRenderer) where T : MonoBehaviour - { - var gameObject = meshRenderer.gameObject; - if (!gameObject.TryGetComponent(out var occluder)) - { - occluder = gameObject.AddComponent(); - occluder.enabled = true; - } - } - - static void DestroyComponentIfNeeded(this MeshRenderer meshRenderer) where T : MonoBehaviour - { - var gameObject = meshRenderer.gameObject; - if (gameObject.TryGetComponent(out var occluder)) - Object.DestroyImmediate(occluder); - } - - [MenuItem(kOcclusionMenu + kOcclusionToolsSubMenu + "Add occlusion components to all open scenes and objects")] - static void AddAllOcclusionComponents() - { - ForEachRenderer((meshRenderer) => - { - meshRenderer.AddComponentIfNeeded(); - }, OccluderEditMode.AllObjects); - } - - [MenuItem(kOcclusionMenu + kOcclusionToolsSubMenu + "Remove occlusion components from all open scenes and objects")] - static void RemoveAllOcclusionComponents() - { - ForEachRenderer((meshRenderer) => - { - meshRenderer.DestroyComponentIfNeeded(); - }, OccluderEditMode.AllObjects); - } - - [MenuItem(kOcclusionMenu + kOcclusionToolsSubMenu + "Add occlusion components to selected")] - static void AddOcclusionComponentsToSelected() - { - ForEachRenderer((meshRenderer) => - { - meshRenderer.AddComponentIfNeeded(); - }, OccluderEditMode.SelectedObjects); - } - - [MenuItem(kOcclusionMenu + kOcclusionToolsSubMenu + "Remove occlusion components from selected")] - static void RemoveOcclusionComponentsFromSelected() - { - ForEachRenderer((meshRenderer) => - { - meshRenderer.DestroyComponentIfNeeded(); - }, OccluderEditMode.SelectedObjects); - } - - [MenuItem(kOcclusionMenu + kOcclusionToolsSubMenu + "Add occluder component to selected")] - static void AddOccluderComponentToSelected() - { - ForEachRenderer((meshRenderer) => - { - meshRenderer.AddComponentIfNeeded(); - }, OccluderEditMode.SelectedObjects); - } - - [MenuItem(kOcclusionMenu + kOcclusionToolsSubMenu + "Add occluder component to all open scenes and objects")] - static void AddOccluderComponentToAll() - { - ForEachRenderer((meshRenderer) => - { - meshRenderer.AddComponentIfNeeded(); - }, OccluderEditMode.AllObjects); - } - - enum OccluderEditMode - { - AllObjects, - SelectedObjects, - } - - static void ForEachRenderer(Action action, OccluderEditMode mode) - { - var renderers = mode == OccluderEditMode.AllObjects - ? Object.FindObjectsByType(FindObjectsSortMode.None) - : Selection.gameObjects.SelectMany(x => x.GetComponents()); - renderers = renderers.Distinct(); - - foreach (var renderer in renderers) - { - action(renderer); - EditorSceneManager.MarkSceneDirty(renderer.gameObject.scene); - } - } - } -} -#endif - diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionMenu.cs.meta b/Unity.Entities.Graphics/Occlusion/OcclusionMenu.cs.meta deleted file mode 100644 index d36483c..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionMenu.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b775b039e68f9204fabbab91eff5c1a5 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionSortJob.cs b/Unity.Entities.Graphics/Occlusion/OcclusionSortJob.cs deleted file mode 100644 index 150f0de..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionSortJob.cs +++ /dev/null @@ -1,18 +0,0 @@ -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using System.Collections.Generic; -using Unity.Rendering.Occlusion.Masked; - - -namespace Unity.Rendering.Occlusion -{ - struct Compare : IComparer - { - int IComparer.Compare(ClippedOccluder x, ClippedOccluder y) - { - return x.screenMin.z.CompareTo(y.screenMin.z); - } - } -} - -#endif diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionSortJob.cs.meta b/Unity.Entities.Graphics/Occlusion/OcclusionSortJob.cs.meta deleted file mode 100644 index 089ca63..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionSortJob.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: be9209ce1b22cbe43b49dc9c4f76ebb9 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionView.cs b/Unity.Entities.Graphics/Occlusion/OcclusionView.cs deleted file mode 100644 index 420fa3d..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionView.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Unity.Entities; -using UnityEditor; -using UnityEngine; - -namespace Unity.Rendering.Occlusion -{ - - /// - /// Represents occlusion view settings. - /// - public struct OcclusionViewSettings - { - /// - /// Indicates whether to process occlusion culling for the occlusion view. - /// - public bool enabled; - /// - /// The width of the occlusion buffer. - /// - public uint width; - /// - /// The height of the occlusion buffer. - /// - public uint height; - } - - /// - /// Explicitly specifies which frustum views are occlusion views and configures occlusion view settings. - /// - [ExecuteInEditMode] - [DisallowMultipleComponent] - public class OcclusionView : MonoBehaviour - { - /// - /// Indicates whether to process occlusion culling for the attached frustum view. - /// - public bool OcclusionEnabled = true; - - /// - /// The width of the occlusion buffer. - /// - public uint OcclusionBufferWidth = DefaultBufferSize; - - /// - /// The height of the occlusion buffer. - /// - public uint OcclusionBufferHeight = DefaultBufferSize; - - /// - /// The default value for the occlusion buffer height and width. - /// - public static readonly uint DefaultBufferSize = 512; - -#if ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - - void Update() - { - if (World.DefaultGameObjectInjectionWorld == null) - return; - - var entitiesGraphicsSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged(); - - if (entitiesGraphicsSystem != null) - { - var occlusion = entitiesGraphicsSystem.OcclusionCulling; - if (occlusion != null) - { - occlusion.UpdateSettings(this); - } - } - } - - public void OnValidate() - { - Update(); -#if UNITY_EDITOR - OcclusionBrowseWindow.Refresh(); -#endif - } - - private void OnDestroy() - { - if (World.DefaultGameObjectInjectionWorld == null) - return; - - var entitiesGraphicsSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystemManaged(); - - if (entitiesGraphicsSystem != null) - { - var occlusion = entitiesGraphicsSystem.OcclusionCulling; - if (occlusion != null) - { - occlusion.UpdateSettings(this); - } - } - } -#endif - } -} diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionView.cs.meta b/Unity.Entities.Graphics/Occlusion/OcclusionView.cs.meta deleted file mode 100644 index 79824b1..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionView.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f7be0320cff28664c857bf079ae21d0b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionViewEditor.cs b/Unity.Entities.Graphics/Occlusion/OcclusionViewEditor.cs deleted file mode 100644 index c077899..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionViewEditor.cs +++ /dev/null @@ -1,69 +0,0 @@ -#if UNITY_EDITOR && ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using UnityEditor; -using UnityEngine; -using Unity.Rendering.Occlusion; - -[CustomEditor(typeof(OcclusionView))] -[CanEditMultipleObjects] -public class OcclusionViewEditor : Editor -{ - SerializedProperty enabled; - SerializedProperty width; - SerializedProperty height; - string displayWarning; - - void OnEnable() - { - enabled = serializedObject.FindProperty("OcclusionEnabled"); - width = serializedObject.FindProperty("OcclusionBufferWidth"); - height = serializedObject.FindProperty("OcclusionBufferHeight"); - displayWarning = ""; - } - - public override void OnInspectorGUI() - { - serializedObject.Update(); - - int oldWidth = width.intValue; - int oldHeight = height.intValue; - - EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(enabled); - EditorGUILayout.DelayedIntField(width); - EditorGUILayout.DelayedIntField(height); - - if (displayWarning != "") - { - EditorGUILayout.HelpBox(displayWarning, MessageType.Warning); - } - - if (EditorGUI.EndChangeCheck()) - { - displayWarning = ""; - - // TODO: The valid multiples are dependent on the tile count per bin, which is currently - // hardcoded to (2,4). Fix this once that's driven by the view settings. - if (width.intValue <= 0 || width.intValue % 64 != 0) - { - InvalidDimensionWarning("width", width.intValue, 64); - width.intValue = oldWidth; - } - - if (height.intValue <= 0 || height.intValue % 64 != 0) - { - InvalidDimensionWarning("height", height.intValue, 64); - height.intValue = oldHeight; - } - - serializedObject.ApplyModifiedProperties(); - } - } - - private void InvalidDimensionWarning(string dimName, int dim, int mul) - { - displayWarning = $"Invalid occlusion buffer {dimName} = {dim}. The {dimName} must be a multiple of {mul} greater than zero."; - } -} - -#endif diff --git a/Unity.Entities.Graphics/Occlusion/OcclusionViewEditor.cs.meta b/Unity.Entities.Graphics/Occlusion/OcclusionViewEditor.cs.meta deleted file mode 100644 index 3168aa7..0000000 --- a/Unity.Entities.Graphics/Occlusion/OcclusionViewEditor.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 707ac57dcd191724ea4633f2ee589f37 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/UI.meta b/Unity.Entities.Graphics/Occlusion/UI.meta deleted file mode 100644 index ceca848..0000000 --- a/Unity.Entities.Graphics/Occlusion/UI.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: cb15b63cfdd316a42beba08e88061ef9 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Graphics/Occlusion/UI/OcclusionBrowserView.cs b/Unity.Entities.Graphics/Occlusion/UI/OcclusionBrowserView.cs deleted file mode 100644 index d692318..0000000 --- a/Unity.Entities.Graphics/Occlusion/UI/OcclusionBrowserView.cs +++ /dev/null @@ -1,243 +0,0 @@ -#if UNITY_EDITOR && ENABLE_UNITY_OCCLUSION && (HDRP_10_0_0_OR_NEWER || URP_10_0_0_OR_NEWER) - -using System; -using System.Collections.Generic; -using Unity.Mathematics; -using Unity.Rendering.Occlusion.Masked; -using UnityEngine; -using UnityEngine.UIElements; - -public class OcclusionBrowserView : VisualElement -{ - public string ViewName - { - get => viewName.text; - set => viewName.text = value; - } - - public int CurrentSlice { get; set; } - - public Texture2D CurrentSliceTexture { get; set; } - - List BufferGroups { get; set; } - - public OcclusionBrowserView() - { - VisualTreeAsset template = UnityEditor.AssetDatabase.LoadAssetAtPath("Packages/com.unity.entities.graphics/Unity.Entities.Graphics/Occlusion/UI/OcclusionBrowserView.uxml"); - template.CloneTree(this); - - Initialize(); - } - - private VisualElement container; - - private Label viewName; - private Label statusBar, statusBar2; - private Button nextSlice, prevSlice; - private VisualElement viewData; - private VisualElement sliceList; - - /// - public override VisualElement contentContainer => this.container; - - public ListView ViewList { get; private set; } - public Label InfoText { get => statusBar2; } - - public void Reset() - { - bool hasSlices = BufferGroups.Count > 1; - nextSlice.SetEnabled(hasSlices); - prevSlice.SetEnabled(hasSlices); - - nextSlice.clicked += () => Debug.Log("Next"); - prevSlice.clicked += () => Debug.Log("Prev"); - } - - private void Initialize() - { - viewName = this.Q