Skip to content

Commit

Permalink
Added StencilBuffer test to Testbed.
Browse files Browse the repository at this point in the history
  • Loading branch information
LukasBanana committed Aug 8, 2023
1 parent 3e44487 commit b798ed3
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 8 deletions.
Binary file not shown.
8 changes: 5 additions & 3 deletions tests/Testbed/TestDepthBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ DEF_TEST( DepthBuffer )
RenderTarget* renderTarget = renderer->CreateRenderTarget(renderTargetDesc);
renderTarget->SetName("renderTarget");

// Create PSO for rendering
// Create PSO for rendering to the depth buffer
PipelineLayout* psoLayout = renderer->CreatePipelineLayout(Parse("cbuffer(Scene@1):vert:frag"));

GraphicsPipelineDescriptor psoDesc;
Expand Down Expand Up @@ -107,7 +107,9 @@ DEF_TEST( DepthBuffer )
};
const TextureRegion readbackTexRegion{ readbackTexPosition, Extent3D{ 1, 1, 1 } };

float readbackDepthValue = -1.0f;
constexpr float invalidDepthValue = -1.0f;

float readbackDepthValue = invalidDepthValue;

DstImageDescriptor dstImageDesc;
{
Expand All @@ -128,7 +130,7 @@ DEF_TEST( DepthBuffer )
{
dstImageDesc.format = ImageFormat::Depth;
dstImageDesc.data = readbackDepthBuffer.data();
dstImageDesc.dataSize = sizeof(float) * readbackDepthBuffer.size();
dstImageDesc.dataSize = sizeof(decltype(readbackDepthBuffer)::value_type) * readbackDepthBuffer.size();
dstImageDesc.dataType = DataType::Float32;
}
renderer->ReadTexture(*readbackTex, TextureRegion{ Offset3D{}, texDesc.extent }, dstImageDesc);
Expand Down
163 changes: 162 additions & 1 deletion tests/Testbed/TestStencilBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,172 @@
*/

#include "Testbed.h"
#include <LLGL/Utils/Parse.h>
#include <Gauss/ProjectionMatrix4.h>
#include <Gauss/Translate.h>
#include <Gauss/Rotate.h>
#include <limits.h>


DEF_TEST( StencilBuffer )
{
//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::D24UNormS8UInt;
texDesc.extent.width = resolution.width;
texDesc.extent.height = resolution.height;
texDesc.bindFlags = BindFlags::DepthStencilAttachment;
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 to the stencil buffer with dynamic reference value
PipelineLayout* psoLayout = renderer->CreatePipelineLayout(Parse("cbuffer(Scene@1):vert:frag"));

GraphicsPipelineDescriptor psoDesc;
{
psoDesc.pipelineLayout = psoLayout;
psoDesc.renderPass = renderTarget->GetRenderPass();
psoDesc.vertexShader = shaders[VSSolid];
psoDesc.stencil.testEnabled = true;
psoDesc.stencil.referenceDynamic = true;
psoDesc.stencil.front.compareOp = LLGL::CompareOp::Greater;
psoDesc.stencil.front.stencilFailOp = LLGL::StencilOp::Keep;
psoDesc.stencil.front.depthFailOp = LLGL::StencilOp::Keep;
psoDesc.stencil.front.depthPassOp = LLGL::StencilOp::Replace;
psoDesc.rasterizer.cullMode = CullMode::Back;
}
PipelineState* pso = renderer->CreatePipelineState(psoDesc);

if (const Report* report = pso->GetReport())
{
if (report->HasErrors())
{
Log::Errorf("PSO creation failed:\n%s", report->GetText());
return TestResult::FailedErrors;
}
}

// 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
constexpr std::uint32_t stencilRef = 50;
static_assert(stencilRef <= UINT8_MAX, "'stencilRef' must not be greater than UINT8_MAX");

cmdBuffer->Begin();
{
cmdBuffer->UpdateBuffer(*sceneCbuffer, 0, &sceneConstants, sizeof(sceneConstants));
cmdBuffer->BeginRenderPass(*renderTarget);
{
// Draw scene
cmdBuffer->Clear(ClearFlags::Stencil);
cmdBuffer->SetPipelineState(*pso);
cmdBuffer->SetStencilReference(stencilRef);
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<std::int32_t>(resolution.width/2),
static_cast<std::int32_t>(resolution.height/2),
0,
};
const TextureRegion readbackTexRegion{ readbackTexPosition, Extent3D{ 1, 1, 1 } };

constexpr std::uint8_t invalidStencilValue = 0xFF;

std::uint8_t readbackStencilValue = invalidStencilValue;

DstImageDescriptor dstImageDesc;
{
dstImageDesc.format = ImageFormat::Stencil;
dstImageDesc.data = &readbackStencilValue;
dstImageDesc.dataSize = sizeof(readbackStencilValue);
dstImageDesc.dataType = DataType::UInt8;
}
renderer->ReadTexture(*readbackTex, readbackTexRegion, dstImageDesc);

const int deltaStencilValue = std::abs(static_cast<int>(readbackStencilValue) - static_cast<int>(stencilRef));

// Match entire depth and create delta heat map
std::vector<std::uint8_t> readbackStencilBuffer;
readbackStencilBuffer.resize(texDesc.extent.width * texDesc.extent.height, 0);
{
dstImageDesc.format = ImageFormat::Stencil;
dstImageDesc.data = readbackStencilBuffer.data();
dstImageDesc.dataSize = sizeof(decltype(readbackStencilBuffer)::value_type) * readbackStencilBuffer.size();
dstImageDesc.dataType = DataType::UInt8;
}
renderer->ReadTexture(*readbackTex, TextureRegion{ Offset3D{}, texDesc.extent }, dstImageDesc);

SaveStencilImageTGA(readbackStencilBuffer, resolution, "StencilBuffer_Set50");

const int diff = DiffImagesTGA("StencilBuffer_Set50");

// Clear resources
renderer->Release(*pso);
renderer->Release(*psoLayout);
renderer->Release(*renderTarget);
renderer->Release(*readbackTex);

// Evaluate readback result
if (readbackStencilValue == invalidStencilValue)
{
Log::Errorf("Failed to read back value from stencil buffer texture at center\n");
return TestResult::FailedErrors;
}
if (deltaStencilValue > 0)
{
Log::Errorf(
"Mismatch between stencil buffer value at center (%d) and expected value (%d): delta = %d\n",
static_cast<int>(readbackStencilValue), static_cast<int>(stencilRef), deltaStencilValue
);
return TestResult::FailedMismatch;
}
if (diff != 0)
{
Log::Errorf("Mismatch between reference and result images for stencil buffer (diff = %d)\n", diff);
return TestResult::FailedMismatch;
}

return TestResult::Passed;
}

20 changes: 16 additions & 4 deletions tests/Testbed/TestbedContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,14 @@ void TestbedContext::RunAllTests()
RUN_TEST( BufferCopy );
RUN_TEST( TextureTypes );
RUN_TEST( TextureWriteAndRead );
RUN_TEST( TextureCopy );
RUN_TEST( TextureToBufferCopy );
RUN_TEST( BufferToTextureCopy );
//RUN_TEST( TextureCopy ); //TODO: not implemented yet
//RUN_TEST( TextureToBufferCopy ); //TODO: not implemented yet
//RUN_TEST( BufferToTextureCopy ); //TODO: not implemented yet
RUN_TEST( DepthBuffer );
RUN_TEST( StencilBuffer );
RUN_TEST( RenderTargetNoAttachments );
RUN_TEST( RenderTarget1Attachment );
RUN_TEST( RenderTargetNAttachments );
//RUN_TEST( RenderTargetNAttachments ); //TODO: not implemented yet

#undef RUN_TEST

Expand Down Expand Up @@ -944,6 +944,18 @@ void TestbedContext::SaveDepthImageTGA(const std::vector<float>& image, const LL
SaveImageTGA(colors, extent, path + name + ".Result.tga", verbose);
}

void TestbedContext::SaveStencilImageTGA(const std::vector<std::uint8_t>& image, const LLGL::Extent2D& extent, const std::string& name)
{
std::vector<ColorRGBub> colors;
colors.resize(image.size());

for (std::size_t i = 0; i < image.size(); ++i)
colors[i] = ColorRGBub{ image[i] };

const std::string path = outputDir + moduleName + "/";
SaveImageTGA(colors, extent, path + name + ".Result.tga", verbose);
}

static int GetColorDiff(std::uint8_t a, std::uint8_t b)
{
return static_cast<int>(a < b ? b - a : a - b);
Expand Down
1 change: 1 addition & 0 deletions tests/Testbed/TestbedContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ class TestbedContext

void SaveDepthImageTGA(const std::vector<float>& image, const LLGL::Extent2D& extent, const std::string& name);
void SaveDepthImageTGA(const std::vector<float>& image, const LLGL::Extent2D& extent, const std::string& name, float nearPlane, float farPlane);
void SaveStencilImageTGA(const std::vector<std::uint8_t>& image, const LLGL::Extent2D& extent, const std::string& name);

// Creates a heat-map image from the two input filenames and returns the highest difference pixel value. A negative value indicates an error.
int DiffImagesTGA(const std::string& name, int threshold = 1, int scale = 1);
Expand Down

0 comments on commit b798ed3

Please sign in to comment.