From 0be1a3cbde2e548da34ed54176058c99adf9b626 Mon Sep 17 00:00:00 2001 From: Dynamitos Date: Thu, 29 Aug 2024 11:23:55 +0200 Subject: [PATCH] Fixing Depth culling out of bounds errors --- res/shaders/DepthCommon.slang | 19 +++++++ res/shaders/DepthCullingTask.slang | 53 +++++++++++------ res/shaders/DepthMipGen.slang | 6 +- res/shaders/lib/Bounding.slang | 7 ++- src/Engine/Graphics/RenderPass/BasePass.cpp | 2 +- .../Graphics/RenderPass/DepthCullingPass.cpp | 57 ++++++++++++++++--- .../Graphics/RenderPass/DepthCullingPass.h | 13 +++++ src/Engine/Graphics/Vulkan/Graphics.cpp | 1 + src/Engine/Material/Material.cpp | 22 +++---- 9 files changed, 136 insertions(+), 44 deletions(-) create mode 100644 res/shaders/DepthCommon.slang diff --git a/res/shaders/DepthCommon.slang b/res/shaders/DepthCommon.slang new file mode 100644 index 0000000..e353f59 --- /dev/null +++ b/res/shaders/DepthCommon.slang @@ -0,0 +1,19 @@ +struct DepthDebugData +{ + uint mipLevel; + uint mipOffset; + int2 mipDimensions; + int2 screenCornerMin; + int2 screenCornerMax; + int2 origScreenMin; + int2 origScreenMax; +}; + +struct DepthData +{ + //uint bufferLength; + Texture2D texture; + RWStructuredBuffer buffer; + //globallycoherent RWStructuredBuffer debugHead; + //globallycoherent RWStructuredBuffer debugData; +}; diff --git a/res/shaders/DepthCullingTask.slang b/res/shaders/DepthCullingTask.slang index 90b8b0c..5ede2e3 100644 --- a/res/shaders/DepthCullingTask.slang +++ b/res/shaders/DepthCullingTask.slang @@ -1,6 +1,7 @@ import Common; import Scene; import Bounding; +import DepthCommon; groupshared MeshPayload p; groupshared uint head; @@ -10,37 +11,55 @@ groupshared Frustum viewFrustum; groupshared float4x4 modelViewProjection; groupshared bool meshVisible; -struct DepthData -{ - Texture2D texture; - RWStructuredBuffer buffer; -} +//void writeDebug(uint mipLevel, uint mipOffset, int2 mipDimensions, int2 screenMin, int2 screenMax, int2 origScreenMin, int2 origScreenMax) +//{ +// uint index = 0; +// InterlockedAdd(pDepthAttachment.debugHead[0], 1, index); +// index = min(index, 10000); +// { +// pDepthAttachment.debugData[index].mipLevel = mipLevel; +// pDepthAttachment.debugData[index].mipOffset = mipOffset; +// pDepthAttachment.debugData[index].mipDimensions = mipDimensions; +// pDepthAttachment.debugData[index].screenCornerMax = screenMax; +// pDepthAttachment.debugData[index].screenCornerMin = screenMin; +// pDepthAttachment.debugData[index].origScreenMax = origScreenMax; +// pDepthAttachment.debugData[index].origScreenMin = origScreenMin; +// } +//} ParameterBlock pDepthAttachment; - bool isBoxVisible(AABB bounding) { - uint2 mipDimensions = uint2(uint(pViewParams.screenDimensions.x), uint(pViewParams.screenDimensions.y)); + int2 mipDimensions = uint2(uint(pViewParams.screenDimensions.x), uint(pViewParams.screenDimensions.y)); // now we calculate what mip level we need to only sample up to 4 texels covering the entire meshlet - uint2 screenCornerMin = mipDimensions; - uint2 screenCornerMax = uint2(0, 0); + int2 screenCornerMin = mipDimensions; + int2 screenCornerMax = uint2(0, 0); // lower values are closer float maxDepth = bounding.projectScreenDepth(modelViewProjection, screenCornerMin, screenCornerMax); uint mipOffset = 0; +// uint mipLevel = 0; +// int2 origScreenMin = screenCornerMin; +// int2 origScreenMax = screenCornerMax; // in theory this wouldnt work if no corner was in screen, as min would be greater that max, however we verified that with view culling while(screenCornerMax.x - screenCornerMin.x > 1 || screenCornerMax.y - screenCornerMin.y > 1) { +// mipLevel++; mipOffset += mipDimensions.x * mipDimensions.y; - mipDimensions = uint2(mipDimensions.x + 1, mipDimensions.y + 1) / 2; - screenCornerMin = uint2(screenCornerMin.x + 1, screenCornerMin.y + 1) / 2; - screenCornerMax = uint2(screenCornerMax.x, screenCornerMax.y) / 2; + mipDimensions = int2(mipDimensions.x + 1, mipDimensions.y + 1) / 2; + screenCornerMin = int2(screenCornerMin.x + 1, screenCornerMin.y + 1) / 2; + screenCornerMax = int2(screenCornerMax.x, screenCornerMax.y) / 2; } + uint i0 = mipOffset + (screenCornerMin.y * mipDimensions.x) + screenCornerMin.x; + uint i1 = mipOffset + (screenCornerMin.y * mipDimensions.x) + screenCornerMax.x; + uint i2 = mipOffset + (screenCornerMax.y * mipDimensions.x) + screenCornerMin.x; + uint i3 = mipOffset + (screenCornerMax.y * mipDimensions.x) + screenCornerMax.x; + // now we sample 4 texels from the depth at the calculated mip level, this should give us the screen extent of the meshlet - float d1 = pDepthAttachment.buffer[mipOffset + (screenCornerMin.y * mipDimensions.x) + screenCornerMin.x]; - float d2 = pDepthAttachment.buffer[mipOffset + (screenCornerMin.y * mipDimensions.x) + screenCornerMax.x]; - float d3 = pDepthAttachment.buffer[mipOffset + (screenCornerMax.y * mipDimensions.x) + screenCornerMin.x]; - float d4 = pDepthAttachment.buffer[mipOffset + (screenCornerMax.y * mipDimensions.x) + screenCornerMax.x]; + float d1 = pDepthAttachment.buffer[i0]; + float d2 = pDepthAttachment.buffer[i1]; + float d3 = pDepthAttachment.buffer[i2]; + float d4 = pDepthAttachment.buffer[i3]; // we want to check if the minimum depth (the value farthest away) is smaller than the maximum bounding box depth // otherwise, there is no way for the meshlet to be visible @@ -102,7 +121,7 @@ void taskMain( { #ifdef DEPTH_CULLING // if the meshlet bounding box is behind the cached depth buffer, we skip - //if(isBoxVisible(meshlet.bounding)) + if(isBoxVisible(meshlet.bounding)) #endif { uint index; diff --git a/res/shaders/DepthMipGen.slang b/res/shaders/DepthMipGen.slang index 9fa73f0..338e6e4 100644 --- a/res/shaders/DepthMipGen.slang +++ b/res/shaders/DepthMipGen.slang @@ -1,10 +1,6 @@ import Common; +import DepthCommon; -struct DepthData -{ - Texture2D texture; - RWStructuredBuffer buffer; -} ParameterBlock pDepthAttachment; struct MipParam diff --git a/res/shaders/lib/Bounding.slang b/res/shaders/lib/Bounding.slang index baa3269..b849cc3 100644 --- a/res/shaders/lib/Bounding.slang +++ b/res/shaders/lib/Bounding.slang @@ -51,7 +51,7 @@ struct AABB } return result; } - float projectScreenDepth(float4x4 mvp, inout uint2 screenCornerMin, inout uint2 screenCornerMax) + float projectScreenDepth(float4x4 mvp, inout int2 screenCornerMin, inout int2 screenCornerMax) { float maxDepth = 0; float4 corners[8]; @@ -67,8 +67,9 @@ struct AABB { float4 clipCorner = mul(mvp, corners[i]); float4 screenCorner = clipToScreen(clipCorner); - screenCornerMin = uint2(min(screenCornerMin.x, uint(screenCorner.x)), min(screenCornerMin.y, uint(screenCorner.y))); - screenCornerMax = uint2(max(screenCornerMax.x, uint(screenCorner.x)), max(screenCornerMax.y, uint(screenCorner.y))); + int2 screenCoords = int2(clamp(int(screenCorner.x), 0, int(pViewParams.screenDimensions.x)), clamp(int(screenCorner.y), 0, int(pViewParams.screenDimensions.y))); + screenCornerMin = int2(min(screenCornerMin.x, screenCoords.x), min(screenCornerMin.y, screenCoords.y)); + screenCornerMax = int2(max(screenCornerMax.x, screenCoords.x), max(screenCornerMax.y, screenCoords.y)); maxDepth = max(maxDepth, screenCorner.z); } return maxDepth; diff --git a/src/Engine/Graphics/RenderPass/BasePass.cpp b/src/Engine/Graphics/RenderPass/BasePass.cpp index 74d13e7..8d528b8 100644 --- a/src/Engine/Graphics/RenderPass/BasePass.cpp +++ b/src/Engine/Graphics/RenderPass/BasePass.cpp @@ -472,7 +472,7 @@ void BasePass::createRenderPass() { oLightGrid = resources->requestTexture("LIGHTCULLING_OLIGHTGRID"); tLightGrid = resources->requestTexture("LIGHTCULLING_TLIGHTGRID"); - waterRenderer->setViewport(viewport, renderPass); + //waterRenderer->setViewport(viewport, renderPass); // Debug rendering { diff --git a/src/Engine/Graphics/RenderPass/DepthCullingPass.cpp b/src/Engine/Graphics/RenderPass/DepthCullingPass.cpp index 67d021d..fabaf19 100644 --- a/src/Engine/Graphics/RenderPass/DepthCullingPass.cpp +++ b/src/Engine/Graphics/RenderPass/DepthCullingPass.cpp @@ -6,6 +6,10 @@ using namespace Seele; DepthCullingPass::DepthCullingPass(Gfx::PGraphics graphics, PScene scene) : RenderPass(graphics, scene) { depthAttachmentLayout = graphics->createDescriptorLayout("pDepthAttachment"); + //depthAttachmentLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + // .binding = 0, + // .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + //}); depthAttachmentLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .binding = 0, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLED_IMAGE, @@ -16,6 +20,16 @@ DepthCullingPass::DepthCullingPass(Gfx::PGraphics graphics, PScene scene) : Rend .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, .shaderStages = Gfx::SE_SHADER_STAGE_TASK_BIT_EXT | Gfx::SE_SHADER_STAGE_COMPUTE_BIT, }); + //depthAttachmentLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + // .binding = 3, + // .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + // .shaderStages = Gfx::SE_SHADER_STAGE_TASK_BIT_EXT | Gfx::SE_SHADER_STAGE_COMPUTE_BIT, + //}); + //depthAttachmentLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + // .binding = 4, + // .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + // .shaderStages = Gfx::SE_SHADER_STAGE_TASK_BIT_EXT | Gfx::SE_SHADER_STAGE_COMPUTE_BIT, + //}); depthAttachmentLayout->create(); depthCullingLayout = graphics->createPipelineLayout("DepthPrepassLayout"); @@ -73,9 +87,15 @@ void DepthCullingPass::render() { Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + //uint32 reset = 0; + //debugHead->updateContents(0, sizeof(uint32), &reset); + Gfx::PDescriptorSet set = depthAttachmentLayout->allocateDescriptorSet(); + //set->updateBuffer(0, debugUniform); set->updateTexture(0, Gfx::PTexture2D(depthAttachment.getTexture())); set->updateBuffer(1, depthMipBuffer); + //set->updateBuffer(3, debugHead); + //set->updateBuffer(4, debugData); set->writeChanges(); timestamps->begin(); @@ -221,7 +241,33 @@ void DepthCullingPass::publishOutputs() { width = std::max((width + 1) / 2, 1u); height = std::max((height + 1) / 2, 1u); } - ShaderBufferCreateInfo depthMipInfo = { + + //debugUniform = graphics->createUniformBuffer(UniformBufferCreateInfo{ + // .sourceData = + // { + // .size = sizeof(uint32), + // .data = (uint8*)&bufferSize, + // }, + //}); + //uint32 reset = 0; + //debugHead = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + // .sourceData = + // { + // .size = sizeof(uint32), + // .data = (uint8*)&reset, + // }, + // + //}); + //debugData = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + // .sourceData = + // { + // .size = sizeof(DepthDebugData) * 10000, + // .data = nullptr, + // }, + //}); + //graphics->waitDeviceIdle(); + + depthMipBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .sourceData = { .size = bufferSize * sizeof(uint32), @@ -229,17 +275,14 @@ void DepthCullingPass::publishOutputs() { }, .numElements = bufferSize, .name = "DepthMipBuffer", - }; - depthMipBuffer = graphics->createShaderBuffer(depthMipInfo); + }); - ShaderCompilationInfo mipComputeInfo = { + graphics->beginShaderCompilation(ShaderCompilationInfo{ .name = "DepthMipCompute", .modules = {"DepthMipGen"}, .entryPoints = {{"initialReduce", "DepthMipGen"}, {"reduceLevel", "DepthMipGen"}}, .rootSignature = depthComputeLayout, - }; - - graphics->beginShaderCompilation(mipComputeInfo); + }); depthInitialReduceShader = graphics->createComputeShader({0}); depthComputeLayout->create(); diff --git a/src/Engine/Graphics/RenderPass/DepthCullingPass.h b/src/Engine/Graphics/RenderPass/DepthCullingPass.h index b9407f7..518cf2f 100644 --- a/src/Engine/Graphics/RenderPass/DepthCullingPass.h +++ b/src/Engine/Graphics/RenderPass/DepthCullingPass.h @@ -25,6 +25,15 @@ class DepthCullingPass : public RenderPass { UVector2 srcMipDim; UVector2 dstMipDim; }; + + struct DepthDebugData { + uint32 mipLevel; + uint32 mipOffset; + UVector2 mipDimensions; + UVector2 screenCornerMin; + UVector2 screenCornerMax; + }; + Array mipOffsets; Array mipDims; Gfx::OShaderBuffer depthMipBuffer; @@ -41,6 +50,10 @@ class DepthCullingPass : public RenderPass { Gfx::OComputeShader depthMipGenShader; Gfx::PComputePipeline depthMipGen; + //Gfx::OUniformBuffer debugUniform; + //Gfx::OShaderBuffer debugHead; + //Gfx::OShaderBuffer debugData; + Gfx::PShaderBuffer cullingBuffer; }; DEFINE_REF(DepthCullingPass) diff --git a/src/Engine/Graphics/Vulkan/Graphics.cpp b/src/Engine/Graphics/Vulkan/Graphics.cpp index eb892aa..e70f982 100644 --- a/src/Engine/Graphics/Vulkan/Graphics.cpp +++ b/src/Engine/Graphics/Vulkan/Graphics.cpp @@ -774,6 +774,7 @@ void Graphics::pickPhysicalDevice() { .pNext = &features11, .features = { + //.robustBufferAccess = true, .geometryShader = true, .fillModeNonSolid = true, .wideLines = true, diff --git a/src/Engine/Material/Material.cpp b/src/Engine/Material/Material.cpp index 0fa49dd..2c6d39d 100644 --- a/src/Engine/Material/Material.cpp +++ b/src/Engine/Material/Material.cpp @@ -37,25 +37,25 @@ void Material::init(Gfx::PGraphics graphics) { layout = graphics->createDescriptorLayout("pMaterial"); layout->addDescriptorBinding(Gfx::DescriptorBinding{ .binding = 0, + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .bindingFlags = Gfx::SE_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT, + .shaderStages = Gfx::SE_SHADER_STAGE_FRAGMENT_BIT | Gfx::SE_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, + }); + layout->addDescriptorBinding(Gfx::DescriptorBinding{ + .binding = 1, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .descriptorCount = 512, .bindingFlags = Gfx::SE_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT, .shaderStages = Gfx::SE_SHADER_STAGE_FRAGMENT_BIT | Gfx::SE_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, }); layout->addDescriptorBinding(Gfx::DescriptorBinding{ - .binding = 1, + .binding = 2, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLER, .descriptorCount = 512, .bindingFlags = Gfx::SE_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT, .shaderStages = Gfx::SE_SHADER_STAGE_FRAGMENT_BIT | Gfx::SE_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, }); - layout->addDescriptorBinding(Gfx::DescriptorBinding{ - .binding = 2, - .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .descriptorCount = 1, - .bindingFlags = Gfx::SE_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT, - .shaderStages = Gfx::SE_SHADER_STAGE_FRAGMENT_BIT | Gfx::SE_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, - }); layout->create(); floatBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .dynamic = true, @@ -76,17 +76,17 @@ void Material::updateDescriptor() { Gfx::SE_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); layout->reset(); set = layout->allocateDescriptorSet(); + set->updateBuffer(0, floatBuffer); for (uint32 i = 0; i < textures.size(); ++i) { if (textures[i] != nullptr) { - set->updateTexture(0, i, textures[i]); + set->updateTexture(1, i, textures[i]); } } for (uint32 i = 0; i < samplers.size(); ++i) { if (samplers[i] != nullptr) { - set->updateSampler(1, i, samplers[i]); + set->updateSampler(2, i, samplers[i]); } } - set->updateBuffer(2, floatBuffer); set->writeChanges(); }