feat: add RHI abstraction layer unit tests

- Add RHITestFixture with RHI_BACKEND env var support for backend selection
- Add unit tests for: Device, Buffer, Texture, SwapChain, CommandList, CommandQueue, Shader, Fence, Sampler
- Tests can run against D3D12 or OpenGL backends via RHI_BACKEND env var
- Add integration folder placeholder for future integration tests
This commit is contained in:
2026-03-22 16:18:51 +08:00
parent a980f2bd66
commit 8d4447915d
15 changed files with 1260 additions and 0 deletions

View File

@@ -41,3 +41,5 @@ target_include_directories(rhi_engine_tests PRIVATE
enable_testing()
add_test(NAME RHIEngineTests COMMAND rhi_engine_tests)
add_subdirectory(unit)

View File

@@ -0,0 +1,25 @@
# RHI Integration Tests
This folder will contain integration tests for the RHI abstraction layer.
Integration tests verify the interaction between multiple RHI components working together.
## Planned Tests
- Full render pipeline test (device -> swap chain -> command list -> present)
- Multi-threaded command recording
- Resource state transitions across multiple objects
- Frame synchronization with fences
## Running Tests
```bash
# Build once, run against different backends
cmake --build build --config Debug
# Test D3D12 backend
RHI_BACKEND=D3D12 ./build/tests/RHI/unit/rhi_unit_tests.exe
# Test OpenGL backend
RHI_BACKEND=OpenGL ./build/tests/RHI/unit/rhi_unit_tests.exe
```

View File

@@ -0,0 +1,41 @@
cmake_minimum_required(VERSION 3.15)
get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../.. ABSOLUTE)
find_package(GTest REQUIRED)
set(TEST_SOURCES
fixtures/RHITestFixture.cpp
test_device.cpp
test_buffer.cpp
test_texture.cpp
test_swap_chain.cpp
test_command_list.cpp
test_command_queue.cpp
test_shader.cpp
test_fence.cpp
test_sampler.cpp
)
add_executable(rhi_unit_tests ${TEST_SOURCES})
target_link_libraries(rhi_unit_tests PRIVATE
d3d12
dxgi
d3dcompiler
opengl32
glfw3
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(rhi_unit_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/fixtures
${PROJECT_ROOT_DIR}/engine/include
${PROJECT_ROOT_DIR}/engine/src
${CMAKE_SOURCE_DIR}/tests/OpenGL/package/include/
)
enable_testing()
add_test(NAME RHIUnitTests COMMAND rhi_unit_tests)

View File

@@ -0,0 +1,47 @@
#include "RHITestFixture.h"
#include <cstdlib>
#include <iostream>
namespace XCEngine {
namespace RHI {
RHIType RHITestFixture::mBackendType = RHIType::D3D12;
void RHITestFixture::SetUpTestSuite() {
}
void RHITestFixture::TearDownTestSuite() {
}
void RHITestFixture::SetUp() {
const char* backend = std::getenv("RHI_BACKEND");
if (backend != nullptr) {
std::string backendStr(backend);
if (backendStr == "OpenGL" || backendStr == "opengl") {
mBackendType = RHIType::OpenGL;
} else if (backendStr == "D3D12" || backendStr == "d3d12") {
mBackendType = RHIType::D3D12;
}
}
mDevice = RHIFactory::CreateRHIDevice(mBackendType);
ASSERT_NE(mDevice, nullptr);
RHIDeviceDesc desc = {};
desc.enableDebugLayer = true;
desc.appName = L"RHIUnitTest";
bool initResult = mDevice->Initialize(desc);
ASSERT_TRUE(initResult);
}
void RHITestFixture::TearDown() {
if (mDevice != nullptr) {
mDevice->Shutdown();
delete mDevice;
mDevice = nullptr;
}
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,39 @@
#pragma once
#include <gtest/gtest.h>
#include <string>
#include "XCEngine/RHI/RHIFactory.h"
#include "XCEngine/RHI/RHIDevice.h"
#include "XCEngine/RHI/RHICommandQueue.h"
#include "XCEngine/RHI/RHICommandList.h"
#include "XCEngine/RHI/RHIBuffer.h"
#include "XCEngine/RHI/RHITexture.h"
#include "XCEngine/RHI/RHISwapChain.h"
#include "XCEngine/RHI/RHIShader.h"
#include "XCEngine/RHI/RHIFence.h"
#include "XCEngine/RHI/RHISampler.h"
#include "XCEngine/RHI/RHIDescriptorPool.h"
#include "XCEngine/RHI/RHIPipelineLayout.h"
namespace XCEngine {
namespace RHI {
class RHITestFixture : public ::testing::Test {
protected:
void SetUp() override;
void TearDown() override;
static void SetUpTestSuite();
static void TearDownTestSuite();
RHIDevice* GetDevice() { return mDevice; }
RHIType GetBackendType() const { return mBackendType; }
private:
static RHIType mBackendType;
RHIDevice* mDevice = nullptr;
};
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,134 @@
#include "fixtures/RHITestFixture.h"
#include "XCEngine/RHI/RHIBuffer.h"
using namespace XCEngine::RHI;
TEST_F(RHITestFixture, Buffer_Map_Unmap) {
BufferDesc desc = {};
desc.size = 256;
desc.stride = sizeof(float);
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
void* data = buffer->Map();
ASSERT_NE(data, nullptr);
memset(data, 0xAB, 256);
buffer->Unmap();
buffer->Shutdown();
delete buffer;
}
TEST_F(RHITestFixture, Buffer_SetData) {
BufferDesc desc = {};
desc.size = 128;
desc.stride = sizeof(float);
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
float testData[16] = {};
buffer->SetData(testData, sizeof(testData));
buffer->Shutdown();
delete buffer;
}
TEST_F(RHITestFixture, Buffer_GetSize) {
BufferDesc desc = {};
desc.size = 1024;
desc.stride = sizeof(float);
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
EXPECT_EQ(buffer->GetSize(), 1024u);
buffer->Shutdown();
delete buffer;
}
TEST_F(RHITestFixture, Buffer_GetStride) {
BufferDesc desc = {};
desc.size = 256;
desc.stride = 16;
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
EXPECT_EQ(buffer->GetStride(), 16u);
buffer->Shutdown();
delete buffer;
}
TEST_F(RHITestFixture, Buffer_SetBufferType) {
BufferDesc desc = {};
desc.size = 256;
desc.stride = sizeof(float);
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
EXPECT_EQ(buffer->GetBufferType(), BufferType::Vertex);
buffer->SetBufferType(BufferType::Index);
EXPECT_EQ(buffer->GetBufferType(), BufferType::Index);
buffer->Shutdown();
delete buffer;
}
TEST_F(RHITestFixture, Buffer_StateManagement) {
BufferDesc desc = {};
desc.size = 256;
desc.stride = sizeof(float);
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
EXPECT_EQ(buffer->GetState(), ResourceStates::Common);
buffer->SetState(ResourceStates::VertexAndConstantBuffer);
EXPECT_EQ(buffer->GetState(), ResourceStates::VertexAndConstantBuffer);
buffer->Shutdown();
delete buffer;
}
TEST_F(RHITestFixture, Buffer_Naming) {
BufferDesc desc = {};
desc.size = 256;
desc.stride = sizeof(float);
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
buffer->SetName("TestBuffer");
EXPECT_EQ(buffer->GetName(), "TestBuffer");
buffer->Shutdown();
delete buffer;
}
TEST_F(RHITestFixture, Buffer_GetNativeHandle) {
BufferDesc desc = {};
desc.size = 256;
desc.stride = sizeof(float);
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
EXPECT_NE(buffer->GetNativeHandle(), nullptr);
buffer->Shutdown();
delete buffer;
}

View File

@@ -0,0 +1,228 @@
#include "fixtures/RHITestFixture.h"
#include "XCEngine/RHI/RHICommandList.h"
#include "XCEngine/RHI/RHITexture.h"
using namespace XCEngine::RHI;
TEST_F(RHITestFixture, CommandList_Reset_Close) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
cmdList->Reset();
cmdList->Close();
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, CommandList_SetPrimitiveTopology) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
cmdList->Reset();
cmdList->SetPrimitiveTopology(PrimitiveTopology::TriangleList);
cmdList->Close();
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, CommandList_SetViewport) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
Viewport vp = { 0.0f, 0.0f, 800.0f, 600.0f, 0.0f, 1.0f };
cmdList->Reset();
cmdList->SetViewport(vp);
cmdList->Close();
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, CommandList_SetViewports) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
Viewport viewports[2] = {
{ 0.0f, 0.0f, 400.0f, 300.0f, 0.0f, 1.0f },
{ 400.0f, 300.0f, 400.0f, 300.0f, 0.0f, 1.0f }
};
cmdList->Reset();
cmdList->SetViewports(2, viewports);
cmdList->Close();
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, CommandList_SetScissorRect) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
Rect rect = { 0, 0, 800, 600 };
cmdList->Reset();
cmdList->SetScissorRect(rect);
cmdList->Close();
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, CommandList_Draw) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
cmdList->Reset();
cmdList->SetPrimitiveTopology(PrimitiveTopology::TriangleList);
cmdList->Draw(3);
cmdList->Close();
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, CommandList_DrawIndexed) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
cmdList->Reset();
cmdList->SetPrimitiveTopology(PrimitiveTopology::TriangleList);
cmdList->DrawIndexed(6);
cmdList->Close();
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, CommandList_ClearRenderTarget) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
TextureDesc texDesc = {};
texDesc.width = 256;
texDesc.height = 256;
texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
RHITexture* texture = GetDevice()->CreateTexture(texDesc);
ASSERT_NE(texture, nullptr);
float color[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
cmdList->Reset();
cmdList->ClearRenderTarget(texture->GetNativeHandle(), color);
cmdList->Close();
texture->Shutdown();
delete texture;
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, CommandList_SetDepthStencilState) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
DepthStencilState dsState = {};
dsState.depthEnable = true;
dsState.depthWriteMask = true;
dsState.depthFunc = ComparisonFunc::Less;
cmdList->Reset();
cmdList->SetDepthStencilState(dsState);
cmdList->Close();
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, CommandList_SetBlendState) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
BlendState blendState = {};
blendState.alphaToCoverageEnable = false;
blendState.independentBlendEnable = false;
cmdList->Reset();
cmdList->SetBlendState(blendState);
cmdList->Close();
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, CommandList_SetStencilRef) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
cmdList->Reset();
cmdList->SetStencilRef(0);
cmdList->Close();
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, CommandList_TransitionBarrier) {
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
TextureDesc texDesc = {};
texDesc.width = 256;
texDesc.height = 256;
texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
RHITexture* texture = GetDevice()->CreateTexture(texDesc);
ASSERT_NE(texture, nullptr);
cmdList->Reset();
cmdList->TransitionBarrier(texture->GetNativeHandle(), ResourceStates::Common, ResourceStates::RenderTarget);
cmdList->Close();
texture->Shutdown();
delete texture;
cmdList->Shutdown();
delete cmdList;
}

