Add Vulkan RHI minimal backend path

This commit is contained in:
2026-03-27 12:05:12 +08:00
parent 90961f01aa
commit c33404767e
25 changed files with 1894 additions and 16 deletions

View File

@@ -14,6 +14,10 @@
#include "XCEngine/RHI/RHIScreenshot.h"
#include "XCEngine/RHI/RHIEnums.h"
#if defined(XCENGINE_SUPPORT_VULKAN)
#include "XCEngine/RHI/Vulkan/VulkanSwapChain.h"
#endif
using namespace XCEngine::Debug;
using namespace XCEngine::Containers;
@@ -83,7 +87,7 @@ void RHIIntegrationFixture::SetUp() {
ASSERT_NE(mDevice, nullptr);
bool initResult = false;
if (GetParam() == RHIType::D3D12) {
if (GetParam() == RHIType::D3D12 || GetParam() == RHIType::Vulkan) {
RHIDeviceDesc desc = {};
desc.enableDebugLayer = false;
desc.enableGPUValidation = false;
@@ -132,7 +136,7 @@ void RHIIntegrationFixture::SetUp() {
auto* rtv = new D3D12ResourceView();
rtv->InitializeAsRenderTarget(device, backBuffer.GetResource(), &rtvDesc, mRTVHeap, i);
mRTVs.push_back(rtv);
mBackBufferViews.push_back(rtv);
}
mDepthStencilTexture = new D3D12Texture();
@@ -152,26 +156,55 @@ void RHIIntegrationFixture::SetUp() {
}
void RHIIntegrationFixture::BeginRender() {
if (GetParam() == RHIType::Vulkan) {
#if defined(XCENGINE_SUPPORT_VULKAN)
auto* vulkanSwapChain = static_cast<VulkanSwapChain*>(mSwapChain);
ASSERT_NE(vulkanSwapChain, nullptr);
ASSERT_TRUE(vulkanSwapChain->AcquireNextImage());
#endif
}
mCurrentBackBufferIndex = mSwapChain->GetCurrentBackBufferIndex();
Log("[TEST] BeginRender: backBufferIndex=%d", mCurrentBackBufferIndex);
}
void RHIIntegrationFixture::SetRenderTargetForClear(bool includeDepthStencil) {
if (GetParam() == RHIType::D3D12) {
Log("[TEST] SetRenderTargetForClear: D3D12 branch, mRTVs.size=%d, index=%d",
(int)mRTVs.size(), mCurrentBackBufferIndex);
if (!mRTVs.empty() && mCurrentBackBufferIndex < mRTVs.size()) {
Log("[TEST] SetRenderTargetForClear: D3D12 branch, mBackBufferViews.size=%d, index=%d",
(int)mBackBufferViews.size(), mCurrentBackBufferIndex);
if (!mBackBufferViews.empty() && mCurrentBackBufferIndex < mBackBufferViews.size()) {
D3D12Texture* backBuffer = static_cast<D3D12Texture*>(mSwapChain->GetCurrentBackBuffer());
D3D12CommandList* d3d12CmdList = static_cast<D3D12CommandList*>(mCommandList);
Log("[TEST] SetRenderTargetForClear: calling TransitionBarrier");
d3d12CmdList->TransitionBarrier(backBuffer->GetResource(), ResourceStates::Present, ResourceStates::RenderTarget);
RHIResourceView* rtv = mRTVs[mCurrentBackBufferIndex];
RHIResourceView* rtv = mBackBufferViews[mCurrentBackBufferIndex];
Log("[TEST] SetRenderTargetForClear: calling SetRenderTargets, rtv=%p", (void*)rtv);
mCommandList->SetRenderTargets(1, &rtv, includeDepthStencil ? mDSV : nullptr);
Log("[TEST] SetRenderTargetForClear: done");
} else {
Log("[TEST] SetRenderTargetForClear: skipped - condition failed");
}
} else if (GetParam() == RHIType::Vulkan) {
if (mCurrentBackBufferIndex < 0) {
return;
}
const size_t backBufferIndex = static_cast<size_t>(mCurrentBackBufferIndex);
if (mBackBufferViews.size() <= backBufferIndex) {
mBackBufferViews.resize(backBufferIndex + 1, nullptr);
}
if (mBackBufferViews[backBufferIndex] == nullptr) {
ResourceViewDesc viewDesc = {};
viewDesc.dimension = ResourceViewDimension::Texture2D;
viewDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
viewDesc.arraySize = 1;
mBackBufferViews[backBufferIndex] = mDevice->CreateRenderTargetView(GetCurrentBackBuffer(), viewDesc);
}
ASSERT_NE(mBackBufferViews[backBufferIndex], nullptr);
RHIResourceView* rtv = mBackBufferViews[backBufferIndex];
mCommandList->SetRenderTargets(1, &rtv, nullptr);
}
}
@@ -191,13 +224,17 @@ void RHIIntegrationFixture::TearDown() {
mScreenshot = nullptr;
}
if (GetParam() == RHIType::D3D12) {
for (auto* rtv : mRTVs) {
delete rtv;
for (auto* backBufferView : mBackBufferViews) {
if (backBufferView) {
backBufferView->Shutdown();
delete backBufferView;
}
mRTVs.clear();
}
mBackBufferViews.clear();
if (GetParam() == RHIType::D3D12) {
if (mDSV) {
mDSV->Shutdown();
delete mDSV;
mDSV = nullptr;
}
@@ -276,6 +313,8 @@ void RHIIntegrationFixture::WaitForGPU() {
delete fence;
}
Sleep(100);
} else if (GetParam() == RHIType::Vulkan) {
mCommandQueue->WaitForIdle();
}
}

View File

@@ -2,6 +2,7 @@
#include <gtest/gtest.h>
#include <string>
#include <vector>
#include <windows.h>
#include "XCEngine/RHI/RHIFactory.h"
@@ -23,6 +24,10 @@
#include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h"
#endif
#if defined(XCENGINE_SUPPORT_VULKAN)
#include "XCEngine/RHI/Vulkan/VulkanSwapChain.h"
#endif
namespace XCEngine {
namespace RHI {
namespace Integration {
@@ -62,12 +67,12 @@ private:
RHIScreenshot* mScreenshot = nullptr;
HWND mWindow = nullptr;
int mCurrentBackBufferIndex = 0;
std::vector<RHIResourceView*> mBackBufferViews;
#if defined(XCENGINE_SUPPORT_D3D12)
D3D12DescriptorHeap* mRTVHeap = nullptr;
D3D12DescriptorHeap* mDSVHeap = nullptr;
D3D12Texture* mDepthStencilTexture = nullptr;
std::vector<RHIResourceView*> mRTVs;
RHIResourceView* mDSV = nullptr;
#endif
};

View File

@@ -33,11 +33,16 @@ target_link_libraries(rhi_integration_minimal PRIVATE
GTest::gtest
)
if(TARGET Vulkan::Vulkan)
target_link_libraries(rhi_integration_minimal PRIVATE Vulkan::Vulkan)
endif()
target_compile_definitions(rhi_integration_minimal PRIVATE
UNICODE
_UNICODE
XCENGINE_SUPPORT_OPENGL
XCENGINE_SUPPORT_D3D12
XCENGINE_SUPPORT_VULKAN
)
add_custom_command(TARGET rhi_integration_minimal POST_BUILD

View File

@@ -20,7 +20,16 @@ protected:
};
const char* GetScreenshotFilename(RHIType type) {
return type == RHIType::D3D12 ? "minimal_d3d12.ppm" : "minimal_opengl.ppm";
switch (type) {
case RHIType::D3D12:
return "minimal_d3d12.ppm";
case RHIType::OpenGL:
return "minimal_opengl.ppm";
case RHIType::Vulkan:
return "minimal_vulkan.ppm";
default:
return "minimal_unknown.ppm";
}
}
int GetComparisonThreshold(RHIType type) {
@@ -94,6 +103,9 @@ TEST_P(MinimalTest, RenderClear) {
INSTANTIATE_TEST_SUITE_P(D3D12, MinimalTest, ::testing::Values(RHIType::D3D12));
INSTANTIATE_TEST_SUITE_P(OpenGL, MinimalTest, ::testing::Values(RHIType::OpenGL));
#if defined(XCENGINE_SUPPORT_VULKAN)
INSTANTIATE_TEST_SUITE_P(Vulkan, MinimalTest, ::testing::Values(RHIType::Vulkan));
#endif
GTEST_API_ int main(int argc, char** argv) {
Logger::Get().Initialize();

View File

@@ -6,6 +6,7 @@ find_package(GTest REQUIRED)
set(TEST_SOURCES
fixtures/RHITestFixture.cpp
test_factory.cpp
test_device.cpp
test_buffer.cpp
test_texture.cpp
@@ -30,7 +31,10 @@ set(TEST_SOURCES
add_executable(rhi_unit_tests ${TEST_SOURCES})
target_compile_definitions(rhi_unit_tests PRIVATE XCENGINE_SUPPORT_OPENGL)
target_compile_definitions(rhi_unit_tests PRIVATE
XCENGINE_SUPPORT_OPENGL
XCENGINE_SUPPORT_VULKAN
)
target_link_libraries(rhi_unit_tests PRIVATE
d3d12
@@ -42,6 +46,10 @@ target_link_libraries(rhi_unit_tests PRIVATE
GTest::gtest_main
)
if(TARGET Vulkan::Vulkan)
target_link_libraries(rhi_unit_tests PRIVATE Vulkan::Vulkan)
endif()
target_include_directories(rhi_unit_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/fixtures
${PROJECT_ROOT_DIR}/engine/include

View File

@@ -5,6 +5,10 @@
#include "XCEngine/RHI/RHIFactory.h"
#include "XCEngine/RHI/D3D12/D3D12Device.h"
#if defined(XCENGINE_SUPPORT_VULKAN)
#include "XCEngine/RHI/Vulkan/VulkanDevice.h"
#endif
using namespace XCEngine::RHI;
TEST(RHIFactory, CreateD3D12Device_ReturnsValidPointer) {
@@ -30,11 +34,29 @@ TEST(RHIFactory, CreateInvalidDevice_ReturnsNullptr) {
}
TEST(RHIFactory, CreateInvalidType_ReturnsNullptr) {
RHIDevice* device = RHIFactory::CreateRHIDevice(RHIType::Vulkan);
RHIDevice* device = RHIFactory::CreateRHIDevice(RHIType::Metal);
ASSERT_EQ(device, nullptr);
}
#if defined(XCENGINE_SUPPORT_VULKAN)
TEST(RHIFactory, CreateVulkanDevice_ReturnsValidPointer) {
RHIDevice* device = RHIFactory::CreateRHIDevice(RHIType::Vulkan);
ASSERT_NE(device, nullptr);
delete device;
}
TEST(RHIFactory, CreateVulkanDeviceByName_ReturnsValidPointer) {
RHIDevice* device = RHIFactory::CreateRHIDevice("Vulkan");
ASSERT_NE(device, nullptr);
delete device;
}
#endif
TEST(D3D12DeviceCreation, DirectCreation_Success) {
D3D12Device* device = new D3D12Device();
@@ -42,3 +64,13 @@ TEST(D3D12DeviceCreation, DirectCreation_Success) {
delete device;
}
#if defined(XCENGINE_SUPPORT_VULKAN)
TEST(VulkanDeviceCreation, DirectCreation_Success) {
VulkanDevice* device = new VulkanDevice();
ASSERT_NE(device, nullptr);
delete device;
}
#endif