From e455ada90a034e6d8d971d68685274ab37a79d2e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Jun 2021 14:22:46 +0200 Subject: [PATCH] Optimize Debug Draw wireframe sphere to use auto-LOD --- Source/Engine/Debug/DebugDraw.cpp | 118 +++++++++++++++++------------- 1 file changed, 68 insertions(+), 50 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 883c50882..ac2b744bc 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -16,6 +16,7 @@ #include "Engine/Graphics/GPUPipelineState.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderBuffers.h" +#include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/DynamicBuffer.h" #include "Engine/Graphics/Shaders/GPUConstantBuffer.h" #include "Engine/Graphics/Shaders/GPUShader.h" @@ -31,9 +32,11 @@ // Debug draw service configuration #define DEBUG_DRAW_INITIAL_VB_CAPACITY (4 * 1024) // -#define DEBUG_DRAW_SPHERE_RESOLUTION 64 -#define DEBUG_DRAW_SPHERE_LINES_COUNT (DEBUG_DRAW_SPHERE_RESOLUTION + 1) * 3 -#define DEBUG_DRAW_SPHERE_VERTICES DEBUG_DRAW_SPHERE_LINES_COUNT * 2 +#define DEBUG_DRAW_SPHERE_LOD0_RESOLUTION 64 +#define DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE 0.2f +#define DEBUG_DRAW_SPHERE_LOD1_RESOLUTION 16 +#define DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE 0.08f +#define DEBUG_DRAW_SPHERE_LOD2_RESOLUTION 8 // #define DEBUG_DRAW_CIRCLE_RESOLUTION 32 #define DEBUG_DRAW_CIRCLE_LINES_COUNT DEBUG_DRAW_CIRCLE_RESOLUTION @@ -44,6 +47,39 @@ // #define DEBUG_DRAW_TRIANGLE_SPHERE_RESOLUTION 12 +struct DebugSphereCache +{ + Array> Vertices; + + void Init(int32 resolution) + { + const int32 verticesCount = (resolution + 1) * 3 * 2; + Vertices.Resize(verticesCount); + int32 index = 0; + const float step = TWO_PI / (float)resolution; + for (float a = 0.0f; a < TWO_PI; a += step) + { + // Calculate sines and cosines + const float sinA = Math::Sin(a); + const float cosA = Math::Cos(a); + const float sinB = Math::Sin(a + step); + const float cosB = Math::Cos(a + step); + + // XY loop + Vertices[index++] = Vector3(cosA, sinA, 0.0f); + Vertices[index++] = Vector3(cosB, sinB, 0.0f); + + // XZ loop + Vertices[index++] = Vector3(cosA, 0.0f, sinA); + Vertices[index++] = Vector3(cosB, 0.0f, sinB); + + // YZ loop + Vertices[index++] = Vector3(0.0f, cosA, sinA); + Vertices[index++] = Vector3(0.0f, cosB, sinB); + } + } +}; + struct DebugLine { Vector3 Start; @@ -248,6 +284,8 @@ struct DebugDrawContext { DebugDrawData DebugDrawDefault; DebugDrawData DebugDrawDepthTest; + Vector3 LastViewPos = Vector3::Zero; + Matrix LastViewProj = Matrix::Identity; }; namespace @@ -263,9 +301,9 @@ namespace PsData DebugDrawPsTrianglesDefault; PsData DebugDrawPsTrianglesDepthTest; DynamicVertexBuffer* DebugDrawVB = nullptr; - Vector3 SphereCache[DEBUG_DRAW_SPHERE_VERTICES]; Vector3 CircleCache[DEBUG_DRAW_CIRCLE_VERTICES]; Array SphereTriangleCache; + DebugSphereCache SphereCache[3]; }; extern int32 BoxTrianglesIndicesCache[]; @@ -396,32 +434,13 @@ bool DebugDrawService::Init() Context = &GlobalContext; // Init wireframe sphere cache - int32 index = 0; - float step = TWO_PI / DEBUG_DRAW_SPHERE_RESOLUTION; - for (float a = 0.0f; a < TWO_PI; a += step) - { - // Calculate sines and cosines - float sinA = Math::Sin(a); - float cosA = Math::Cos(a); - float sinB = Math::Sin(a + step); - float cosB = Math::Cos(a + step); - - // XY loop - SphereCache[index++] = Vector3(cosA, sinA, 0.0f); - SphereCache[index++] = Vector3(cosB, sinB, 0.0f); - - // XZ loop - SphereCache[index++] = Vector3(cosA, 0.0f, sinA); - SphereCache[index++] = Vector3(cosB, 0.0f, sinB); - - // YZ loop - SphereCache[index++] = Vector3(0.0f, cosA, sinA); - SphereCache[index++] = Vector3(0.0f, cosB, sinB); - } + SphereCache[0].Init(DEBUG_DRAW_SPHERE_LOD0_RESOLUTION); + SphereCache[1].Init(DEBUG_DRAW_SPHERE_LOD1_RESOLUTION); + SphereCache[2].Init(DEBUG_DRAW_SPHERE_LOD2_RESOLUTION); // Init wireframe circle cache - index = 0; - step = TWO_PI / DEBUG_DRAW_CIRCLE_RESOLUTION; + int32 index = 0; + float step = TWO_PI / (float)DEBUG_DRAW_CIRCLE_RESOLUTION; for (float a = 0.0f; a < TWO_PI; a += step) { // Calculate sines and cosines @@ -648,6 +667,8 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe if (renderContext.Buffers == nullptr || !DebugDrawVB) return; auto context = GPUDevice::Instance->GetMainContext(); + Context->LastViewPos = renderContext.View.Position; + Context->LastViewProj = renderContext.View.Projection; // Fallback to task buffers if (target == nullptr && renderContext.Task) @@ -923,21 +944,6 @@ void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& } } -#define DRAW_WIRE_BOX_LINE(i0, i1, list) l.Start = corners[i0]; l.End = corners[i1]; Context->list.Add(l) -#define DRAW_WIRE_BOX(list) \ - DRAW_WIRE_BOX_LINE(0, 1, list); \ - DRAW_WIRE_BOX_LINE(0, 3, list); \ - DRAW_WIRE_BOX_LINE(0, 4, list); \ - DRAW_WIRE_BOX_LINE(1, 2, list); \ - DRAW_WIRE_BOX_LINE(1, 5, list); \ - DRAW_WIRE_BOX_LINE(2, 3, list); \ - DRAW_WIRE_BOX_LINE(2, 6, list); \ - DRAW_WIRE_BOX_LINE(3, 7, list); \ - DRAW_WIRE_BOX_LINE(4, 5, list); \ - DRAW_WIRE_BOX_LINE(4, 7, list); \ - DRAW_WIRE_BOX_LINE(5, 6, list); \ - DRAW_WIRE_BOX_LINE(6, 7, list) - void DebugDraw::DrawWireBox(const BoundingBox& box, const Color& color, float duration, bool depthTest) { // Get corners @@ -1036,26 +1042,37 @@ void DebugDraw::DrawWireBox(const OrientedBoundingBox& box, const Color& color, void DebugDraw::DrawWireSphere(const BoundingSphere& sphere, const Color& color, float duration, bool depthTest) { + // Select LOD + int32 index; + const float screenRadiusSquared = RenderTools::ComputeBoundsScreenRadiusSquared(sphere.Center, sphere.Radius, Context->LastViewPos, Context->LastViewProj); + if (screenRadiusSquared > DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE * DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE * 0.25f) + index = 0; + else if (screenRadiusSquared > DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE * DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE * 0.25f) + index = 1; + else + index = 2; + auto& cache = SphereCache[index]; + // Draw lines of the unit sphere after linear transform auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; if (duration > 0) { DebugLine l = { Vector3::Zero, Vector3::Zero, Color32(color), duration }; - for (int32 i = 0; i < DEBUG_DRAW_SPHERE_VERTICES;) + for (int32 i = 0; i < cache.Vertices.Count();) { - l.Start = sphere.Center + SphereCache[i++] * sphere.Radius; - l.End = sphere.Center + SphereCache[i++] * sphere.Radius; + l.Start = sphere.Center + cache.Vertices.Get()[i++] * sphere.Radius; + l.End = sphere.Center + cache.Vertices.Get()[i++] * sphere.Radius; debugDrawData.DefaultLines.Add(l); } } else { Vertex l = { Vector3::Zero, Color32(color) }; - for (int32 i = 0; i < DEBUG_DRAW_SPHERE_VERTICES;) + for (int32 i = 0; i < cache.Vertices.Count();) { - l.Position = sphere.Center + SphereCache[i++] * sphere.Radius; + l.Position = sphere.Center + cache.Vertices.Get()[i++] * sphere.Radius; debugDrawData.OneFrameLines.Add(l); - l.Position = sphere.Center + SphereCache[i++] * sphere.Radius; + l.Position = sphere.Center + cache.Vertices.Get()[i++] * sphere.Radius; debugDrawData.OneFrameLines.Add(l); } } @@ -1263,7 +1280,8 @@ void DebugDraw::DrawWireTube(const Vector3& position, const Quaternion& orientat else { // Set up - const float step = TWO_PI / DEBUG_DRAW_SPHERE_RESOLUTION; + const int32 resolution = 64; + const float step = TWO_PI / (float)resolution; radius = Math::Max(radius, 0.05f); length = Math::Max(length, 0.05f); const float halfLength = length / 2.0f;