View File

@@ -0,0 +1,109 @@
#include "fixtures/RHITestFixture.h"
#include "XCEngine/RHI/RHICommandQueue.h"
#include "XCEngine/RHI/RHICommandList.h"
#include "XCEngine/RHI/RHIFence.h"
using namespace XCEngine::RHI;
TEST_F(RHITestFixture, CommandQueue_ExecuteCommandLists) {
CommandQueueDesc queueDesc = {};
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
ASSERT_NE(queue, nullptr);
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
cmdList->Reset();
cmdList->Close();
void* cmdLists[] = { cmdList };
queue->ExecuteCommandLists(1, cmdLists);
cmdList->Shutdown();
delete cmdList;
queue->Shutdown();
delete queue;
}
TEST_F(RHITestFixture, CommandQueue_SignalWaitFence) {
CommandQueueDesc queueDesc = {};
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
ASSERT_NE(queue, nullptr);
FenceDesc fenceDesc = {};
fenceDesc.initialValue = 0;
RHIFence* fence = GetDevice()->CreateFence(fenceDesc);
ASSERT_NE(fence, nullptr);
queue->Signal(fence, 1);
fence->Wait(1);
EXPECT_EQ(fence->GetCompletedValue(), 1u);
fence->Shutdown();
delete fence;
queue->Shutdown();
delete queue;
}
TEST_F(RHITestFixture, CommandQueue_GetCompletedValue) {
CommandQueueDesc queueDesc = {};
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
ASSERT_NE(queue, nullptr);
uint64_t completedValue = queue->GetCompletedValue();
EXPECT_GE(completedValue, 0u);
queue->Shutdown();
delete queue;
}
TEST_F(RHITestFixture, CommandQueue_WaitForIdle) {
CommandQueueDesc queueDesc = {};
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
ASSERT_NE(queue, nullptr);
queue->WaitForIdle();
queue->Shutdown();
delete queue;
}
TEST_F(RHITestFixture, CommandQueue_GetType) {
CommandQueueDesc queueDesc = {};
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
ASSERT_NE(queue, nullptr);
EXPECT_EQ(queue->GetType(), CommandQueueType::Direct);
queue->Shutdown();
delete queue;
}
TEST_F(RHITestFixture, CommandQueue_GetTimestampFrequency) {
CommandQueueDesc queueDesc = {};
queueDesc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc);
ASSERT_NE(queue, nullptr);
uint64_t frequency = queue->GetTimestampFrequency();
EXPECT_GT(frequency, 0u);
queue->Shutdown();
delete queue;
}

