diff --git a/include/LLGL/CommandBuffer.h b/include/LLGL/CommandBuffer.h index ff1b6586ac..1b7b2af462 100644 --- a/include/LLGL/CommandBuffer.h +++ b/include/LLGL/CommandBuffer.h @@ -491,6 +491,7 @@ class LLGL_EXPORT CommandBuffer : public RenderSystemChild \param[in] renderPass Specifies an optional render pass object. If this is null, the default render pass for the specified render target will be used. This render pass object must be compatible with the render pass object the specified render target was created with. + Note that the default render pass will ignore the previous framebuffer content (see AttachmentLoadOp::Undefined), i.e. such a render pass section should fill the entire framebuffer. \param[in] numClearValues Specifies the number of clear values that are specified in the \c clearValues parameter. This \em should be greater than or equal to the number of render pass attachments whose load operation (i.e. AttachmentFormatDescriptor::loadOp) is set to AttachmentLoadOp::Clear. @@ -752,7 +753,7 @@ class LLGL_EXPORT CommandBuffer : public RenderSystemChild \remarks This must only be called if a graphics pipeline is currently bound. \see EndStreamOutput - \see SetGraphicsPipeline + \see SetPipelineState \see RenderingFeatures::hasStreamOutputs \see RenderingLimits::maxStreamOutputs */ diff --git a/tests/Testbed/.gitignore b/tests/Testbed/.gitignore new file mode 100644 index 0000000000..2fe317a809 --- /dev/null +++ b/tests/Testbed/.gitignore @@ -0,0 +1 @@ +Output/ diff --git a/tests/Testbed/TestDepthBuffer.cpp b/tests/Testbed/TestDepthBuffer.cpp index 5d1f8fae33..7997c4d5e2 100644 --- a/tests/Testbed/TestDepthBuffer.cpp +++ b/tests/Testbed/TestDepthBuffer.cpp @@ -6,11 +6,140 @@ */ #include "Testbed.h" +#include +#include +#include +#include DEF_TEST( DepthBuffer ) { - //todo + const Extent2D resolution{ swapChain->GetResolution() }; + + if (shaders[VSSolid] == nullptr || shaders[PSSolid] == nullptr) + { + Log::Errorf("Missing shaders for backend\n"); + return TestResult::FailedErrors; + } + + // Create texture for readback with depth-only format (D32Float) + TextureDescriptor texDesc; + { + texDesc.format = Format::D32Float; + texDesc.extent.width = resolution.width; + texDesc.extent.height = resolution.height; + texDesc.bindFlags = BindFlags::DepthStencilAttachment | BindFlags::CopySrc | BindFlags::Sampled; + texDesc.mipLevels = 1; + } + Texture* readbackTex = renderer->CreateTexture(texDesc); + readbackTex->SetName("readbackTex"); + + // Create depth-only render target for scene + RenderTargetDescriptor renderTargetDesc; + { + renderTargetDesc.resolution = resolution; + renderTargetDesc.depthStencilAttachment = readbackTex; + } + RenderTarget* renderTarget = renderer->CreateRenderTarget(renderTargetDesc); + renderTarget->SetName("renderTarget"); + + // Create PSO for rendering + PipelineLayout* psoLayout = renderer->CreatePipelineLayout(Parse("cbuffer(Scene@0):vert:frag")); + + GraphicsPipelineDescriptor psoDesc; + { + psoDesc.pipelineLayout = psoLayout; + psoDesc.renderPass = renderTarget->GetRenderPass(); + psoDesc.vertexShader = shaders[VSSolid]; + psoDesc.depth.testEnabled = true; + psoDesc.depth.writeEnabled = true; + psoDesc.rasterizer.cullMode = CullMode::Back; + } + PipelineState* pso = renderer->CreatePipelineState(psoDesc); + + // Update scene constants + sceneConstants.wMatrix.LoadIdentity(); + Gs::Translate(sceneConstants.wMatrix, Gs::Vector3f{ 0, 0, 2 }); + Gs::RotateFree(sceneConstants.wMatrix, Gs::Vector3f{ 0, 1, 0 }, Gs::Deg2Rad(20.0f)); + + Gs::Matrix4f vMatrix; + vMatrix.LoadIdentity(); + Gs::Translate(vMatrix, Gs::Vector3f{ 0, 0, -3 }); + vMatrix.MakeInverse(); + + Gs::Matrix4f vpMatrix = projection * vMatrix; + sceneConstants.wvpMatrix = vpMatrix * sceneConstants.wMatrix; + + // Render scene + cmdBuffer->Begin(); + { + cmdBuffer->UpdateBuffer(*sceneCbuffer, 0, &sceneConstants, sizeof(sceneConstants)); + cmdBuffer->BeginRenderPass(*renderTarget); + { + // Draw scene + cmdBuffer->Clear(ClearFlags::ColorDepth); + cmdBuffer->SetPipelineState(*pso); + cmdBuffer->SetViewport(resolution); + cmdBuffer->SetVertexBuffer(*meshBuffer); + cmdBuffer->SetIndexBuffer(*meshBuffer, Format::R32UInt, models[ModelCube].indexBufferOffset); + cmdBuffer->SetResource(0, *sceneCbuffer); + cmdBuffer->DrawIndexed(models[ModelCube].numIndices, 0); + } + cmdBuffer->EndRenderPass(); + } + cmdBuffer->End(); + + // Readback depth buffer and compare with expected result + const Offset3D readbackTexPosition + { + static_cast(resolution.width/2), + static_cast(resolution.height/2), + 0, + }; + const TextureRegion readbackTexRegion{ readbackTexPosition, Extent3D{ 1, 1, 1 } }; + + float readbackDepthValue = -1.0f; + + DstImageDescriptor dstImageDesc; + { + dstImageDesc.format = ImageFormat::Depth; + dstImageDesc.data = &readbackDepthValue; + dstImageDesc.dataSize = sizeof(readbackDepthValue); + dstImageDesc.dataType = DataType::Float32; + } + renderer->ReadTexture(*readbackTex, readbackTexRegion, dstImageDesc); + + const float expectedDepthValue = 0.975574434f; + const float deltaDepthValue = std::abs(readbackDepthValue - expectedDepthValue); + + if (deltaDepthValue > epsilon) + { + Log::Errorf( + "Mismatch between depth buffer value at center (%f) and expected value (%f): delta = %f\n", + readbackDepthValue, expectedDepthValue, deltaDepthValue + ); + //return TestResult::FailedMismatch; + } + + // Match entire depth and create delta heat map + std::vector readbackDepthBuffer; + readbackDepthBuffer.resize(texDesc.extent.width * texDesc.extent.height, -1.0f); + { + dstImageDesc.format = ImageFormat::Depth; + dstImageDesc.data = readbackDepthBuffer.data(); + dstImageDesc.dataSize = sizeof(float) * readbackDepthBuffer.size(); + dstImageDesc.dataType = DataType::Float32; + } + renderer->ReadTexture(*readbackTex, TextureRegion{ Offset3D{}, texDesc.extent }, dstImageDesc); + + SaveDepthImageTGA(readbackDepthBuffer, resolution, "DepthBuffer.Result.tga", 1.0f, 10.0f); + + // Clear resources + renderer->Release(*pso); + renderer->Release(*psoLayout); + renderer->Release(*renderTarget); + renderer->Release(*readbackTex); + return TestResult::Passed; }