diff --git a/res/shaders/DepthCullingTask.slang b/res/shaders/DepthCullingTask.slang index b8953fe..e002949 100644 --- a/res/shaders/DepthCullingTask.slang +++ b/res/shaders/DepthCullingTask.slang @@ -6,8 +6,14 @@ groupshared uint head; groupshared MeshData mesh; groupshared InstanceData instance; groupshared Frustum viewFrustum; +groupshared float4x4 modelViewProjection; -ParameterBlock pDepthAttachment; +struct DepthData +{ + Texture2D texture; +} + +ParameterBlock pDepthAttachment; [numthreads(TASK_GROUP_SIZE, 1, 1)] [shader("amplification")] @@ -15,6 +21,7 @@ void taskMain( uint threadID: SV_GroupThreadID, uint groupID: SV_GroupID, ) { + const uint mipLevels = uint(log2(max(pViewParams.screenDimensions.x, pViewParams.screenDimensions.y))); if (threadID == 0) { head = 0; @@ -23,6 +30,7 @@ void taskMain( p.instanceId = pOffsets.instanceOffset + groupID; p.meshletOffset = mesh.meshletOffset; p.cullingOffset = pScene.cullingOffsets[p.instanceId]; + modelViewProjection = mul(mul(pViewParams.projectionMatrix, pViewParams.viewMatrix), instance.transformMatrix); float3 origin = viewToModel(instance.inverseTransformMatrix, float4(0, 0, 0, 1)).xyz; const float offset = 0.0f; float3 corners[4] = { @@ -46,13 +54,60 @@ void taskMain( // if any triangle was visible last frame, it was drawn by the cached pass already if(!culling.anyVisible()) { -#ifdef VIEW_CULLING + // if the meshlet is outside of the frustum, we skip it since we cant do depth culling anyways if(meshlet.bounding.insideFrustum(viewFrustum)) -#endif { - uint index; - InterlockedAdd(head, 1, index); - p.culledMeshlets[index] = i; + // now we calculate what mip level we need to only sample up to 4 texels covering the entire meshlet + uint2 screenCornerMin = uint2(uint(pViewParams.screenDimensions.x), uint(pViewParams.screenDimensions.y)); + uint2 screenCornerMax = uint2(0, 0); + // we use reverse depth, so higher values are closer + float maxDepth = 0; + { + float4 corners[8]; + corners[0] = float4(meshlet.bounding.min.x, meshlet.bounding.min.y, meshlet.bounding.min.z, 1.0f); + corners[1] = float4(meshlet.bounding.min.x, meshlet.bounding.min.y, meshlet.bounding.max.z, 1.0f); + corners[2] = float4(meshlet.bounding.min.x, meshlet.bounding.max.y, meshlet.bounding.min.z, 1.0f); + corners[3] = float4(meshlet.bounding.min.x, meshlet.bounding.max.y, meshlet.bounding.max.z, 1.0f); + corners[4] = float4(meshlet.bounding.max.x, meshlet.bounding.min.y, meshlet.bounding.min.z, 1.0f); + corners[5] = float4(meshlet.bounding.max.x, meshlet.bounding.min.y, meshlet.bounding.max.z, 1.0f); + corners[6] = float4(meshlet.bounding.max.x, meshlet.bounding.max.y, meshlet.bounding.min.z, 1.0f); + corners[7] = float4(meshlet.bounding.max.x, meshlet.bounding.max.y, meshlet.bounding.max.z, 1.0f); + for(uint i = 0; i < 8; ++i) + { + float4 clipCorner = mul(modelViewProjection, 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))); + maxDepth = max(maxDepth, screenCorner.z); + } + } + uint mipLevel = 0; + // 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++; + screenCornerMin /= 2; + screenCornerMax /= 2; + } + + // now we sample 4 texels from the depth at the calculated mip level, this should give us the + float d1 = pDepthAttachment.texture.Load(int3(screenCornerMin.x, screenCornerMin.y, mipLevel)).r; + float d2 = pDepthAttachment.texture.Load(int3(screenCornerMin.x, screenCornerMax.y, mipLevel)).r; + float d3 = pDepthAttachment.texture.Load(int3(screenCornerMax.x, screenCornerMin.y, mipLevel)).r; + float d4 = pDepthAttachment.texture.Load(int3(screenCornerMax.x, screenCornerMax.y, mipLevel)).r; + + // 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 + float d = min(min(d1, d2), min(d3, d4)); + + // this is technically not correct, as the mipmap is generated with a linear filter, but we actually would need a min filter, but whatever + if(d < maxDepth) + { + uint index; + InterlockedAdd(head, 1, index); + p.culledMeshlets[index] = i; + } + } } } diff --git a/res/shaders/lib/Common.slang b/res/shaders/lib/Common.slang index d928626..798b478 100644 --- a/res/shaders/lib/Common.slang +++ b/res/shaders/lib/Common.slang @@ -79,6 +79,13 @@ float4 screenToModel(float4x4 inverseTransform, float4 screen) return worldToModel(inverseTransform, world); } +float4 clipToScreen(float4 clip) +{ + float2 texCoord = float2(clip.x, 1.0f-clip.y) / 2.0f + 0.5f; + + return float4(texCoord * pViewParams.screenDimensions, clip.z, clip.w); +} + struct Plane { float3 n; diff --git a/src/Editor/Window/SceneView.cpp b/src/Editor/Window/SceneView.cpp index 9d3cc26..528da11 100644 --- a/src/Editor/Window/SceneView.cpp +++ b/src/Editor/Window/SceneView.cpp @@ -8,7 +8,9 @@ #include "Graphics/Mesh.h" #include "Scene/Scene.h" #include "Window/Window.h" - +#include "Graphics/RenderPass/BasePass.h" +#include "Graphics/RenderPass/DepthCullingPass.h" +#include "Graphics/RenderPass/LightCullingPass.h" using namespace Seele; using namespace Seele::Editor; @@ -16,7 +18,7 @@ using namespace Seele::Editor; SceneView::SceneView(Gfx::PGraphics graphics, PWindow owner, const ViewportCreateInfo& createInfo) : View(graphics, owner, createInfo, "SceneView"), scene(new Scene(graphics)), cameraSystem(createInfo.dimensions, Vector(0, 0, 10)) { cameraSystem.update(viewportCamera, static_cast(Gfx::getCurrentFrameDelta())); - renderGraph.addPass(new DepthPrepass(graphics, scene)); + renderGraph.addPass(new DepthCullingPass(graphics, scene)); renderGraph.addPass(new LightCullingPass(graphics, scene)); renderGraph.addPass(new BasePass(graphics, scene)); renderGraph.setViewport(viewport); diff --git a/src/Editor/Window/SceneView.h b/src/Editor/Window/SceneView.h index d43affe..7cb4a31 100644 --- a/src/Editor/Window/SceneView.h +++ b/src/Editor/Window/SceneView.h @@ -1,7 +1,4 @@ #pragma once -#include "Graphics/RenderPass/BasePass.h" -#include "Graphics/RenderPass/DepthPrepass.h" -#include "Graphics/RenderPass/LightCullingPass.h" #include "ThreadPool.h" #include "ViewportControl.h" #include "Window/View.h" diff --git a/src/Editor/main.cpp b/src/Editor/main.cpp index 210cb21..40c998b 100644 --- a/src/Editor/main.cpp +++ b/src/Editor/main.cpp @@ -15,6 +15,7 @@ #include "Window/PlayView.h" #include "Window/WindowManager.h" #include +#include using namespace Seele; using namespace Seele::Editor; diff --git a/src/Engine/Graphics/Graphics.h b/src/Engine/Graphics/Graphics.h index 1c4af25..acf0ff8 100644 --- a/src/Engine/Graphics/Graphics.h +++ b/src/Engine/Graphics/Graphics.h @@ -79,7 +79,8 @@ class Graphics { virtual OVertexInput createVertexInput(VertexInputStateCreateInfo createInfo) = 0; - virtual void resolveTexture(Gfx::PTexture source, Gfx::PTexture destination) = 0; + virtual void resolveTexture(PTexture source, PTexture destination) = 0; + virtual void copyTexture(PTexture src, PTexture dst) = 0; bool supportMeshShading() const { return meshShadingEnabled; } diff --git a/src/Engine/Graphics/RenderPass/BasePass.cpp b/src/Engine/Graphics/RenderPass/BasePass.cpp index 08db7bb..22f0fd5 100644 --- a/src/Engine/Graphics/RenderPass/BasePass.cpp +++ b/src/Engine/Graphics/RenderPass/BasePass.cpp @@ -14,7 +14,6 @@ #include "RenderGraph.h" #include "Window/Window.h" - using namespace Seele; extern bool useViewCulling; @@ -164,13 +163,10 @@ void BasePass::render() { Gfx::PGraphicsPipeline pipeline = graphics->createGraphicsPipeline(std::move(pipelineInfo)); command->bindPipeline(pipeline); } - command->bindDescriptor(viewParamsSet); - command->bindDescriptor(vertexData->getVertexDataSet()); - command->bindDescriptor(vertexData->getInstanceDataSet()); - command->bindDescriptor(scene->getLightEnvironment()->getDescriptorSet()); - command->bindDescriptor(opaqueCulling); for (const auto& drawCall : materialData.instances) { - command->bindDescriptor(drawCall.materialInstance->getDescriptorSet()); + command->bindDescriptor({viewParamsSet, vertexData->getVertexDataSet(), vertexData->getInstanceDataSet(), + scene->getLightEnvironment()->getDescriptorSet(), drawCall.materialInstance->getDescriptorSet(), + opaqueCulling}); command->pushConstants(Gfx::SE_SHADER_STAGE_TASK_BIT_EXT | Gfx::SE_SHADER_STAGE_VERTEX_BIT, 0, sizeof(VertexData::DrawCallOffsets), &drawCall.offsets); if (graphics->supportMeshShading()) { @@ -190,37 +186,32 @@ void BasePass::render() { graphics->executeCommands(std::move(commands)); graphics->endRenderPass(); - // Sync color write with next pass/swapchain present - // colorAttachment.getTexture()->pipelineBarrier( - // Gfx::SE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - // Gfx::SE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - // Gfx::SE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - // Gfx::SE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT - //); - // Sync depth with next pass/next frame - // depthAttachment.getTexture()->pipelineBarrier( - // Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - // Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - // Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, - // Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT - //); } void BasePass::endFrame() {} void BasePass::publishOutputs() { + TextureCreateInfo depthBufferInfo = { + .format = Gfx::SE_FORMAT_D32_SFLOAT, + .width = viewport->getOwner()->getFramebufferWidth(), + .height = viewport->getOwner()->getFramebufferHeight(), + .samples = viewport->getSamples(), + .usage = Gfx::SE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + }; + basePassDepth = graphics->createTexture2D(depthBufferInfo); + colorAttachment = Gfx::RenderTargetAttachment(viewport, Gfx::SE_IMAGE_LAYOUT_UNDEFINED, Gfx::SE_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, Gfx::SE_ATTACHMENT_LOAD_OP_CLEAR, Gfx::SE_ATTACHMENT_STORE_OP_STORE); resources->registerRenderPassOutput("BASEPASS_COLOR", colorAttachment); + depthAttachment = + Gfx::RenderTargetAttachment(basePassDepth, Gfx::SE_IMAGE_LAYOUT_UNDEFINED, Gfx::SE_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + Gfx::SE_ATTACHMENT_LOAD_OP_CLEAR, Gfx::SE_ATTACHMENT_STORE_OP_STORE); + resources->registerRenderPassOutput("BASEPASS_DEPTH", depthAttachment); } void BasePass::createRenderPass() { cullingBuffer = resources->requestBuffer("CULLINGBUFFER"); - depthAttachment = resources->requestRenderTarget("DEPTHPREPASS_DEPTH"); - depthAttachment.setLoadOp(Gfx::SE_ATTACHMENT_LOAD_OP_LOAD); - depthAttachment.setInitialLayout(Gfx::SE_IMAGE_LAYOUT_GENERAL); - depthAttachment.setFinalLayout(Gfx::SE_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); Gfx::RenderTargetLayout layout = Gfx::RenderTargetLayout{ .colorAttachments = {colorAttachment}, .depthAttachment = depthAttachment, diff --git a/src/Engine/Graphics/RenderPass/BasePass.h b/src/Engine/Graphics/RenderPass/BasePass.h index c186ab0..40d8a7a 100644 --- a/src/Engine/Graphics/RenderPass/BasePass.h +++ b/src/Engine/Graphics/RenderPass/BasePass.h @@ -28,6 +28,9 @@ class BasePass : public RenderPass { Gfx::PDescriptorSet opaqueCulling; Gfx::PDescriptorSet transparentCulling; + // use a different texture here so we can do multisampling + Gfx::OTexture2D basePassDepth; + PCameraActor source; Gfx::OPipelineLayout basePassLayout; Gfx::ODescriptorLayout lightCullingLayout; diff --git a/src/Engine/Graphics/RenderPass/CMakeLists.txt b/src/Engine/Graphics/RenderPass/CMakeLists.txt index a7a97e7..f5ff9d8 100644 --- a/src/Engine/Graphics/RenderPass/CMakeLists.txt +++ b/src/Engine/Graphics/RenderPass/CMakeLists.txt @@ -6,8 +6,8 @@ target_sources(Engine CachedDepthPass.cpp DebugPass.h DebugPass.cpp - DepthPrepass.h - DepthPrepass.cpp + DepthCullingPass.h + DepthCullingPass.cpp LightCullingPass.h LightCullingPass.cpp RayTracingPass.h @@ -32,7 +32,7 @@ target_sources(Engine BasePass.h CachedDepthPass.h DebugPass.h - DepthPrepass.h + DepthCullingPass.h LightCullingPass.h RayTracingPass.h RenderGraph.h diff --git a/src/Engine/Graphics/RenderPass/CachedDepthPass.cpp b/src/Engine/Graphics/RenderPass/CachedDepthPass.cpp index ffc907f..e1af606 100644 --- a/src/Engine/Graphics/RenderPass/CachedDepthPass.cpp +++ b/src/Engine/Graphics/RenderPass/CachedDepthPass.cpp @@ -113,9 +113,7 @@ void CachedDepthPass::render() { Gfx::PGraphicsPipeline pipeline = graphics->createGraphicsPipeline(std::move(pipelineInfo)); command->bindPipeline(pipeline); } - command->bindDescriptor(viewParamsSet); - command->bindDescriptor(vertexData->getVertexDataSet()); - command->bindDescriptor(vertexData->getInstanceDataSet()); + command->bindDescriptor({viewParamsSet, vertexData->getVertexDataSet(), vertexData->getInstanceDataSet()}); uint32 offset = 0; command->pushConstants(Gfx::SE_SHADER_STAGE_TASK_BIT_EXT | Gfx::SE_SHADER_STAGE_VERTEX_BIT, 0, sizeof(VertexData::DrawCallOffsets), &offset); @@ -143,18 +141,6 @@ void CachedDepthPass::render() { graphics->executeCommands(std::move(commands)); graphics->endRenderPass(); - // Sync depth read/write with depth pass depth read - // depthBuffer->pipelineBarrier( - // Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - // Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - // Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, - // Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT); - // sync visibility write with depth pass visibility write - // visibilityBuffer->pipelineBarrier( - // Gfx::SE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - // Gfx::SE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - // Gfx::SE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - // Gfx::SE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); } void CachedDepthPass::endFrame() {} diff --git a/src/Engine/Graphics/RenderPass/DebugPass.cpp b/src/Engine/Graphics/RenderPass/DebugPass.cpp index 8b200e4..a4cd74d 100644 --- a/src/Engine/Graphics/RenderPass/DebugPass.cpp +++ b/src/Engine/Graphics/RenderPass/DebugPass.cpp @@ -74,7 +74,7 @@ void DebugPass::createRenderPass() { colorAttachment.setInitialLayout(Gfx::SE_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); colorAttachment.setInitialLayout(Gfx::SE_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - depthAttachment = resources->requestRenderTarget("DEPTHPREPASS_DEPTH"); + depthAttachment = resources->requestRenderTarget("BASEPASS_DEPTH"); depthAttachment.setLoadOp(Gfx::SE_ATTACHMENT_LOAD_OP_LOAD); depthAttachment.setInitialLayout(Gfx::SE_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); depthAttachment.setFinalLayout(Gfx::SE_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); diff --git a/src/Engine/Graphics/RenderPass/DepthPrepass.cpp b/src/Engine/Graphics/RenderPass/DepthCullingPass.cpp similarity index 77% rename from src/Engine/Graphics/RenderPass/DepthPrepass.cpp rename to src/Engine/Graphics/RenderPass/DepthCullingPass.cpp index 5bc851f..1cfb3b1 100644 --- a/src/Engine/Graphics/RenderPass/DepthPrepass.cpp +++ b/src/Engine/Graphics/RenderPass/DepthCullingPass.cpp @@ -1,4 +1,4 @@ -#include "DepthPrepass.h" +#include "DepthCullingPass.h" #include "Graphics/Shader.h" using namespace Seele; @@ -6,9 +6,18 @@ using namespace Seele; extern bool usePositionOnly; extern bool useViewCulling; -DepthPrepass::DepthPrepass(Gfx::PGraphics graphics, PScene scene) : RenderPass(graphics, scene) { +DepthCullingPass::DepthCullingPass(Gfx::PGraphics graphics, PScene scene) : RenderPass(graphics, scene) { + depthTextureLayout = graphics->createDescriptorLayout("pDepthAttachment"); + depthTextureLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .binding = 0, + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .shaderStages = Gfx::SE_SHADER_STAGE_TASK_BIT_EXT | Gfx::SE_SHADER_STAGE_MESH_BIT_EXT, + }); + depthTextureLayout->create(); + depthPrepassLayout = graphics->createPipelineLayout("DepthPrepassLayout"); depthPrepassLayout->addDescriptorLayout(viewParamsLayout); + depthPrepassLayout->addDescriptorLayout(depthTextureLayout); depthPrepassLayout->addPushConstants(Gfx::SePushConstantRange{ .stageFlags = Gfx::SE_SHADER_STAGE_TASK_BIT_EXT | Gfx::SE_SHADER_STAGE_VERTEX_BIT, .offset = 0, @@ -41,11 +50,32 @@ DepthPrepass::DepthPrepass(Gfx::PGraphics graphics, PScene scene) : RenderPass(g } } -DepthPrepass::~DepthPrepass() {} +DepthCullingPass::~DepthCullingPass() {} -void DepthPrepass::beginFrame(const Component::Camera& cam) { RenderPass::beginFrame(cam); } +void DepthCullingPass::beginFrame(const Component::Camera& cam) { RenderPass::beginFrame(cam); } + +void DepthCullingPass::render() { + depthAttachment.getTexture()->changeLayout(Gfx::SE_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, Gfx::SE_ACCESS_TRANSFER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT); + depthMipTexture->changeLayout(Gfx::SE_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT); + + graphics->copyTexture(depthAttachment.getTexture(), Gfx::PTexture2D(depthMipTexture)); + depthMipTexture->generateMipmaps(); + + depthAttachment.getTexture()->changeLayout( + Gfx::SE_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, Gfx::SE_ACCESS_TRANSFER_READ_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT); + depthMipTexture->changeLayout(Gfx::SE_IMAGE_LAYOUT_GENERAL, Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, + Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_TASK_SHADER_BIT_EXT); + + Gfx::PDescriptorSet set = depthTextureLayout->allocateDescriptorSet(); + set->updateTexture(0, Gfx::PTexture2D(depthMipTexture)); + set->writeChanges(); -void DepthPrepass::render() { graphics->beginRenderPass(renderPass); Array commands; @@ -112,9 +142,7 @@ void DepthPrepass::render() { Gfx::PGraphicsPipeline pipeline = graphics->createGraphicsPipeline(std::move(pipelineInfo)); command->bindPipeline(pipeline); } - command->bindDescriptor(viewParamsSet); - command->bindDescriptor(vertexData->getVertexDataSet()); - command->bindDescriptor(vertexData->getInstanceDataSet()); + command->bindDescriptor({viewParamsSet, vertexData->getVertexDataSet(), vertexData->getInstanceDataSet(), set}); uint32 offset = 0; command->pushConstants(Gfx::SE_SHADER_STAGE_TASK_BIT_EXT | Gfx::SE_SHADER_STAGE_VERTEX_BIT, 0, sizeof(VertexData::DrawCallOffsets), &offset); @@ -146,24 +174,24 @@ void DepthPrepass::render() { depthAttachment.getTexture()->pipelineBarrier( Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); - // Sync depth read/write with base pass read - // depthAttachment.getTexture()->pipelineBarrier( - // Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - // Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - // Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, - // Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT - //); // Sync visibility write with compute read visibilityAttachment.getTexture()->pipelineBarrier(Gfx::SE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); } -void DepthPrepass::endFrame() {} +void DepthCullingPass::endFrame() {} -void DepthPrepass::publishOutputs() {} +void DepthCullingPass::publishOutputs() { + uint32 width = viewport->getOwner()->getFramebufferWidth(); + uint32 height = viewport->getOwner()->getFramebufferHeight(); + uint32 mipLevels = static_cast(std::floor(std::log2(std::max(width, height)))) + 1; + TextureCreateInfo depthMipInfo = { + .format = Gfx::SE_FORMAT_D32_SFLOAT, .width = width, .height = height, .mipLevels = mipLevels, .name = "DepthMipTexture"}; + depthMipTexture = graphics->createTexture2D(depthMipInfo); +} -void DepthPrepass::createRenderPass() { +void DepthCullingPass::createRenderPass() { cullingBuffer = resources->requestBuffer("CULLINGBUFFER"); depthAttachment = resources->requestRenderTarget("DEPTHPREPASS_DEPTH"); diff --git a/src/Engine/Graphics/RenderPass/DepthPrepass.h b/src/Engine/Graphics/RenderPass/DepthCullingPass.h similarity index 59% rename from src/Engine/Graphics/RenderPass/DepthPrepass.h rename to src/Engine/Graphics/RenderPass/DepthCullingPass.h index 667c073..9e5d517 100644 --- a/src/Engine/Graphics/RenderPass/DepthPrepass.h +++ b/src/Engine/Graphics/RenderPass/DepthCullingPass.h @@ -3,12 +3,12 @@ #include "RenderPass.h" namespace Seele { -class DepthPrepass : public RenderPass { +class DepthCullingPass : public RenderPass { public: - DepthPrepass(Gfx::PGraphics graphics, PScene scene); - DepthPrepass(DepthPrepass&&) = default; - DepthPrepass& operator=(DepthPrepass&&) = default; - virtual ~DepthPrepass(); + DepthCullingPass(Gfx::PGraphics graphics, PScene scene); + DepthCullingPass(DepthCullingPass&&) = default; + DepthCullingPass& operator=(DepthCullingPass&&) = default; + virtual ~DepthCullingPass(); virtual void beginFrame(const Component::Camera& cam) override; virtual void render() override; virtual void endFrame() override; @@ -16,11 +16,13 @@ class DepthPrepass : public RenderPass { virtual void createRenderPass() override; private: + Gfx::OTexture2D depthMipTexture; Gfx::RenderTargetAttachment depthAttachment; Gfx::RenderTargetAttachment visibilityAttachment; + Gfx::ODescriptorLayout depthTextureLayout; Gfx::OPipelineLayout depthPrepassLayout; Gfx::PShaderBuffer cullingBuffer; }; -DEFINE_REF(DepthPrepass) +DEFINE_REF(DepthCullingPass) } // namespace Seele diff --git a/src/Engine/Graphics/Resources.cpp b/src/Engine/Graphics/Resources.cpp index b8c8213..3b57f9f 100644 --- a/src/Engine/Graphics/Resources.cpp +++ b/src/Engine/Graphics/Resources.cpp @@ -1,8 +1,6 @@ #include "Resources.h" #include "Graphics.h" #include "Material/Material.h" -#include "RenderPass/BasePass.h" -#include "RenderPass/DepthPrepass.h" #include "Resources.h" diff --git a/src/Engine/Graphics/Shader.cpp b/src/Engine/Graphics/Shader.cpp index 3895887..7a50a95 100644 --- a/src/Engine/Graphics/Shader.cpp +++ b/src/Engine/Graphics/Shader.cpp @@ -1,8 +1,8 @@ #include "Shader.h" #include "Graphics/Initializer.h" -#include "Graphics/RenderPass/BasePass.h" -#include "Graphics/RenderPass/DepthPrepass.h" +#include "Material/Material.h" #include "ThreadPool.h" +#include "Graphics/Graphics.h" #include diff --git a/src/Engine/Graphics/Texture.h b/src/Engine/Graphics/Texture.h index 73ccbca..6d22c0a 100644 --- a/src/Engine/Graphics/Texture.h +++ b/src/Engine/Graphics/Texture.h @@ -18,7 +18,7 @@ class Texture : public QueueOwnedResource { virtual void changeLayout(SeImageLayout newLayout, SeAccessFlags srcAccess, SePipelineStageFlags srcStage, SeAccessFlags dstAccess, SePipelineStageFlags dstStage) = 0; virtual void download(uint32 mipLevel, uint32 arrayLayer, uint32 face, Array& buffer) = 0; - + virtual void generateMipmaps() = 0; protected: // Inherited via QueueOwnedResource virtual void executeOwnershipBarrier(QueueType newOwner) = 0; @@ -40,6 +40,7 @@ class Texture2D : public Texture { virtual uint32 getMipLevels() const = 0; virtual void changeLayout(SeImageLayout newLayout, SeAccessFlags srcAccess, SePipelineStageFlags srcStage, SeAccessFlags dstAccess, SePipelineStageFlags dstStage) = 0; + virtual void generateMipmaps() = 0; protected: // Inherited via QueueOwnedResource @@ -62,6 +63,7 @@ class Texture3D : public Texture { virtual uint32 getMipLevels() const = 0; virtual void changeLayout(SeImageLayout newLayout, SeAccessFlags srcAccess, SePipelineStageFlags srcStage, SeAccessFlags dstAccess, SePipelineStageFlags dstStage) = 0; + virtual void generateMipmaps() = 0; protected: // Inherited via QueueOwnedResource @@ -85,6 +87,7 @@ class TextureCube : public Texture { virtual uint32 getMipLevels() const = 0; virtual void changeLayout(SeImageLayout newLayout, SeAccessFlags srcAccess, SePipelineStageFlags srcStage, SeAccessFlags dstAccess, SePipelineStageFlags dstStage) = 0; + virtual void generateMipmaps() = 0; protected: // Inherited via QueueOwnedResource diff --git a/src/Engine/Graphics/Vulkan/Command.cpp b/src/Engine/Graphics/Vulkan/Command.cpp index 386ade4..3a14d0c 100644 --- a/src/Engine/Graphics/Vulkan/Command.cpp +++ b/src/Engine/Graphics/Vulkan/Command.cpp @@ -248,6 +248,7 @@ void RenderCommand::bindDescriptor(Gfx::PDescriptorSet descriptorSet, Array& descriptorSets, Array dynamicOffsets) { assert(threadId == std::this_thread::get_id()); VkDescriptorSet* sets = new VkDescriptorSet[descriptorSets.size()]; + std::memset(sets, 0, sizeof(VkDescriptorSet) * descriptorSets.size()); for (uint32 i = 0; i < descriptorSets.size(); ++i) { auto descriptorSet = descriptorSets[i].cast(); assert(descriptorSet->writeDescriptors.size() == 0); diff --git a/src/Engine/Graphics/Vulkan/Graphics.cpp b/src/Engine/Graphics/Vulkan/Graphics.cpp index b1d0fd5..8b90021 100644 --- a/src/Engine/Graphics/Vulkan/Graphics.cpp +++ b/src/Engine/Graphics/Vulkan/Graphics.cpp @@ -236,6 +236,26 @@ void Graphics::resolveTexture(Gfx::PTexture source, Gfx::PTexture destination) { destinationTex->getImage(), cast(destinationTex->getLayout()), 1, &resolve); } +void Graphics::copyTexture(Gfx::PTexture source, Gfx::PTexture destination) { + PTextureBase src = source.cast(); + PTextureBase dst = destination.cast(); + VkImageBlit blit = {.srcSubresource = {.aspectMask = src->getAspect(), .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1}, + .srcOffsets = + { + {0, 0, 0}, + {(int32)src->getWidth(), (int32)src->getHeight(), (int32)src->getDepth()}, + }, + .dstSubresource = {.aspectMask = dst->aspect, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1}, + .dstOffsets = { + {0, 0, 0}, + {(int32)dst->getWidth(), (int32)dst->getHeight(), (int32)dst->getDepth()}, + }}; + PCommand command = getGraphicsCommands()->getCommands(); + vkCmdBlitImage(command->getHandle(), src->getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst->getImage(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, + src->aspect & VK_IMAGE_ASPECT_DEPTH_BIT ? VK_FILTER_NEAREST : VK_FILTER_LINEAR); +} + Gfx::OBottomLevelAS Graphics::createBottomLevelAccelerationStructure(const Gfx::BottomLevelASCreateInfo& createInfo) { return new BottomLevelAS(this, createInfo); } diff --git a/src/Engine/Graphics/Vulkan/Graphics.h b/src/Engine/Graphics/Vulkan/Graphics.h index 361e3c0..997905a 100644 --- a/src/Engine/Graphics/Vulkan/Graphics.h +++ b/src/Engine/Graphics/Vulkan/Graphics.h @@ -66,6 +66,7 @@ class Graphics : public Gfx::Graphics { virtual Gfx::OVertexInput createVertexInput(VertexInputStateCreateInfo createInfo) override; virtual void resolveTexture(Gfx::PTexture source, Gfx::PTexture destination) override; + virtual void copyTexture(Gfx::PTexture src, Gfx::PTexture dst) override; // Ray Tracing virtual Gfx::OBottomLevelAS createBottomLevelAccelerationStructure(const Gfx::BottomLevelASCreateInfo& createInfo) override; diff --git a/src/Engine/Graphics/Vulkan/Texture.cpp b/src/Engine/Graphics/Vulkan/Texture.cpp index 42ca79c..be9bb77 100644 --- a/src/Engine/Graphics/Vulkan/Texture.cpp +++ b/src/Engine/Graphics/Vulkan/Texture.cpp @@ -146,6 +146,7 @@ TextureBase::TextureBase(PGraphics graphics, VkImageViewType viewType, const Tex VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); commandPool->getCommands()->bindResource(PBufferAllocation(stagingAlloc)); + generateMipmaps(); // When loading a texture from a file, we will almost always use it as a texture map for fragment shaders changeLayout(Gfx::SE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); @@ -270,6 +271,69 @@ void TextureBase::download(uint32 mipLevel, uint32 arrayLayer, uint32 face, Arra graphics->getDestructionManager()->queueResourceForDestruction(std::move(stagingAlloc)); } +void TextureBase::generateMipmaps() { + PCommandPool commandPool = graphics->getQueueCommands(currentOwner); + VkImageMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = handle->image, + .subresourceRange = + { + .aspectMask = aspect, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + int32 mipWidth = width; + int32 mipHeight = height; + for (uint32 i = 1; i < mipLevels; ++i) { + barrier.subresourceRange.baseMipLevel = i - 1; + + vkCmdPipelineBarrier(commandPool->getCommands()->getHandle(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, + nullptr, 0, nullptr, 1, &barrier); + + VkImageBlit blit = { + .srcSubresource = + { + .aspectMask = aspect, + .mipLevel = i - 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcOffsets = + { + {0, 0, 0}, + {mipWidth, mipHeight, 1}, + }, + .dstSubresource = {.aspectMask = aspect, .mipLevel = i, .baseArrayLayer = 0, .layerCount = 1}, + .dstOffsets = + { + {0, 0, 0}, + {mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1}, + }, + }; + vkCmdBlitImage(commandPool->getCommands()->getHandle(), handle->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, handle->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, aspect & VK_IMAGE_ASPECT_DEPTH_BIT ? VK_FILTER_NEAREST : VK_FILTER_LINEAR); + + if (mipWidth > 1) + mipWidth /= 2; + if (mipHeight) + mipHeight /= 2; + } + barrier.subresourceRange.baseMipLevel = mipLevels - 1; + + // just so that all levels are in the same layout and we can transition them as one + vkCmdPipelineBarrier(commandPool->getCommands()->getHandle(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, + nullptr, 0, nullptr, 1, &barrier); + layout = Gfx::SE_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; +} + void TextureBase::executeOwnershipBarrier(Gfx::QueueType newOwner) { Gfx::QueueFamilyMapping mapping = graphics->getFamilyMapping(); VkImageMemoryBarrier imageBarrier = { @@ -343,7 +407,7 @@ void TextureBase::executePipelineBarrier(VkAccessFlags srcAccess, VkPipelineStag { .aspectMask = aspect, .baseMipLevel = 0, - .levelCount = 1, + .levelCount = mipLevels, .baseArrayLayer = 0, .layerCount = 1, }, @@ -369,6 +433,8 @@ void Texture2D::download(uint32 mipLevel, uint32 arrayLayer, uint32 face, Array< TextureBase::download(mipLevel, arrayLayer, face, buffer); } +void Texture2D::generateMipmaps() { TextureBase::generateMipmaps(); } + void Texture2D::executeOwnershipBarrier(Gfx::QueueType newOwner) { TextureBase::executeOwnershipBarrier(newOwner); } void Texture2D::executePipelineBarrier(VkAccessFlags srcAccess, VkPipelineStageFlags srcStage, VkAccessFlags dstAccess, @@ -390,6 +456,9 @@ void Texture3D::changeLayout(Gfx::SeImageLayout newLayout, Gfx::SeAccessFlags sr void Texture3D::download(uint32 mipLevel, uint32 arrayLayer, uint32 face, Array& buffer) { TextureBase::download(mipLevel, arrayLayer, face, buffer); } + +void Texture3D::generateMipmaps() { TextureBase::generateMipmaps(); } + void Texture3D::executeOwnershipBarrier(Gfx::QueueType newOwner) { TextureBase::executeOwnershipBarrier(newOwner); } void Texture3D::executePipelineBarrier(VkAccessFlags srcAccess, VkPipelineStageFlags srcStage, VkAccessFlags dstAccess, @@ -412,6 +481,9 @@ void TextureCube::changeLayout(Gfx::SeImageLayout newLayout, Gfx::SeAccessFlags void TextureCube::download(uint32 mipLevel, uint32 arrayLayer, uint32 face, Array& buffer) { TextureBase::download(mipLevel, arrayLayer, face, buffer); } + +void TextureCube::generateMipmaps() { TextureBase::generateMipmaps(); } + void TextureCube::executeOwnershipBarrier(Gfx::QueueType newOwner) { TextureBase::executeOwnershipBarrier(newOwner); } void TextureCube::executePipelineBarrier(VkAccessFlags srcAccess, VkPipelineStageFlags srcStage, VkAccessFlags dstAccess, diff --git a/src/Engine/Graphics/Vulkan/Texture.h b/src/Engine/Graphics/Vulkan/Texture.h index 22eef14..b1afda9 100644 --- a/src/Engine/Graphics/Vulkan/Texture.h +++ b/src/Engine/Graphics/Vulkan/Texture.h @@ -41,6 +41,7 @@ class TextureBase { void changeLayout(Gfx::SeImageLayout newLayout, VkAccessFlags srcAccess, VkPipelineStageFlags srcStage, VkAccessFlags dstAccess, VkPipelineStageFlags dstStage); void download(uint32 mipLevel, uint32 arrayLayer, uint32 face, Array& buffer); + void generateMipmaps(); protected: OTextureHandle handle; @@ -75,6 +76,7 @@ class Texture2D : public Gfx::Texture2D, public TextureBase { virtual void changeLayout(Gfx::SeImageLayout newLayout, Gfx::SeAccessFlags srcAccess, Gfx::SePipelineStageFlags srcStage, Gfx::SeAccessFlags dstAccess, Gfx::SePipelineStageFlags dstStage) override; virtual void download(uint32 mipLevel, uint32 arrayLayer, uint32 face, Array& buffer) override; + virtual void generateMipmaps() override; protected: // Inherited via QueueOwnedResource @@ -97,6 +99,7 @@ class Texture3D : public Gfx::Texture3D, public TextureBase { virtual void changeLayout(Gfx::SeImageLayout newLayout, Gfx::SeAccessFlags srcAccess, Gfx::SePipelineStageFlags srcStage, Gfx::SeAccessFlags dstAccess, Gfx::SePipelineStageFlags dstStage) override; virtual void download(uint32 mipLevel, uint32 arrayLayer, uint32 face, Array& buffer) override; + virtual void generateMipmaps() override; protected: // Inherited via QueueOwnedResource @@ -119,6 +122,7 @@ class TextureCube : public Gfx::TextureCube, public TextureBase { virtual void changeLayout(Gfx::SeImageLayout newLayout, Gfx::SeAccessFlags srcAccess, Gfx::SePipelineStageFlags srcStage, Gfx::SeAccessFlags dstAccess, Gfx::SePipelineStageFlags dstStage) override; virtual void download(uint32 mipLevel, uint32 arrayLayer, uint32 face, Array& buffer) override; + virtual void generateMipmaps() override; protected: // Inherited via QueueOwnedResource diff --git a/src/Engine/Window/GameView.cpp b/src/Engine/Window/GameView.cpp index 67d358e..a4c4fc8 100644 --- a/src/Engine/Window/GameView.cpp +++ b/src/Engine/Window/GameView.cpp @@ -3,13 +3,13 @@ #include "Asset/AssetRegistry.h" #include "Component/KeyboardInput.h" #include "Graphics/Graphics.h" -#include "Graphics/RenderPass/BasePass.h" #include "Graphics/RenderPass/CachedDepthPass.h" -#include "Graphics/RenderPass/DebugPass.h" -#include "Graphics/RenderPass/DepthPrepass.h" -#include "Graphics/RenderPass/LightCullingPass.h" -#include "Graphics/RenderPass/SkyboxRenderPass.h" +#include "Graphics/RenderPass/DepthCullingPass.h" #include "Graphics/RenderPass/VisibilityPass.h" +#include "Graphics/RenderPass/LightCullingPass.h" +#include "Graphics/RenderPass/BasePass.h" +#include "Graphics/RenderPass/SkyboxRenderPass.h" +#include "Graphics/RenderPass/DebugPass.h" #include "System/CameraUpdater.h" #include "System/LightGather.h" #include "System/MeshUpdater.h" @@ -26,7 +26,7 @@ GameView::GameView(Gfx::PGraphics graphics, PWindow window, const ViewportCreate : View(graphics, window, createInfo, "Game"), scene(new Scene(graphics)), gameInterface(dllPath) { reloadGame(); renderGraph.addPass(new CachedDepthPass(graphics, scene)); - renderGraph.addPass(new DepthPrepass(graphics, scene)); + renderGraph.addPass(new DepthCullingPass(graphics, scene)); renderGraph.addPass(new VisibilityPass(graphics, scene)); renderGraph.addPass(new LightCullingPass(graphics, scene)); renderGraph.addPass(new BasePass(graphics, scene));