View File

@@ -0,0 +1,122 @@
#include "fixtures/RHITestFixture.h"
using namespace XCEngine::RHI;
TEST_F(RHITestFixture, Device_Create_Success) {
ASSERT_NE(GetDevice(), nullptr);
}
TEST_F(RHITestFixture, Device_Initialize_Shutdown) {
RHIDevice* device = RHIFactory::CreateRHIDevice(GetBackendType());
ASSERT_NE(device, nullptr);
RHIDeviceDesc desc = {};
desc.enableDebugLayer = true;
bool initResult = device->Initialize(desc);
ASSERT_TRUE(initResult);
device->Shutdown();
delete device;
}
TEST_F(RHITestFixture, Device_GetCapabilities_ReturnsValid) {
const auto& caps = GetDevice()->GetCapabilities();
EXPECT_GE(caps.maxTexture2DSize, 0u);
EXPECT_GE(caps.maxRenderTargets, 1u);
EXPECT_GE(caps.maxViewports, 1u);
EXPECT_GE(caps.maxVertexAttribs, 8u);
}
TEST_F(RHITestFixture, Device_GetDeviceInfo_ReturnsValid) {
const auto& info = GetDevice()->GetDeviceInfo();
EXPECT_FALSE(info.vendor.empty());
EXPECT_FALSE(info.renderer.empty());
}
TEST_F(RHITestFixture, Device_CreateBuffer_ReturnsValid) {
BufferDesc desc = {};
desc.size = 1024;
desc.stride = sizeof(float);
desc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
RHIBuffer* buffer = GetDevice()->CreateBuffer(desc);
ASSERT_NE(buffer, nullptr);
EXPECT_EQ(buffer->GetSize(), 1024u);
buffer->Shutdown();
delete buffer;
}
TEST_F(RHITestFixture, Device_CreateTexture_ReturnsValid) {
TextureDesc desc = {};
desc.width = 512;
desc.height = 512;
desc.mipLevels = 1;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
desc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
RHITexture* texture = GetDevice()->CreateTexture(desc);
ASSERT_NE(texture, nullptr);
EXPECT_EQ(texture->GetWidth(), 512u);
EXPECT_EQ(texture->GetHeight(), 512u);
texture->Shutdown();
delete texture;
}
TEST_F(RHITestFixture, Device_CreateFence_ReturnsValid) {
FenceDesc desc = {};
desc.initialValue = 0;
desc.flags = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
EXPECT_EQ(fence->GetCompletedValue(), 0u);
fence->Shutdown();
delete fence;
}
TEST_F(RHITestFixture, Device_CreateCommandQueue_ReturnsValid) {
CommandQueueDesc desc = {};
desc.queueType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandQueue* queue = GetDevice()->CreateCommandQueue(desc);
ASSERT_NE(queue, nullptr);
EXPECT_EQ(queue->GetType(), CommandQueueType::Direct);
queue->Shutdown();
delete queue;
}
TEST_F(RHITestFixture, Device_CreateCommandList_ReturnsValid) {
CommandListDesc desc = {};
desc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(desc);
ASSERT_NE(cmdList, nullptr);
cmdList->Shutdown();
delete cmdList;
}
TEST_F(RHITestFixture, Device_CreateSampler_ReturnsValid) {
SamplerDesc desc = {};
desc.filter = static_cast<uint32_t>(FilterMode::Linear);
desc.addressU = static_cast<uint32_t>(TextureAddressMode::Wrap);
desc.addressV = static_cast<uint32_t>(TextureAddressMode::Wrap);
desc.addressW = static_cast<uint32_t>(TextureAddressMode::Wrap);
RHISampler* sampler = GetDevice()->CreateSampler(desc);
ASSERT_NE(sampler, nullptr);
sampler->Shutdown();
delete sampler;
}
TEST_F(RHITestFixture, Device_GetNativeDevice_ReturnsValid) {
void* native = GetDevice()->GetNativeDevice();
ASSERT_NE(native, nullptr);
}

