From 2ac32e13308c087e4e3031ed4c088db95ab1f3fb Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Wed, 25 Mar 2026 13:03:07 +0800 Subject: [PATCH] RHI: Add RenderPass and Framebuffer unit tests with CMake support --- tests/RHI/unit/CMakeLists.txt | 2 + tests/RHI/unit/test_framebuffer.cpp | 332 ++++++++++++++++++++++++++++ tests/RHI/unit/test_render_pass.cpp | 152 +++++++++++++ 3 files changed, 486 insertions(+) create mode 100644 tests/RHI/unit/test_framebuffer.cpp create mode 100644 tests/RHI/unit/test_render_pass.cpp diff --git a/tests/RHI/unit/CMakeLists.txt b/tests/RHI/unit/CMakeLists.txt index 5f56bdec..91c9bc4b 100644 --- a/tests/RHI/unit/CMakeLists.txt +++ b/tests/RHI/unit/CMakeLists.txt @@ -14,6 +14,8 @@ set(TEST_SOURCES test_command_queue.cpp test_shader.cpp test_pipeline_state.cpp + test_render_pass.cpp + test_framebuffer.cpp test_fence.cpp test_sampler.cpp ${CMAKE_SOURCE_DIR}/tests/opengl/package/src/glad.c diff --git a/tests/RHI/unit/test_framebuffer.cpp b/tests/RHI/unit/test_framebuffer.cpp new file mode 100644 index 00000000..35a2c6d6 --- /dev/null +++ b/tests/RHI/unit/test_framebuffer.cpp @@ -0,0 +1,332 @@ +#include "fixtures/RHITestFixture.h" +#include "XCEngine/RHI/RHIRenderPass.h" +#include "XCEngine/RHI/RHIFramebuffer.h" +#include "XCEngine/RHI/RHITexture.h" +#include "XCEngine/RHI/RHIResourceView.h" + +using namespace XCEngine::RHI; + +TEST_P(RHITestFixture, Framebuffer_Create_Basic) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = static_cast(Format::R8G8B8A8_UNorm); + colorAttachment.loadOp = LoadAction::Clear; + colorAttachment.storeOp = StoreAction::Store; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + ASSERT_NE(renderPass, nullptr); + + TextureDesc texDesc = {}; + texDesc.textureType = static_cast(TextureType::Texture2D); + texDesc.width = 256; + texDesc.height = 256; + texDesc.format = static_cast(Format::R8G8B8A8_UNorm); + texDesc.sampleCount = 1; + + RHITexture* texture = GetDevice()->CreateTexture(texDesc); + if (texture != nullptr) { + ResourceViewDesc rtvDesc = {}; + rtvDesc.format = static_cast(Format::R8G8B8A8_UNorm); + RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, rtvDesc); + if (rtv != nullptr) { + RHIFramebuffer* fb = GetDevice()->CreateFramebuffer( + renderPass, 256, 256, 1, &rtv, nullptr); + if (fb != nullptr) { + EXPECT_EQ(fb->GetWidth(), 256u); + EXPECT_EQ(fb->GetHeight(), 256u); + fb->Shutdown(); + delete fb; + } + rtv->Shutdown(); + delete rtv; + } + texture->Shutdown(); + delete texture; + } + renderPass->Shutdown(); + delete renderPass; +} + +TEST_P(RHITestFixture, Framebuffer_GetWidthHeight) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = static_cast(Format::R8G8B8A8_UNorm); + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + ASSERT_NE(renderPass, nullptr); + + TextureDesc texDesc = {}; + texDesc.textureType = static_cast(TextureType::Texture2D); + texDesc.width = 512; + texDesc.height = 512; + texDesc.format = static_cast(Format::R8G8B8A8_UNorm); + + RHITexture* texture = GetDevice()->CreateTexture(texDesc); + if (texture != nullptr) { + ResourceViewDesc rtvDesc = {}; + rtvDesc.format = static_cast(Format::R8G8B8A8_UNorm); + RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, rtvDesc); + if (rtv != nullptr) { + RHIFramebuffer* fb = GetDevice()->CreateFramebuffer( + renderPass, 512, 512, 1, &rtv, nullptr); + if (fb != nullptr) { + EXPECT_EQ(fb->GetWidth(), 512u); + EXPECT_EQ(fb->GetHeight(), 512u); + fb->Shutdown(); + delete fb; + } + rtv->Shutdown(); + delete rtv; + } + texture->Shutdown(); + delete texture; + } + renderPass->Shutdown(); + delete renderPass; +} + +TEST_P(RHITestFixture, Framebuffer_IsValid) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = static_cast(Format::R8G8B8A8_UNorm); + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + ASSERT_NE(renderPass, nullptr); + + TextureDesc texDesc = {}; + texDesc.textureType = static_cast(TextureType::Texture2D); + texDesc.width = 256; + texDesc.height = 256; + texDesc.format = static_cast(Format::R8G8B8A8_UNorm); + + RHITexture* texture = GetDevice()->CreateTexture(texDesc); + if (texture != nullptr) { + ResourceViewDesc rtvDesc = {}; + rtvDesc.format = static_cast(Format::R8G8B8A8_UNorm); + RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, rtvDesc); + if (rtv != nullptr) { + RHIFramebuffer* fb = GetDevice()->CreateFramebuffer( + renderPass, 256, 256, 1, &rtv, nullptr); + if (fb != nullptr) { + EXPECT_TRUE(fb->IsValid()); + fb->Shutdown(); + delete fb; + } + rtv->Shutdown(); + delete rtv; + } + texture->Shutdown(); + delete texture; + } + renderPass->Shutdown(); + delete renderPass; +} + +TEST_P(RHITestFixture, Framebuffer_Shutdown) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = static_cast(Format::R8G8B8A8_UNorm); + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + ASSERT_NE(renderPass, nullptr); + + TextureDesc texDesc = {}; + texDesc.textureType = static_cast(TextureType::Texture2D); + texDesc.width = 256; + texDesc.height = 256; + texDesc.format = static_cast(Format::R8G8B8A8_UNorm); + + RHITexture* texture = GetDevice()->CreateTexture(texDesc); + if (texture != nullptr) { + ResourceViewDesc rtvDesc = {}; + rtvDesc.format = static_cast(Format::R8G8B8A8_UNorm); + RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, rtvDesc); + if (rtv != nullptr) { + RHIFramebuffer* fb = GetDevice()->CreateFramebuffer( + renderPass, 256, 256, 1, &rtv, nullptr); + if (fb != nullptr) { + fb->Shutdown(); + delete fb; + } + rtv->Shutdown(); + delete rtv; + } + texture->Shutdown(); + delete texture; + } + renderPass->Shutdown(); + delete renderPass; +} + +TEST_P(RHITestFixture, Framebuffer_WithDepthStencil) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = static_cast(Format::R8G8B8A8_UNorm); + + AttachmentDesc depthAttachment = {}; + depthAttachment.format = Format::D24_UNorm_S8_UInt; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, &depthAttachment); + ASSERT_NE(renderPass, nullptr); + + TextureDesc colorDesc = {}; + colorDesc.textureType = static_cast(TextureType::Texture2D); + colorDesc.width = 256; + colorDesc.height = 256; + colorDesc.format = static_cast(Format::R8G8B8A8_UNorm); + + TextureDesc depthDesc = {}; + depthDesc.textureType = static_cast(TextureType::Texture2D); + depthDesc.width = 256; + depthDesc.height = 256; + depthDesc.format = static_cast(Format::D24_UNorm_S8_UInt); + + RHITexture* colorTexture = GetDevice()->CreateTexture(colorDesc); + RHITexture* depthTexture = GetDevice()->CreateTexture(depthDesc); + + if (colorTexture != nullptr && depthTexture != nullptr) { + ResourceViewDesc rtvDesc = {}; + rtvDesc.format = static_cast(Format::R8G8B8A8_UNorm); + RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(colorTexture, rtvDesc); + + ResourceViewDesc dsvDesc = {}; + dsvDesc.format = static_cast(Format::D24_UNorm_S8_UInt); + RHIResourceView* dsv = GetDevice()->CreateDepthStencilView(depthTexture, dsvDesc); + + if (rtv != nullptr && dsv != nullptr) { + RHIFramebuffer* fb = GetDevice()->CreateFramebuffer( + renderPass, 256, 256, 1, &rtv, dsv); + if (fb != nullptr) { + EXPECT_TRUE(fb->IsValid()); + fb->Shutdown(); + delete fb; + } + } + if (dsv != nullptr) { + dsv->Shutdown(); + delete dsv; + } + if (rtv != nullptr) { + rtv->Shutdown(); + delete rtv; + } + } + + if (depthTexture != nullptr) { + depthTexture->Shutdown(); + delete depthTexture; + } + if (colorTexture != nullptr) { + colorTexture->Shutdown(); + delete colorTexture; + } + renderPass->Shutdown(); + delete renderPass; +} + +TEST_P(RHITestFixture, Framebuffer_MultipleColorAttachments) { + AttachmentDesc colorAttachments[2] = {}; + colorAttachments[0].format = Format::R8G8B8A8_UNorm; + colorAttachments[1].format = Format::R8G8B8A8_UNorm; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(2, colorAttachments, nullptr); + ASSERT_NE(renderPass, nullptr); + + TextureDesc texDesc = {}; + texDesc.textureType = static_cast(TextureType::Texture2D); + texDesc.width = 256; + texDesc.height = 256; + texDesc.format = static_cast(Format::R8G8B8A8_UNorm); + + RHITexture* texture0 = GetDevice()->CreateTexture(texDesc); + RHITexture* texture1 = GetDevice()->CreateTexture(texDesc); + + if (texture0 != nullptr && texture1 != nullptr) { + ResourceViewDesc rtvDesc = {}; + rtvDesc.format = static_cast(Format::R8G8B8A8_UNorm); + RHIResourceView* rtv0 = GetDevice()->CreateRenderTargetView(texture0, rtvDesc); + RHIResourceView* rtv1 = GetDevice()->CreateRenderTargetView(texture1, rtvDesc); + + if (rtv0 != nullptr && rtv1 != nullptr) { + RHIResourceView* views[2] = { rtv0, rtv1 }; + RHIFramebuffer* fb = GetDevice()->CreateFramebuffer( + renderPass, 256, 256, 2, views, nullptr); + if (fb != nullptr) { + EXPECT_EQ(fb->GetWidth(), 256u); + EXPECT_EQ(fb->GetHeight(), 256u); + fb->Shutdown(); + delete fb; + } + } + if (rtv1 != nullptr) { + rtv1->Shutdown(); + delete rtv1; + } + if (rtv0 != nullptr) { + rtv0->Shutdown(); + delete rtv0; + } + } + + if (texture1 != nullptr) { + texture1->Shutdown(); + delete texture1; + } + if (texture0 != nullptr) { + texture0->Shutdown(); + delete texture0; + } + renderPass->Shutdown(); + delete renderPass; +} + +TEST_P(RHITestFixture, Framebuffer_InvalidDimensions) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = static_cast(Format::R8G8B8A8_UNorm); + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + ASSERT_NE(renderPass, nullptr); + + TextureDesc texDesc = {}; + texDesc.textureType = static_cast(TextureType::Texture2D); + texDesc.width = 256; + texDesc.height = 256; + texDesc.format = static_cast(Format::R8G8B8A8_UNorm); + + RHITexture* texture = GetDevice()->CreateTexture(texDesc); + if (texture != nullptr) { + ResourceViewDesc rtvDesc = {}; + rtvDesc.format = static_cast(Format::R8G8B8A8_UNorm); + RHIResourceView* rtv = GetDevice()->CreateRenderTargetView(texture, rtvDesc); + if (rtv != nullptr) { + RHIFramebuffer* fb = GetDevice()->CreateFramebuffer( + renderPass, 0, 0, 1, &rtv, nullptr); + if (fb != nullptr) { + EXPECT_EQ(fb->GetWidth(), 0u); + EXPECT_EQ(fb->GetHeight(), 0u); + fb->Shutdown(); + delete fb; + } + rtv->Shutdown(); + delete rtv; + } + texture->Shutdown(); + delete texture; + } + renderPass->Shutdown(); + delete renderPass; +} + +TEST_P(RHITestFixture, Framebuffer_NullColorAttachments) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = static_cast(Format::R8G8B8A8_UNorm); + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + ASSERT_NE(renderPass, nullptr); + + RHIResourceView* nullView = nullptr; + RHIFramebuffer* fb = GetDevice()->CreateFramebuffer( + renderPass, 256, 256, 1, &nullView, nullptr); + if (fb != nullptr) { + EXPECT_EQ(fb->GetWidth(), 256u); + fb->Shutdown(); + delete fb; + } + renderPass->Shutdown(); + delete renderPass; +} diff --git a/tests/RHI/unit/test_render_pass.cpp b/tests/RHI/unit/test_render_pass.cpp new file mode 100644 index 00000000..8c3bd840 --- /dev/null +++ b/tests/RHI/unit/test_render_pass.cpp @@ -0,0 +1,152 @@ +#include "fixtures/RHITestFixture.h" +#include "XCEngine/RHI/RHIRenderPass.h" +#include "XCEngine/RHI/RHIFramebuffer.h" +#include "XCEngine/RHI/RHITexture.h" +#include "XCEngine/RHI/RHIResourceView.h" + +using namespace XCEngine::RHI; + +TEST_P(RHITestFixture, RenderPass_Create_ColorOnly) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = Format::R8G8B8A8_UNorm; + colorAttachment.loadOp = LoadAction::Clear; + colorAttachment.storeOp = StoreAction::Store; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + if (renderPass != nullptr) { + EXPECT_EQ(renderPass->GetColorAttachmentCount(), 1u); + renderPass->Shutdown(); + delete renderPass; + } +} + +TEST_P(RHITestFixture, RenderPass_Create_ColorAndDepth) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = Format::R8G8B8A8_UNorm; + colorAttachment.loadOp = LoadAction::Clear; + colorAttachment.storeOp = StoreAction::Store; + + AttachmentDesc depthAttachment = {}; + depthAttachment.format = Format::D24_UNorm_S8_UInt; + depthAttachment.loadOp = LoadAction::Clear; + depthAttachment.storeOp = StoreAction::Store; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, &depthAttachment); + if (renderPass != nullptr) { + EXPECT_EQ(renderPass->GetColorAttachmentCount(), 1u); + EXPECT_NE(renderPass->GetDepthStencilAttachment(), nullptr); + renderPass->Shutdown(); + delete renderPass; + } +} + +TEST_P(RHITestFixture, RenderPass_GetColorAttachmentCount) { + AttachmentDesc colorAttachments[2] = {}; + colorAttachments[0].format = Format::R8G8B8A8_UNorm; + colorAttachments[1].format = Format::R8G8B8A8_UNorm; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(2, colorAttachments, nullptr); + if (renderPass != nullptr) { + EXPECT_EQ(renderPass->GetColorAttachmentCount(), 2u); + renderPass->Shutdown(); + delete renderPass; + } +} + +TEST_P(RHITestFixture, RenderPass_GetColorAttachments) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = Format::R8G8B8A8_UNorm; + colorAttachment.loadOp = LoadAction::Clear; + colorAttachment.storeOp = StoreAction::Store; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + if (renderPass != nullptr) { + const AttachmentDesc* attachments = renderPass->GetColorAttachments(); + ASSERT_NE(attachments, nullptr); + EXPECT_EQ(attachments[0].format, Format::R8G8B8A8_UNorm); + renderPass->Shutdown(); + delete renderPass; + } +} + +TEST_P(RHITestFixture, RenderPass_GetDepthStencilAttachment) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = Format::R8G8B8A8_UNorm; + + AttachmentDesc depthAttachment = {}; + depthAttachment.format = Format::D24_UNorm_S8_UInt; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, &depthAttachment); + if (renderPass != nullptr) { + const AttachmentDesc* ds = renderPass->GetDepthStencilAttachment(); + ASSERT_NE(ds, nullptr); + EXPECT_EQ(ds->format, Format::D24_UNorm_S8_UInt); + renderPass->Shutdown(); + delete renderPass; + } +} + +TEST_P(RHITestFixture, RenderPass_Shutdown) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = Format::R8G8B8A8_UNorm; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + if (renderPass != nullptr) { + renderPass->Shutdown(); + EXPECT_EQ(renderPass->GetColorAttachmentCount(), 0u); + delete renderPass; + } +} + +TEST_P(RHITestFixture, RenderPass_DoubleShutdown) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = Format::R8G8B8A8_UNorm; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + if (renderPass != nullptr) { + renderPass->Shutdown(); + renderPass->Shutdown(); + delete renderPass; + } +} + +TEST_P(RHITestFixture, RenderPass_NullDepthStencil) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = Format::R8G8B8A8_UNorm; + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + if (renderPass != nullptr) { + EXPECT_EQ(renderPass->GetDepthStencilAttachment(), nullptr); + renderPass->Shutdown(); + delete renderPass; + } +} + +TEST_P(RHITestFixture, RenderPass_MultipleColorAttachments) { + AttachmentDesc colorAttachments[4] = {}; + for (int i = 0; i < 4; ++i) { + colorAttachments[i].format = Format::R8G8B8A8_UNorm; + colorAttachments[i].loadOp = LoadAction::Clear; + colorAttachments[i].storeOp = StoreAction::Store; + } + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(4, colorAttachments, nullptr); + if (renderPass != nullptr) { + EXPECT_EQ(renderPass->GetColorAttachmentCount(), 4u); + renderPass->Shutdown(); + delete renderPass; + } +} + +TEST_P(RHITestFixture, RenderPass_GetNativeHandle) { + AttachmentDesc colorAttachment = {}; + colorAttachment.format = static_cast(Format::R8G8B8A8_UNorm); + + RHIRenderPass* renderPass = GetDevice()->CreateRenderPass(1, &colorAttachment, nullptr); + if (renderPass != nullptr) { + void* handle = renderPass->GetNativeHandle(); + EXPECT_EQ(handle, nullptr); + renderPass->Shutdown(); + delete renderPass; + } +}