diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7d5c3016..01ee14b9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -43,6 +43,7 @@ add_subdirectory(RHI) add_subdirectory(RHI/D3D12) add_subdirectory(RHI/OpenGL) add_subdirectory(Resources) +add_subdirectory(Input) # ============================================================ # Test Summary diff --git a/tests/RHI/CMakeLists.txt b/tests/RHI/CMakeLists.txt index 658c0623..b8c3e405 100644 --- a/tests/RHI/CMakeLists.txt +++ b/tests/RHI/CMakeLists.txt @@ -41,3 +41,5 @@ target_include_directories(rhi_engine_tests PRIVATE enable_testing() add_test(NAME RHIEngineTests COMMAND rhi_engine_tests) + +add_subdirectory(unit) diff --git a/tests/RHI/integration/README.md b/tests/RHI/integration/README.md new file mode 100644 index 00000000..82e976a9 --- /dev/null +++ b/tests/RHI/integration/README.md @@ -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 +``` diff --git a/tests/RHI/unit/CMakeLists.txt b/tests/RHI/unit/CMakeLists.txt new file mode 100644 index 00000000..1cac788c --- /dev/null +++ b/tests/RHI/unit/CMakeLists.txt @@ -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) diff --git a/tests/RHI/unit/fixtures/RHITestFixture.cpp b/tests/RHI/unit/fixtures/RHITestFixture.cpp new file mode 100644 index 00000000..0f5acb4a --- /dev/null +++ b/tests/RHI/unit/fixtures/RHITestFixture.cpp @@ -0,0 +1,47 @@ +#include "RHITestFixture.h" +#include +#include + +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 diff --git a/tests/RHI/unit/fixtures/RHITestFixture.h b/tests/RHI/unit/fixtures/RHITestFixture.h new file mode 100644 index 00000000..0a708ff3 --- /dev/null +++ b/tests/RHI/unit/fixtures/RHITestFixture.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#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 diff --git a/tests/RHI/unit/test_buffer.cpp b/tests/RHI/unit/test_buffer.cpp new file mode 100644 index 00000000..f162511b --- /dev/null +++ b/tests/RHI/unit/test_buffer.cpp @@ -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(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(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(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(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(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(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(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(BufferType::Vertex); + + RHIBuffer* buffer = GetDevice()->CreateBuffer(desc); + ASSERT_NE(buffer, nullptr); + + EXPECT_NE(buffer->GetNativeHandle(), nullptr); + + buffer->Shutdown(); + delete buffer; +} diff --git a/tests/RHI/unit/test_command_list.cpp b/tests/RHI/unit/test_command_list.cpp new file mode 100644 index 00000000..9ad50e2f --- /dev/null +++ b/tests/RHI/unit/test_command_list.cpp @@ -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(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(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(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(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(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(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(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(CommandQueueType::Direct); + + RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc); + ASSERT_NE(cmdList, nullptr); + + TextureDesc texDesc = {}; + texDesc.width = 256; + texDesc.height = 256; + texDesc.format = static_cast(Format::R8G8B8A8_UNorm); + texDesc.textureType = static_cast(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(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(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(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(CommandQueueType::Direct); + + RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc); + ASSERT_NE(cmdList, nullptr); + + TextureDesc texDesc = {}; + texDesc.width = 256; + texDesc.height = 256; + texDesc.format = static_cast(Format::R8G8B8A8_UNorm); + texDesc.textureType = static_cast(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; +} diff --git a/tests/RHI/unit/test_command_queue.cpp b/tests/RHI/unit/test_command_queue.cpp new file mode 100644 index 00000000..d75f1c64 --- /dev/null +++ b/tests/RHI/unit/test_command_queue.cpp @@ -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(CommandQueueType::Direct); + + RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc); + ASSERT_NE(queue, nullptr); + + CommandListDesc cmdDesc = {}; + cmdDesc.commandListType = static_cast(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(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(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(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(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(CommandQueueType::Direct); + + RHICommandQueue* queue = GetDevice()->CreateCommandQueue(queueDesc); + ASSERT_NE(queue, nullptr); + + uint64_t frequency = queue->GetTimestampFrequency(); + EXPECT_GT(frequency, 0u); + + queue->Shutdown(); + delete queue; +} diff --git a/tests/RHI/unit/test_device.cpp b/tests/RHI/unit/test_device.cpp new file mode 100644 index 00000000..da73b6a3 --- /dev/null +++ b/tests/RHI/unit/test_device.cpp @@ -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(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(Format::R8G8B8A8_UNorm); + desc.textureType = static_cast(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(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(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(FilterMode::Linear); + desc.addressU = static_cast(TextureAddressMode::Wrap); + desc.addressV = static_cast(TextureAddressMode::Wrap); + desc.addressW = static_cast(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); +} diff --git a/tests/RHI/unit/test_fence.cpp b/tests/RHI/unit/test_fence.cpp new file mode 100644 index 00000000..54d02f3a --- /dev/null +++ b/tests/RHI/unit/test_fence.cpp @@ -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; +} diff --git a/tests/RHI/unit/test_sampler.cpp b/tests/RHI/unit/test_sampler.cpp new file mode 100644 index 00000000..fac5fd1a --- /dev/null +++ b/tests/RHI/unit/test_sampler.cpp @@ -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(FilterMode::Linear); + desc.addressU = static_cast(TextureAddressMode::Wrap); + desc.addressV = static_cast(TextureAddressMode::Wrap); + desc.addressW = static_cast(TextureAddressMode::Wrap); + desc.mipLodBias = 0.0f; + desc.maxAnisotropy = 16; + desc.comparisonFunc = static_cast(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(FilterMode::Linear); + desc.addressU = static_cast(TextureAddressMode::Wrap); + desc.addressV = static_cast(TextureAddressMode::Wrap); + desc.addressW = static_cast(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(FilterMode::Linear); + desc.addressU = static_cast(TextureAddressMode::Wrap); + desc.addressV = static_cast(TextureAddressMode::Wrap); + desc.addressW = static_cast(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(FilterMode::Linear); + desc.addressU = static_cast(TextureAddressMode::Wrap); + desc.addressV = static_cast(TextureAddressMode::Wrap); + desc.addressW = static_cast(TextureAddressMode::Wrap); + + RHISampler* sampler = GetDevice()->CreateSampler(desc); + ASSERT_NE(sampler, nullptr); + + EXPECT_NE(sampler->GetNativeHandle(), nullptr); + + sampler->Shutdown(); + delete sampler; +} diff --git a/tests/RHI/unit/test_shader.cpp b/tests/RHI/unit/test_shader.cpp new file mode 100644 index 00000000..40cb6d8a --- /dev/null +++ b/tests/RHI/unit/test_shader.cpp @@ -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(type), static_cast(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; +} diff --git a/tests/RHI/unit/test_swap_chain.cpp b/tests/RHI/unit/test_swap_chain.cpp new file mode 100644 index 00000000..06d70c6d --- /dev/null +++ b/tests/RHI/unit/test_swap_chain.cpp @@ -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(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(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(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(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(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(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; +} diff --git a/tests/RHI/unit/test_texture.cpp b/tests/RHI/unit/test_texture.cpp new file mode 100644 index 00000000..03ef1966 --- /dev/null +++ b/tests/RHI/unit/test_texture.cpp @@ -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(Format::R8G8B8A8_UNorm); + desc.textureType = static_cast(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(Format::R16_Float); + desc.textureType = static_cast(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(Format::R8G8B8A8_UNorm); + desc.textureType = static_cast(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(Format::R8G8B8A8_UNorm); + desc.textureType = static_cast(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(Format::R8G8B8A8_UNorm); + desc.textureType = static_cast(TextureType::Texture2D); + + RHITexture* texture = GetDevice()->CreateTexture(desc); + ASSERT_NE(texture, nullptr); + + EXPECT_NE(texture->GetNativeHandle(), nullptr); + + texture->Shutdown(); + delete texture; +}