View File

@@ -0,0 +1,99 @@
#include "fixtures/RHITestFixture.h"
#include "XCEngine/RHI/RHIFence.h"
using namespace XCEngine::RHI;
TEST_F(RHITestFixture, Fence_Create) {
FenceDesc desc = {};
desc.initialValue = 0;
desc.flags = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
fence->Shutdown();
delete fence;
}
TEST_F(RHITestFixture, Fence_Signal) {
FenceDesc desc = {};
desc.initialValue = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
fence->Signal();
fence->Shutdown();
delete fence;
}
TEST_F(RHITestFixture, Fence_SignalWithValue) {
FenceDesc desc = {};
desc.initialValue = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
fence->Signal(5);
fence->Shutdown();
delete fence;
}
TEST_F(RHITestFixture, Fence_Wait) {
FenceDesc desc = {};
desc.initialValue = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
fence->Signal(1);
fence->Wait(1);
fence->Shutdown();
delete fence;
}
TEST_F(RHITestFixture, Fence_GetCompletedValue) {
FenceDesc desc = {};
desc.initialValue = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
EXPECT_EQ(fence->GetCompletedValue(), 0u);
fence->Signal(1);
EXPECT_EQ(fence->GetCompletedValue(), 1u);
fence->Shutdown();
delete fence;
}
TEST_F(RHITestFixture, Fence_IsSignaled) {
FenceDesc desc = {};
desc.initialValue = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
EXPECT_FALSE(fence->IsSignaled());
fence->Signal(1);
EXPECT_TRUE(fence->IsSignaled());
fence->Shutdown();
delete fence;
}
TEST_F(RHITestFixture, Fence_GetNativeHandle) {
FenceDesc desc = {};
desc.initialValue = 0;
RHIFence* fence = GetDevice()->CreateFence(desc);
ASSERT_NE(fence, nullptr);
EXPECT_NE(fence->GetNativeHandle(), nullptr);
fence->Shutdown();
delete fence;
}

