Add Vulkan sphere integration support

This commit is contained in:
2026-03-27 15:07:21 +08:00
parent 18fd09b36f
commit 22ccdfb371
16 changed files with 429 additions and 63 deletions

View File

@@ -139,13 +139,35 @@ void RHIIntegrationFixture::SetUp() {
mBackBufferViews.push_back(rtv);
}
mDepthStencilTexture = new D3D12Texture();
ASSERT_NE(mDepthStencilTexture, nullptr);
ASSERT_TRUE(mDepthStencilTexture->InitializeDepthStencil(device, width, height));
auto* depthStencilTexture = new D3D12Texture();
ASSERT_NE(depthStencilTexture, nullptr);
ASSERT_TRUE(depthStencilTexture->InitializeDepthStencil(device, width, height));
mDepthStencilTexture = depthStencilTexture;
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = D3D12ResourceView::CreateDepthStencilDesc(Format::D24_UNorm_S8_UInt, D3D12_DSV_DIMENSION_TEXTURE2D);
auto* d3d12DSV = new D3D12ResourceView();
d3d12DSV->InitializeAsDepthStencil(device, mDepthStencilTexture->GetResource(), &dsvDesc, mDSVHeap, 0);
mDSV = d3d12DSV;
d3d12DSV->InitializeAsDepthStencil(device, depthStencilTexture->GetResource(), &dsvDesc, mDSVHeap, 0);
mDepthStencilView = d3d12DSV;
} else if (GetParam() == RHIType::Vulkan) {
TextureDesc depthDesc = {};
depthDesc.width = static_cast<uint32_t>(width);
depthDesc.height = static_cast<uint32_t>(height);
depthDesc.depth = 1;
depthDesc.mipLevels = 1;
depthDesc.arraySize = 1;
depthDesc.format = static_cast<uint32_t>(Format::D24_UNorm_S8_UInt);
depthDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
depthDesc.sampleCount = 1;
depthDesc.sampleQuality = 0;
depthDesc.flags = 0;
mDepthStencilTexture = mDevice->CreateTexture(depthDesc);
ASSERT_NE(mDepthStencilTexture, nullptr);
ResourceViewDesc depthViewDesc = {};
depthViewDesc.dimension = ResourceViewDimension::Texture2D;
depthViewDesc.format = static_cast<uint32_t>(Format::D24_UNorm_S8_UInt);
mDepthStencilView = mDevice->CreateDepthStencilView(mDepthStencilTexture, depthViewDesc);
ASSERT_NE(mDepthStencilView, nullptr);
}
mScreenshot = RHIScreenshot::Create(GetParam());
@@ -179,7 +201,7 @@ void RHIIntegrationFixture::SetRenderTargetForClear(bool includeDepthStencil) {
d3d12CmdList->TransitionBarrier(backBuffer->GetResource(), ResourceStates::Present, ResourceStates::RenderTarget);
RHIResourceView* rtv = mBackBufferViews[mCurrentBackBufferIndex];
Log("[TEST] SetRenderTargetForClear: calling SetRenderTargets, rtv=%p", (void*)rtv);
mCommandList->SetRenderTargets(1, &rtv, includeDepthStencil ? mDSV : nullptr);
mCommandList->SetRenderTargets(1, &rtv, includeDepthStencil ? mDepthStencilView : nullptr);
Log("[TEST] SetRenderTargetForClear: done");
} else {
Log("[TEST] SetRenderTargetForClear: skipped - condition failed");
@@ -204,7 +226,7 @@ void RHIIntegrationFixture::SetRenderTargetForClear(bool includeDepthStencil) {
ASSERT_NE(mBackBufferViews[backBufferIndex], nullptr);
RHIResourceView* rtv = mBackBufferViews[backBufferIndex];
mCommandList->SetRenderTargets(1, &rtv, nullptr);
mCommandList->SetRenderTargets(1, &rtv, includeDepthStencil ? mDepthStencilView : nullptr);
}
}
@@ -232,19 +254,19 @@ void RHIIntegrationFixture::TearDown() {
}
mBackBufferViews.clear();
if (mDepthStencilView) {
mDepthStencilView->Shutdown();
delete mDepthStencilView;
mDepthStencilView = nullptr;
}
if (mDepthStencilTexture) {
mDepthStencilTexture->Shutdown();
delete mDepthStencilTexture;
mDepthStencilTexture = nullptr;
}
if (GetParam() == RHIType::D3D12) {
if (mDSV) {
mDSV->Shutdown();
delete mDSV;
mDSV = nullptr;
}
if (mDepthStencilTexture) {
mDepthStencilTexture->Shutdown();
delete mDepthStencilTexture;
mDepthStencilTexture = nullptr;
}
if (mRTVHeap) {
mRTVHeap->Shutdown();
delete mRTVHeap;

View File

@@ -11,7 +11,9 @@
#include "XCEngine/RHI/RHICommandList.h"
#include "XCEngine/RHI/RHISwapChain.h"
#include "XCEngine/RHI/RHIScreenshot.h"
#include "XCEngine/RHI/RHIResourceView.h"
#include "XCEngine/RHI/RHIEnums.h"
#include "XCEngine/RHI/RHITexture.h"
#include "XCEngine/RHI/OpenGL/OpenGLDevice.h"
#include "XCEngine/Debug/Logger.h"
#include "XCEngine/Debug/ConsoleLogSink.h"
@@ -68,12 +70,12 @@ private:
HWND mWindow = nullptr;
int mCurrentBackBufferIndex = 0;
std::vector<RHIResourceView*> mBackBufferViews;
RHITexture* mDepthStencilTexture = nullptr;
RHIResourceView* mDepthStencilView = nullptr;
#if defined(XCENGINE_SUPPORT_D3D12)
D3D12DescriptorHeap* mRTVHeap = nullptr;
D3D12DescriptorHeap* mDSVHeap = nullptr;
D3D12Texture* mDepthStencilTexture = nullptr;
RHIResourceView* mDSV = nullptr;
#endif
};

View File

@@ -9,6 +9,8 @@ set(PACKAGE_DIR ${CMAKE_SOURCE_DIR}/tests/opengl/package)
get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../.. ABSOLUTE)
find_package(Vulkan QUIET)
add_executable(rhi_integration_sphere
main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../fixtures/RHIIntegrationFixture.cpp
@@ -33,6 +35,60 @@ target_link_libraries(rhi_integration_sphere PRIVATE
GTest::gtest
)
if(Vulkan_FOUND)
set(XCENGINE_GLSLANG_VALIDATOR_HINT "$ENV{VULKAN_SDK}")
find_program(
XCENGINE_GLSLANG_VALIDATOR
NAMES glslangValidator glslangValidator.exe
HINTS
"${XCENGINE_GLSLANG_VALIDATOR_HINT}/Bin"
"${Vulkan_ROOT}/Bin")
if(NOT XCENGINE_GLSLANG_VALIDATOR)
file(GLOB XCENGINE_VULKAN_BIN_DIRS "D:/VulkanSDK/*/Bin")
if(XCENGINE_VULKAN_BIN_DIRS)
list(SORT XCENGINE_VULKAN_BIN_DIRS COMPARE NATURAL ORDER DESCENDING)
foreach(XCENGINE_VULKAN_BIN_DIR IN LISTS XCENGINE_VULKAN_BIN_DIRS)
find_program(
XCENGINE_GLSLANG_VALIDATOR
NAMES glslangValidator glslangValidator.exe
PATHS "${XCENGINE_VULKAN_BIN_DIR}"
NO_DEFAULT_PATH)
if(XCENGINE_GLSLANG_VALIDATOR)
break()
endif()
endforeach()
endif()
endif()
if(NOT XCENGINE_GLSLANG_VALIDATOR)
message(FATAL_ERROR "glslangValidator not found for Vulkan sphere shaders")
endif()
set(SPHERE_VULKAN_VERTEX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Res/Shader/sphere_vulkan.vert)
set(SPHERE_VULKAN_FRAGMENT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Res/Shader/sphere_vulkan.frag)
add_custom_command(TARGET rhi_integration_sphere PRE_BUILD
COMMAND ${XCENGINE_GLSLANG_VALIDATOR}
-V
-S
vert
-o
$<TARGET_FILE_DIR:rhi_integration_sphere>/sphere_vulkan.vert.spv
${SPHERE_VULKAN_VERTEX_SOURCE}
COMMAND ${XCENGINE_GLSLANG_VALIDATOR}
-V
-S
frag
-o
$<TARGET_FILE_DIR:rhi_integration_sphere>/sphere_vulkan.frag.spv
${SPHERE_VULKAN_FRAGMENT_SOURCE}
VERBATIM)
target_link_libraries(rhi_integration_sphere PRIVATE Vulkan::Vulkan)
target_compile_definitions(rhi_integration_sphere PRIVATE XCENGINE_SUPPORT_VULKAN)
endif()
target_compile_definitions(rhi_integration_sphere PRIVATE
UNICODE
_UNICODE

View File

@@ -0,0 +1,11 @@
#version 450
layout(set = 2, binding = 0) uniform texture2D uTexture;
layout(set = 3, binding = 0) uniform sampler uSampler;
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = texture(sampler2D(uTexture, uSampler), vTexCoord);
}

View File

@@ -0,0 +1,19 @@
#version 450
layout(location = 0) in vec4 aPosition;
layout(location = 1) in vec2 aTexCoord;
layout(set = 1, binding = 0, std140) uniform MatrixBuffer {
mat4 gProjectionMatrix;
mat4 gViewMatrix;
mat4 gModelMatrix;
};
layout(location = 0) out vec2 vTexCoord;
void main() {
vec4 positionWS = gModelMatrix * aPosition;
vec4 positionVS = gViewMatrix * positionWS;
gl_Position = gProjectionMatrix * positionVS;
vTexCoord = aTexCoord;
}

View File

@@ -1,5 +1,6 @@
#include <windows.h>
#include <filesystem>
#include <fstream>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
@@ -61,6 +62,27 @@ std::filesystem::path ResolveRuntimePath(const char* relativePath) {
return GetExecutableDirectory() / relativePath;
}
std::vector<uint8_t> LoadBinaryFileRelative(const char* filename) {
const std::filesystem::path path = ResolveRuntimePath(filename);
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
return {};
}
const std::streamsize size = file.tellg();
if (size <= 0) {
return {};
}
std::vector<uint8_t> bytes(static_cast<size_t>(size));
file.seekg(0, std::ios::beg);
if (!file.read(reinterpret_cast<char*>(bytes.data()), size)) {
return {};
}
return bytes;
}
void GenerateSphere(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices, float radius, int segments) {
vertices.clear();
indices.clear();
@@ -188,7 +210,16 @@ void main() {
)";
const char* GetScreenshotFilename(RHIType type) {
return type == RHIType::D3D12 ? "sphere_d3d12.ppm" : "sphere_opengl.ppm";
switch (type) {
case RHIType::D3D12:
return "sphere_d3d12.ppm";
case RHIType::OpenGL:
return "sphere_opengl.ppm";
case RHIType::Vulkan:
return "sphere_vulkan.ppm";
default:
return "sphere_unknown.ppm";
}
}
int GetComparisonThreshold(RHIType type) {
@@ -275,7 +306,7 @@ GraphicsPipelineDesc CreateSpherePipelineDesc(RHIType type, RHIPipelineLayout* p
desc.fragmentShader.sourceLanguage = ShaderLanguage::HLSL;
desc.fragmentShader.entryPoint = L"MainPS";
desc.fragmentShader.profile = L"ps_5_0";
} else {
} else if (type == RHIType::OpenGL) {
desc.vertexShader.source.assign(kSphereVertexShader, kSphereVertexShader + strlen(kSphereVertexShader));
desc.vertexShader.sourceLanguage = ShaderLanguage::GLSL;
desc.vertexShader.profile = L"vs_4_30";
@@ -283,6 +314,14 @@ GraphicsPipelineDesc CreateSpherePipelineDesc(RHIType type, RHIPipelineLayout* p
desc.fragmentShader.source.assign(kSphereFragmentShader, kSphereFragmentShader + strlen(kSphereFragmentShader));
desc.fragmentShader.sourceLanguage = ShaderLanguage::GLSL;
desc.fragmentShader.profile = L"fs_4_30";
} else if (type == RHIType::Vulkan) {
desc.vertexShader.source = LoadBinaryFileRelative("sphere_vulkan.vert.spv");
desc.vertexShader.sourceLanguage = ShaderLanguage::SPIRV;
desc.vertexShader.entryPoint = L"main";
desc.fragmentShader.source = LoadBinaryFileRelative("sphere_vulkan.frag.spv");
desc.fragmentShader.sourceLanguage = ShaderLanguage::SPIRV;
desc.fragmentShader.entryPoint = L"main";
}
return desc;
@@ -657,6 +696,9 @@ TEST_P(SphereTest, RenderSphere) {
INSTANTIATE_TEST_SUITE_P(D3D12, SphereTest, ::testing::Values(RHIType::D3D12));
INSTANTIATE_TEST_SUITE_P(OpenGL, SphereTest, ::testing::Values(RHIType::OpenGL));
#if defined(XCENGINE_SUPPORT_VULKAN)
INSTANTIATE_TEST_SUITE_P(Vulkan, SphereTest, ::testing::Values(RHIType::Vulkan));
#endif
GTEST_API_ int main(int argc, char** argv) {
Logger::Get().Initialize();