View File

@@ -0,0 +1,77 @@
#include "fixtures/RHITestFixture.h"
#include "XCEngine/RHI/RHISampler.h"
using namespace XCEngine::RHI;
TEST_F(RHITestFixture, Sampler_Create) {
SamplerDesc desc = {};
desc.filter = static_cast<uint32_t>(FilterMode::Linear);
desc.addressU = static_cast<uint32_t>(TextureAddressMode::Wrap);
desc.addressV = static_cast<uint32_t>(TextureAddressMode::Wrap);
desc.addressW = static_cast<uint32_t>(TextureAddressMode::Wrap);
desc.mipLodBias = 0.0f;
desc.maxAnisotropy = 16;
desc.comparisonFunc = static_cast<uint32_t>(ComparisonFunc::Less);
desc.borderColorR = 0.0f;
desc.borderColorG = 0.0f;
desc.borderColorB = 0.0f;
desc.borderColorA = 0.0f;
desc.minLod = 0.0f;
desc.maxLod = 100.0f;
RHISampler* sampler = GetDevice()->CreateSampler(desc);
ASSERT_NE(sampler, nullptr);
sampler->Shutdown();
delete sampler;
}
TEST_F(RHITestFixture, Sampler_Bind_Unbind) {
SamplerDesc desc = {};
desc.filter = static_cast<uint32_t>(FilterMode::Linear);
desc.addressU = static_cast<uint32_t>(TextureAddressMode::Wrap);
desc.addressV = static_cast<uint32_t>(TextureAddressMode::Wrap);
desc.addressW = static_cast<uint32_t>(TextureAddressMode::Wrap);
RHISampler* sampler = GetDevice()->CreateSampler(desc);
ASSERT_NE(sampler, nullptr);
sampler->Bind(0);
sampler->Unbind(0);
sampler->Shutdown();
delete sampler;
}
TEST_F(RHITestFixture, Sampler_GetID) {
SamplerDesc desc = {};
desc.filter = static_cast<uint32_t>(FilterMode::Linear);
desc.addressU = static_cast<uint32_t>(TextureAddressMode::Wrap);
desc.addressV = static_cast<uint32_t>(TextureAddressMode::Wrap);
desc.addressW = static_cast<uint32_t>(TextureAddressMode::Wrap);
RHISampler* sampler = GetDevice()->CreateSampler(desc);
ASSERT_NE(sampler, nullptr);
unsigned int id = sampler->GetID();
EXPECT_GE(id, 0u);
sampler->Shutdown();
delete sampler;
}
TEST_F(RHITestFixture, Sampler_GetNativeHandle) {
SamplerDesc desc = {};
desc.filter = static_cast<uint32_t>(FilterMode::Linear);
desc.addressU = static_cast<uint32_t>(TextureAddressMode::Wrap);
desc.addressV = static_cast<uint32_t>(TextureAddressMode::Wrap);
desc.addressW = static_cast<uint32_t>(TextureAddressMode::Wrap);
RHISampler* sampler = GetDevice()->CreateSampler(desc);
ASSERT_NE(sampler, nullptr);
EXPECT_NE(sampler->GetNativeHandle(), nullptr);
sampler->Shutdown();
delete sampler;
}

View File

@@ -0,0 +1,123 @@
#include "fixtures/RHITestFixture.h"
#include "XCEngine/RHI/RHIShader.h"
using namespace XCEngine::RHI;
TEST_F(RHITestFixture, Shader_Compile_FromSource) {
const char* vertexShader = R"(
cbuffer ConstantBuffer : register(b0)
{
float4x4 worldViewProj;
}
struct VS_INPUT {
float3 pos : POSITION;
};
struct VS_OUTPUT {
float4 pos : SV_POSITION;
};
VS_OUTPUT main(VS_INPUT input) {
VS_OUTPUT output;
output.pos = float4(input.pos, 1.0f);
return output;
}
)";
RHIShader* shader = GetDevice()->CompileShader({});
ASSERT_NE(shader, nullptr);
shader->Shutdown();
delete shader;
}
TEST_F(RHITestFixture, Shader_GetType) {
RHIShader* shader = GetDevice()->CompileShader({});
ASSERT_NE(shader, nullptr);
ShaderType type = shader->GetType();
EXPECT_GE(static_cast<uint8_t>(type), static_cast<uint8_t>(ShaderType::Vertex));
shader->Shutdown();
delete shader;
}
TEST_F(RHITestFixture, Shader_IsValid) {
RHIShader* shader = GetDevice()->CompileShader({});
ASSERT_NE(shader, nullptr);
EXPECT_TRUE(shader->IsValid());
shader->Shutdown();
delete shader;
}
TEST_F(RHITestFixture, Shader_Bind_Unbind) {
RHIShader* shader = GetDevice()->CompileShader({});
ASSERT_NE(shader, nullptr);
shader->Bind();
shader->Unbind();
shader->Shutdown();
delete shader;
}
TEST_F(RHITestFixture, Shader_SetInt) {
RHIShader* shader = GetDevice()->CompileShader({});
ASSERT_NE(shader, nullptr);
shader->SetInt("testInt", 42);
shader->Shutdown();
delete shader;
}
TEST_F(RHITestFixture, Shader_SetFloat) {
RHIShader* shader = GetDevice()->CompileShader({});
ASSERT_NE(shader, nullptr);
shader->SetFloat("testFloat", 3.14f);
shader->Shutdown();
delete shader;
}
TEST_F(RHITestFixture, Shader_SetVec3) {
RHIShader* shader = GetDevice()->CompileShader({});
ASSERT_NE(shader, nullptr);
shader->SetVec3("testVec3", 1.0f, 2.0f, 3.0f);
shader->Shutdown();
delete shader;
}
TEST_F(RHITestFixture, Shader_SetVec4) {
RHIShader* shader = GetDevice()->CompileShader({});
ASSERT_NE(shader, nullptr);
shader->SetVec4("testVec4", 1.0f, 2.0f, 3.0f, 4.0f);
shader->Shutdown();
delete shader;
}
TEST_F(RHITestFixture, Shader_SetMat4) {
RHIShader* shader = GetDevice()->CompileShader({});
ASSERT_NE(shader, nullptr);
float mat[16] = {};
shader->SetMat4("testMat4", mat);
shader->Shutdown();
delete shader;
}
TEST_F(RHITestFixture, Shader_GetNativeHandle) {
RHIShader* shader = GetDevice()->CompileShader({});
ASSERT_NE(shader, nullptr);
EXPECT_NE(shader->GetNativeHandle(), nullptr);
shader->Shutdown();
delete shader;
}

View File

@@ -0,0 +1,110 @@
#include "fixtures/RHITestFixture.h"
#include "XCEngine/RHI/RHISwapChain.h"
using namespace XCEngine::RHI;
TEST_F(RHITestFixture, SwapChain_Create) {
SwapChainDesc desc = {};
desc.width = 800;
desc.height = 600;
desc.bufferCount = 2;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
desc.refreshRate = 60;
desc.sampleCount = 1;
desc.sampleQuality = 0;
desc.swapEffect = 0;
desc.flags = 0;
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
ASSERT_NE(swapChain, nullptr);
swapChain->Shutdown();
delete swapChain;
}
TEST_F(RHITestFixture, SwapChain_GetCurrentBackBufferIndex) {
SwapChainDesc desc = {};
desc.width = 800;
desc.height = 600;
desc.bufferCount = 2;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
ASSERT_NE(swapChain, nullptr);
uint32_t index = swapChain->GetCurrentBackBufferIndex();
EXPECT_LT(index, 2u);
swapChain->Shutdown();
delete swapChain;
}
TEST_F(RHITestFixture, SwapChain_GetCurrentBackBuffer) {
SwapChainDesc desc = {};
desc.width = 800;
desc.height = 600;
desc.bufferCount = 2;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
ASSERT_NE(swapChain, nullptr);
RHITexture* backBuffer = swapChain->GetCurrentBackBuffer();
EXPECT_NE(backBuffer, nullptr);
swapChain->Shutdown();
delete swapChain;
}
TEST_F(RHITestFixture, SwapChain_Resize) {
SwapChainDesc desc = {};
desc.width = 800;
desc.height = 600;
desc.bufferCount = 2;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
ASSERT_NE(swapChain, nullptr);
swapChain->Resize(1024, 768);
swapChain->Shutdown();
delete swapChain;
}
TEST_F(RHITestFixture, SwapChain_FullscreenState) {
SwapChainDesc desc = {};
desc.width = 800;
desc.height = 600;
desc.bufferCount = 2;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
ASSERT_NE(swapChain, nullptr);
EXPECT_FALSE(swapChain->IsFullscreen());
swapChain->SetFullscreen(true);
EXPECT_TRUE(swapChain->IsFullscreen());
swapChain->SetFullscreen(false);
swapChain->Shutdown();
delete swapChain;
}
TEST_F(RHITestFixture, SwapChain_ShouldClose) {
SwapChainDesc desc = {};
desc.width = 800;
desc.height = 600;
desc.bufferCount = 2;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
RHISwapChain* swapChain = GetDevice()->CreateSwapChain(desc);
ASSERT_NE(swapChain, nullptr);
EXPECT_FALSE(swapChain->ShouldClose());
swapChain->SetShouldClose(true);
EXPECT_TRUE(swapChain->ShouldClose());
swapChain->Shutdown();
delete swapChain;
}

View File

@@ -0,0 +1,103 @@
#include "fixtures/RHITestFixture.h"
#include "XCEngine/RHI/RHITexture.h"
using namespace XCEngine::RHI;
TEST_F(RHITestFixture, Texture_Create_Texture2D) {
TextureDesc desc = {};
desc.width = 512;
desc.height = 512;
desc.depth = 1;
desc.mipLevels = 1;
desc.arraySize = 1;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
desc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
desc.sampleCount = 1;
desc.sampleQuality = 0;
desc.flags = 0;
RHITexture* texture = GetDevice()->CreateTexture(desc);
ASSERT_NE(texture, nullptr);
EXPECT_EQ(texture->GetWidth(), 512u);
EXPECT_EQ(texture->GetHeight(), 512u);
EXPECT_EQ(texture->GetDepth(), 1u);
EXPECT_EQ(texture->GetMipLevels(), 1u);
EXPECT_EQ(texture->GetFormat(), Format::R8G8B8A8_UNorm);
EXPECT_EQ(texture->GetTextureType(), TextureType::Texture2D);
texture->Shutdown();
delete texture;
}
TEST_F(RHITestFixture, Texture_Create_Texture3D) {
TextureDesc desc = {};
desc.width = 64;
desc.height = 64;
desc.depth = 64;
desc.mipLevels = 1;
desc.format = static_cast<uint32_t>(Format::R16_Float);
desc.textureType = static_cast<uint32_t>(TextureType::Texture3D);
RHITexture* texture = GetDevice()->CreateTexture(desc);
ASSERT_NE(texture, nullptr);
EXPECT_EQ(texture->GetWidth(), 64u);
EXPECT_EQ(texture->GetHeight(), 64u);
EXPECT_EQ(texture->GetDepth(), 64u);
EXPECT_EQ(texture->GetTextureType(), TextureType::Texture3D);
texture->Shutdown();
delete texture;
}
TEST_F(RHITestFixture, Texture_StateManagement) {
TextureDesc desc = {};
desc.width = 256;
desc.height = 256;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
desc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
RHITexture* texture = GetDevice()->CreateTexture(desc);
ASSERT_NE(texture, nullptr);
EXPECT_EQ(texture->GetState(), ResourceStates::Common);
texture->SetState(ResourceStates::RenderTarget);
EXPECT_EQ(texture->GetState(), ResourceStates::RenderTarget);
texture->Shutdown();
delete texture;
}
TEST_F(RHITestFixture, Texture_Naming) {
TextureDesc desc = {};
desc.width = 256;
desc.height = 256;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
desc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
RHITexture* texture = GetDevice()->CreateTexture(desc);
ASSERT_NE(texture, nullptr);
texture->SetName("TestTexture");
EXPECT_EQ(texture->GetName(), "TestTexture");
texture->Shutdown();
delete texture;
}
TEST_F(RHITestFixture, Texture_GetNativeHandle) {
TextureDesc desc = {};
desc.width = 256;
desc.height = 256;
desc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
desc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
RHITexture* texture = GetDevice()->CreateTexture(desc);
ASSERT_NE(texture, nullptr);
EXPECT_NE(texture->GetNativeHandle(), nullptr);
texture->Shutdown();
delete texture;
}