Add backpack RHI integration test
This commit is contained in:
@@ -138,6 +138,7 @@ inline DXGI_FORMAT ToD3D12(Format format) {
|
|||||||
case Format::R8_UNorm: return DXGI_FORMAT_R8_UNORM;
|
case Format::R8_UNorm: return DXGI_FORMAT_R8_UNORM;
|
||||||
case Format::R8G8_UNorm: return DXGI_FORMAT_R8G8_UNORM;
|
case Format::R8G8_UNorm: return DXGI_FORMAT_R8G8_UNORM;
|
||||||
case Format::R8G8B8A8_UNorm: return DXGI_FORMAT_R8G8B8A8_UNORM;
|
case Format::R8G8B8A8_UNorm: return DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
case Format::R16_UInt: return DXGI_FORMAT_R16_UINT;
|
||||||
case Format::R16G16B16A16_Float: return DXGI_FORMAT_R16G16B16A16_FLOAT;
|
case Format::R16G16B16A16_Float: return DXGI_FORMAT_R16G16B16A16_FLOAT;
|
||||||
case Format::R32G32B32A32_Float: return DXGI_FORMAT_R32G32B32A32_FLOAT;
|
case Format::R32G32B32A32_Float: return DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||||
case Format::R16_Float: return DXGI_FORMAT_R16_FLOAT;
|
case Format::R16_Float: return DXGI_FORMAT_R16_FLOAT;
|
||||||
@@ -165,6 +166,7 @@ inline Format FromD3D12(DXGI_FORMAT format) {
|
|||||||
case DXGI_FORMAT_R8_UNORM: return Format::R8_UNorm;
|
case DXGI_FORMAT_R8_UNORM: return Format::R8_UNorm;
|
||||||
case DXGI_FORMAT_R8G8_UNORM: return Format::R8G8_UNorm;
|
case DXGI_FORMAT_R8G8_UNORM: return Format::R8G8_UNorm;
|
||||||
case DXGI_FORMAT_R8G8B8A8_UNORM: return Format::R8G8B8A8_UNorm;
|
case DXGI_FORMAT_R8G8B8A8_UNORM: return Format::R8G8B8A8_UNorm;
|
||||||
|
case DXGI_FORMAT_R16_UINT: return Format::R16_UInt;
|
||||||
case DXGI_FORMAT_R16G16B16A16_FLOAT: return Format::R16G16B16A16_Float;
|
case DXGI_FORMAT_R16G16B16A16_FLOAT: return Format::R16G16B16A16_Float;
|
||||||
case DXGI_FORMAT_R32G32B32A32_FLOAT: return Format::R32G32B32A32_Float;
|
case DXGI_FORMAT_R32G32B32A32_FLOAT: return Format::R32G32B32A32_Float;
|
||||||
case DXGI_FORMAT_R16_FLOAT: return Format::R16_Float;
|
case DXGI_FORMAT_R16_FLOAT: return Format::R16_Float;
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ private:
|
|||||||
unsigned int m_currentVAO;
|
unsigned int m_currentVAO;
|
||||||
unsigned int m_currentProgram;
|
unsigned int m_currentProgram;
|
||||||
unsigned int m_internalVAO;
|
unsigned int m_internalVAO;
|
||||||
|
unsigned int m_currentIndexType;
|
||||||
|
uint64_t m_currentIndexOffset;
|
||||||
OpenGLPipelineState* m_currentPipelineState;
|
OpenGLPipelineState* m_currentPipelineState;
|
||||||
std::vector<unsigned int> m_enabledVertexAttributes;
|
std::vector<unsigned int> m_enabledVertexAttributes;
|
||||||
OpenGLShader* m_currentShader;
|
OpenGLShader* m_currentShader;
|
||||||
|
|||||||
@@ -299,6 +299,7 @@ enum class Format : uint32_t {
|
|||||||
R8_UNorm,
|
R8_UNorm,
|
||||||
R8G8_UNorm,
|
R8G8_UNorm,
|
||||||
R8G8B8A8_UNorm,
|
R8G8B8A8_UNorm,
|
||||||
|
R16_UInt,
|
||||||
R16G16B16A16_Float,
|
R16G16B16A16_Float,
|
||||||
R32G32B32A32_Float,
|
R32G32B32A32_Float,
|
||||||
R16_Float,
|
R16_Float,
|
||||||
|
|||||||
@@ -58,11 +58,35 @@ bool GetOpenGLVertexAttribFormat(Format format, OpenGLVertexAttribFormat& attrib
|
|||||||
case Format::R32G32B32A32_UInt:
|
case Format::R32G32B32A32_UInt:
|
||||||
attributeFormat = { 4, GL_UNSIGNED_INT, GL_FALSE, true };
|
attributeFormat = { 4, GL_UNSIGNED_INT, GL_FALSE, true };
|
||||||
return true;
|
return true;
|
||||||
|
case Format::R16_UInt:
|
||||||
|
attributeFormat = { 1, GL_UNSIGNED_SHORT, GL_FALSE, true };
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLenum ResolveOpenGLIndexType(Format format) {
|
||||||
|
switch (format) {
|
||||||
|
case Format::R16_UInt:
|
||||||
|
return GL_UNSIGNED_SHORT;
|
||||||
|
case Format::R32_UInt:
|
||||||
|
case Format::Unknown:
|
||||||
|
default:
|
||||||
|
return GL_UNSIGNED_INT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetIndexTypeSize(GLenum indexType) {
|
||||||
|
switch (indexType) {
|
||||||
|
case GL_UNSIGNED_SHORT:
|
||||||
|
return sizeof(GLushort);
|
||||||
|
case GL_UNSIGNED_INT:
|
||||||
|
default:
|
||||||
|
return sizeof(GLuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
OpenGLCommandList::OpenGLCommandList()
|
OpenGLCommandList::OpenGLCommandList()
|
||||||
@@ -70,6 +94,8 @@ OpenGLCommandList::OpenGLCommandList()
|
|||||||
, m_currentVAO(0)
|
, m_currentVAO(0)
|
||||||
, m_currentProgram(0)
|
, m_currentProgram(0)
|
||||||
, m_internalVAO(0)
|
, m_internalVAO(0)
|
||||||
|
, m_currentIndexType(GL_UNSIGNED_INT)
|
||||||
|
, m_currentIndexOffset(0)
|
||||||
, m_currentPipelineState(nullptr)
|
, m_currentPipelineState(nullptr)
|
||||||
, m_currentShader(nullptr)
|
, m_currentShader(nullptr)
|
||||||
, m_composedFramebuffer(nullptr) {
|
, m_composedFramebuffer(nullptr) {
|
||||||
@@ -120,12 +146,13 @@ void OpenGLCommandList::SetVertexBuffers(unsigned int startSlot, unsigned int co
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::SetIndexBuffer(unsigned int buffer, unsigned int type) {
|
void OpenGLCommandList::SetIndexBuffer(unsigned int buffer, unsigned int type) {
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
|
SetIndexBuffer(buffer, type, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::SetIndexBuffer(unsigned int buffer, unsigned int type, size_t offset) {
|
void OpenGLCommandList::SetIndexBuffer(unsigned int buffer, unsigned int type, size_t offset) {
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
|
||||||
(void)offset;
|
m_currentIndexType = type;
|
||||||
|
m_currentIndexOffset = static_cast<uint64_t>(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::BindVertexArray(unsigned int vao) {
|
void OpenGLCommandList::BindVertexArray(unsigned int vao) {
|
||||||
@@ -137,7 +164,8 @@ void OpenGLCommandList::BindVertexArray(unsigned int vao, unsigned int indexBuff
|
|||||||
m_currentVAO = vao;
|
m_currentVAO = vao;
|
||||||
glBindVertexArray(vao);
|
glBindVertexArray(vao);
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||||
(void)indexType;
|
m_currentIndexType = indexType;
|
||||||
|
m_currentIndexOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::UseShader(unsigned int program) {
|
void OpenGLCommandList::UseShader(unsigned int program) {
|
||||||
@@ -261,13 +289,22 @@ void OpenGLCommandList::DrawInstanced(PrimitiveType type, unsigned int vertexCou
|
|||||||
|
|
||||||
void OpenGLCommandList::DrawIndexed(PrimitiveType type, unsigned int indexCount, unsigned int startIndex, int baseVertex) {
|
void OpenGLCommandList::DrawIndexed(PrimitiveType type, unsigned int indexCount, unsigned int startIndex, int baseVertex) {
|
||||||
m_primitiveType = ToOpenGL(type);
|
m_primitiveType = ToOpenGL(type);
|
||||||
glDrawElements(m_primitiveType, indexCount, GL_UNSIGNED_INT, (void*)(startIndex * sizeof(unsigned int)));
|
const uint64_t indexOffset = m_currentIndexOffset + static_cast<uint64_t>(startIndex) * GetIndexTypeSize(m_currentIndexType);
|
||||||
|
glDrawElements(m_primitiveType,
|
||||||
|
indexCount,
|
||||||
|
m_currentIndexType,
|
||||||
|
reinterpret_cast<const void*>(static_cast<uintptr_t>(indexOffset)));
|
||||||
(void)baseVertex;
|
(void)baseVertex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::DrawIndexedInstanced(PrimitiveType type, unsigned int indexCount, unsigned int instanceCount, unsigned int startIndex, int baseVertex, unsigned int startInstance) {
|
void OpenGLCommandList::DrawIndexedInstanced(PrimitiveType type, unsigned int indexCount, unsigned int instanceCount, unsigned int startIndex, int baseVertex, unsigned int startInstance) {
|
||||||
m_primitiveType = ToOpenGL(type);
|
m_primitiveType = ToOpenGL(type);
|
||||||
glDrawElementsInstanced(m_primitiveType, indexCount, GL_UNSIGNED_INT, (void*)(startIndex * sizeof(unsigned int)), instanceCount);
|
const uint64_t indexOffset = m_currentIndexOffset + static_cast<uint64_t>(startIndex) * GetIndexTypeSize(m_currentIndexType);
|
||||||
|
glDrawElementsInstanced(m_primitiveType,
|
||||||
|
indexCount,
|
||||||
|
m_currentIndexType,
|
||||||
|
reinterpret_cast<const void*>(static_cast<uintptr_t>(indexOffset)),
|
||||||
|
instanceCount);
|
||||||
(void)baseVertex;
|
(void)baseVertex;
|
||||||
(void)startInstance;
|
(void)startInstance;
|
||||||
}
|
}
|
||||||
@@ -284,7 +321,7 @@ void OpenGLCommandList::DrawIndirect(PrimitiveType type, unsigned int buffer, si
|
|||||||
void OpenGLCommandList::DrawIndexedIndirect(PrimitiveType type, unsigned int buffer, size_t offset, unsigned int drawCount, unsigned int stride) {
|
void OpenGLCommandList::DrawIndexedIndirect(PrimitiveType type, unsigned int buffer, size_t offset, unsigned int drawCount, unsigned int stride) {
|
||||||
m_primitiveType = ToOpenGL(type);
|
m_primitiveType = ToOpenGL(type);
|
||||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer);
|
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer);
|
||||||
glDrawElementsIndirect(m_primitiveType, GL_UNSIGNED_INT, (void*)offset);
|
glDrawElementsIndirect(m_primitiveType, m_currentIndexType, (void*)offset);
|
||||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
|
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
|
||||||
(void)drawCount;
|
(void)drawCount;
|
||||||
(void)stride;
|
(void)stride;
|
||||||
@@ -477,6 +514,8 @@ void OpenGLCommandList::Shutdown() {
|
|||||||
void OpenGLCommandList::Reset() {
|
void OpenGLCommandList::Reset() {
|
||||||
ReleaseComposedFramebuffer();
|
ReleaseComposedFramebuffer();
|
||||||
m_currentPipelineState = nullptr;
|
m_currentPipelineState = nullptr;
|
||||||
|
m_currentIndexOffset = 0;
|
||||||
|
m_currentIndexType = GL_UNSIGNED_INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::Close() {
|
void OpenGLCommandList::Close() {
|
||||||
@@ -827,7 +866,8 @@ void OpenGLCommandList::SetIndexBuffer(RHIResourceView* buffer, uint64_t offset)
|
|||||||
|
|
||||||
GLuint glBuffer = view->GetBuffer();
|
GLuint glBuffer = view->GetBuffer();
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glBuffer);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glBuffer);
|
||||||
(void)offset;
|
m_currentIndexType = ResolveOpenGLIndexType(view->GetFormat());
|
||||||
|
m_currentIndexOffset = view->GetBufferOffset() + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::CopyResource(RHIResourceView* dst, RHIResourceView* src) {
|
void OpenGLCommandList::CopyResource(RHIResourceView* dst, RHIResourceView* src) {
|
||||||
@@ -853,12 +893,22 @@ void OpenGLCommandList::CopyResource(RHIResourceView* dst, RHIResourceView* src)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::Draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertex, uint32_t startInstance) {
|
void OpenGLCommandList::Draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertex, uint32_t startInstance) {
|
||||||
glDrawArraysInstanced(GL_TRIANGLES, static_cast<GLint>(startVertex), static_cast<GLsizei>(vertexCount), static_cast<GLsizei>(instanceCount));
|
glDrawArraysInstanced(m_primitiveType,
|
||||||
|
static_cast<GLint>(startVertex),
|
||||||
|
static_cast<GLsizei>(vertexCount),
|
||||||
|
static_cast<GLsizei>(instanceCount));
|
||||||
|
(void)startInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::DrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t startIndex, int32_t baseVertex, uint32_t startInstance) {
|
void OpenGLCommandList::DrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t startIndex, int32_t baseVertex, uint32_t startInstance) {
|
||||||
glDrawElementsInstanced(GL_TRIANGLES, static_cast<GLsizei>(indexCount), GL_UNSIGNED_INT,
|
const uint64_t indexOffset = m_currentIndexOffset + static_cast<uint64_t>(startIndex) * GetIndexTypeSize(m_currentIndexType);
|
||||||
reinterpret_cast<const void*>(startIndex * sizeof(GLuint)), static_cast<GLsizei>(instanceCount));
|
glDrawElementsInstanced(m_primitiveType,
|
||||||
|
static_cast<GLsizei>(indexCount),
|
||||||
|
m_currentIndexType,
|
||||||
|
reinterpret_cast<const void*>(static_cast<uintptr_t>(indexOffset)),
|
||||||
|
static_cast<GLsizei>(instanceCount));
|
||||||
|
(void)baseVertex;
|
||||||
|
(void)startInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::SetPrimitiveTopology(PrimitiveTopology topology) {
|
void OpenGLCommandList::SetPrimitiveTopology(PrimitiveTopology topology) {
|
||||||
|
|||||||
@@ -8,3 +8,4 @@ add_subdirectory(minimal)
|
|||||||
add_subdirectory(triangle)
|
add_subdirectory(triangle)
|
||||||
add_subdirectory(quad)
|
add_subdirectory(quad)
|
||||||
add_subdirectory(sphere)
|
add_subdirectory(sphere)
|
||||||
|
add_subdirectory(backpack)
|
||||||
|
|||||||
62
tests/RHI/integration/backpack/CMakeLists.txt
Normal file
62
tests/RHI/integration/backpack/CMakeLists.txt
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
project(rhi_integration_backpack)
|
||||||
|
|
||||||
|
set(ENGINE_ROOT_DIR ${CMAKE_SOURCE_DIR}/engine)
|
||||||
|
set(PACKAGE_DIR ${CMAKE_SOURCE_DIR}/tests/opengl/package)
|
||||||
|
|
||||||
|
get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../.. ABSOLUTE)
|
||||||
|
|
||||||
|
add_executable(rhi_integration_backpack
|
||||||
|
main.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../fixtures/RHIIntegrationFixture.cpp
|
||||||
|
${PACKAGE_DIR}/src/glad.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(rhi_integration_backpack PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../fixtures
|
||||||
|
${ENGINE_ROOT_DIR}/include
|
||||||
|
${PACKAGE_DIR}/include
|
||||||
|
${PROJECT_ROOT_DIR}/engine/src
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(rhi_integration_backpack PRIVATE
|
||||||
|
d3d12
|
||||||
|
dxgi
|
||||||
|
d3dcompiler
|
||||||
|
winmm
|
||||||
|
opengl32
|
||||||
|
XCEngine
|
||||||
|
GTest::gtest
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(rhi_integration_backpack PRIVATE
|
||||||
|
UNICODE
|
||||||
|
_UNICODE
|
||||||
|
XCENGINE_SUPPORT_OPENGL
|
||||||
|
XCENGINE_SUPPORT_D3D12
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_command(TARGET rhi_integration_backpack POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/Res
|
||||||
|
$<TARGET_FILE_DIR:rhi_integration_backpack>/Res
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
${CMAKE_SOURCE_DIR}/tests/RHI/integration/compare_ppm.py
|
||||||
|
$<TARGET_FILE_DIR:rhi_integration_backpack>/
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm
|
||||||
|
$<TARGET_FILE_DIR:rhi_integration_backpack>/GT.ppm
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
${ENGINE_ROOT_DIR}/third_party/renderdoc/renderdoc.dll
|
||||||
|
$<TARGET_FILE_DIR:rhi_integration_backpack>/
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
${ENGINE_ROOT_DIR}/third_party/assimp/bin/assimp-vc143-mt.dll
|
||||||
|
$<TARGET_FILE_DIR:rhi_integration_backpack>/
|
||||||
|
)
|
||||||
|
|
||||||
|
include(GoogleTest)
|
||||||
|
gtest_discover_tests(rhi_integration_backpack)
|
||||||
20342
tests/RHI/integration/backpack/GT.ppm
Normal file
20342
tests/RHI/integration/backpack/GT.ppm
Normal file
File diff suppressed because one or more lines are too long
BIN
tests/RHI/integration/backpack/Res/models/backpack/ao.jpg
Normal file
BIN
tests/RHI/integration/backpack/Res/models/backpack/ao.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
@@ -0,0 +1,16 @@
|
|||||||
|
# Blender MTL File: 'None'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Scene_-_Root
|
||||||
|
Ns 225.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.0 0.0 0.0
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
map_Kd diffuse.jpg
|
||||||
|
map_Bump normal.png
|
||||||
|
map_Ks specular.jpg
|
||||||
|
|
||||||
BIN
tests/RHI/integration/backpack/Res/models/backpack/diffuse.jpg
Normal file
BIN
tests/RHI/integration/backpack/Res/models/backpack/diffuse.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 MiB |
BIN
tests/RHI/integration/backpack/Res/models/backpack/normal.png
Normal file
BIN
tests/RHI/integration/backpack/Res/models/backpack/normal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 MiB |
BIN
tests/RHI/integration/backpack/Res/models/backpack/roughness.jpg
Normal file
BIN
tests/RHI/integration/backpack/Res/models/backpack/roughness.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 MiB |
@@ -0,0 +1,3 @@
|
|||||||
|
Model by Berk Gedik, from: https://sketchfab.com/3d-models/survival-guitar-backpack-low-poly-799f8c4511f84fab8c3f12887f7e6b36
|
||||||
|
|
||||||
|
Modified material assignment (Joey de Vries) for easier load in OpenGL model loading chapter, and renamed albedo to diffuse and metallic to specular to match non-PBR lighting setup.
|
||||||
BIN
tests/RHI/integration/backpack/Res/models/backpack/specular.jpg
Normal file
BIN
tests/RHI/integration/backpack/Res/models/backpack/specular.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 MiB |
983
tests/RHI/integration/backpack/main.cpp
Normal file
983
tests/RHI/integration/backpack/main.cpp
Normal file
@@ -0,0 +1,983 @@
|
|||||||
|
#define NOMINMAX
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <math.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
#include <string.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "../fixtures/RHIIntegrationFixture.h"
|
||||||
|
#include "XCEngine/Core/Containers/String.h"
|
||||||
|
#include "XCEngine/Core/Math/Matrix4.h"
|
||||||
|
#include "XCEngine/Core/Math/Vector3.h"
|
||||||
|
#include "XCEngine/Core/Math/Vector4.h"
|
||||||
|
#include "XCEngine/Debug/ConsoleLogSink.h"
|
||||||
|
#include "XCEngine/Debug/Logger.h"
|
||||||
|
#include "XCEngine/Resources/Material/Material.h"
|
||||||
|
#include "XCEngine/Resources/Mesh/Mesh.h"
|
||||||
|
#include "XCEngine/Resources/Mesh/MeshImportSettings.h"
|
||||||
|
#include "XCEngine/Resources/Mesh/MeshLoader.h"
|
||||||
|
#include "XCEngine/Resources/Texture/Texture.h"
|
||||||
|
#include "XCEngine/RHI/RHIBuffer.h"
|
||||||
|
#include "XCEngine/RHI/RHICommandList.h"
|
||||||
|
#include "XCEngine/RHI/RHICommandQueue.h"
|
||||||
|
#include "XCEngine/RHI/RHIDescriptorPool.h"
|
||||||
|
#include "XCEngine/RHI/RHIDescriptorSet.h"
|
||||||
|
#include "XCEngine/RHI/RHIPipelineLayout.h"
|
||||||
|
#include "XCEngine/RHI/RHIPipelineState.h"
|
||||||
|
#include "XCEngine/RHI/RHIResourceView.h"
|
||||||
|
#include "XCEngine/RHI/RHISampler.h"
|
||||||
|
#include "XCEngine/RHI/RHISwapChain.h"
|
||||||
|
#include "XCEngine/RHI/RHITexture.h"
|
||||||
|
|
||||||
|
using namespace XCEngine::Containers;
|
||||||
|
using namespace XCEngine::Debug;
|
||||||
|
using namespace XCEngine::Math;
|
||||||
|
using namespace XCEngine::Resources;
|
||||||
|
using namespace XCEngine::RHI;
|
||||||
|
using namespace XCEngine::RHI::Integration;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct BackpackVertex {
|
||||||
|
float position[4];
|
||||||
|
float normal[4];
|
||||||
|
float uv[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SceneConstants {
|
||||||
|
Matrix4x4 projection;
|
||||||
|
Matrix4x4 view;
|
||||||
|
Matrix4x4 model;
|
||||||
|
Vector4 cameraPosition;
|
||||||
|
Vector4 lightDirection;
|
||||||
|
Vector4 lightColor;
|
||||||
|
Vector4 ambientColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MaterialResources {
|
||||||
|
RHITexture* baseColorTexture = nullptr;
|
||||||
|
RHIResourceView* baseColorView = nullptr;
|
||||||
|
RHITexture* specularTexture = nullptr;
|
||||||
|
RHIResourceView* specularView = nullptr;
|
||||||
|
RHIDescriptorPool* texturePool = nullptr;
|
||||||
|
RHIDescriptorSet* textureSet = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr uint32_t kFrameWidth = 1280;
|
||||||
|
constexpr uint32_t kFrameHeight = 720;
|
||||||
|
constexpr uint32_t kTextureBindingCount = 2;
|
||||||
|
constexpr uint32_t kSamplerBindingCount = 2;
|
||||||
|
constexpr uint32_t kDescriptorSetCount = 3;
|
||||||
|
constexpr float kPi = 3.14159265358979323846f;
|
||||||
|
|
||||||
|
std::filesystem::path GetExecutableDirectory() {
|
||||||
|
char exePath[MAX_PATH] = {};
|
||||||
|
const DWORD length = GetModuleFileNameA(nullptr, exePath, MAX_PATH);
|
||||||
|
if (length == 0 || length >= MAX_PATH) {
|
||||||
|
return std::filesystem::current_path();
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::filesystem::path(exePath).parent_path();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path ResolveRuntimePath(const char* relativePath) {
|
||||||
|
return GetExecutableDirectory() / relativePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* GetScreenshotFilename(RHIType type) {
|
||||||
|
return type == RHIType::D3D12 ? "backpack_d3d12.ppm" : "backpack_opengl.ppm";
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetComparisonThreshold(RHIType type) {
|
||||||
|
return type == RHIType::OpenGL ? 8 : 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
Format ToRHITextureFormat(TextureFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case TextureFormat::RGBA8_UNORM:
|
||||||
|
return Format::R8G8B8A8_UNorm;
|
||||||
|
default:
|
||||||
|
return Format::Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RHITexture* CreateSolidColorTexture(RHIDevice* device, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||||
|
const uint8_t pixels[] = { r, g, b, a };
|
||||||
|
|
||||||
|
TextureDesc textureDesc = {};
|
||||||
|
textureDesc.width = 1;
|
||||||
|
textureDesc.height = 1;
|
||||||
|
textureDesc.depth = 1;
|
||||||
|
textureDesc.mipLevels = 1;
|
||||||
|
textureDesc.arraySize = 1;
|
||||||
|
textureDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||||
|
textureDesc.textureType = static_cast<uint32_t>(XCEngine::RHI::TextureType::Texture2D);
|
||||||
|
textureDesc.sampleCount = 1;
|
||||||
|
textureDesc.sampleQuality = 0;
|
||||||
|
textureDesc.flags = 0;
|
||||||
|
|
||||||
|
return device->CreateTexture(textureDesc, pixels, sizeof(pixels), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateTextureViewFromResourceTexture(RHIDevice* device,
|
||||||
|
Texture* resourceTexture,
|
||||||
|
RHITexture*& outTexture,
|
||||||
|
RHIResourceView*& outView) {
|
||||||
|
outTexture = nullptr;
|
||||||
|
outView = nullptr;
|
||||||
|
|
||||||
|
if (device == nullptr || resourceTexture == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Format rhiFormat = ToRHITextureFormat(resourceTexture->GetFormat());
|
||||||
|
if (rhiFormat == Format::Unknown || resourceTexture->GetPixelData() == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureDesc textureDesc = {};
|
||||||
|
textureDesc.width = resourceTexture->GetWidth();
|
||||||
|
textureDesc.height = resourceTexture->GetHeight();
|
||||||
|
textureDesc.depth = resourceTexture->GetDepth();
|
||||||
|
textureDesc.mipLevels = resourceTexture->GetMipLevels();
|
||||||
|
textureDesc.arraySize = resourceTexture->GetArraySize();
|
||||||
|
textureDesc.format = static_cast<uint32_t>(rhiFormat);
|
||||||
|
textureDesc.textureType = static_cast<uint32_t>(XCEngine::RHI::TextureType::Texture2D);
|
||||||
|
textureDesc.sampleCount = 1;
|
||||||
|
textureDesc.sampleQuality = 0;
|
||||||
|
textureDesc.flags = 0;
|
||||||
|
|
||||||
|
outTexture = device->CreateTexture(
|
||||||
|
textureDesc,
|
||||||
|
resourceTexture->GetPixelData(),
|
||||||
|
resourceTexture->GetPixelDataSize(),
|
||||||
|
resourceTexture->GetWidth() * 4u);
|
||||||
|
if (outTexture == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceViewDesc textureViewDesc = {};
|
||||||
|
textureViewDesc.format = static_cast<uint32_t>(rhiFormat);
|
||||||
|
textureViewDesc.dimension = ResourceViewDimension::Texture2D;
|
||||||
|
textureViewDesc.mipLevel = 0;
|
||||||
|
outView = device->CreateShaderResourceView(outTexture, textureViewDesc);
|
||||||
|
|
||||||
|
if (outView == nullptr) {
|
||||||
|
outTexture->Shutdown();
|
||||||
|
delete outTexture;
|
||||||
|
outTexture = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneConstants CreateSceneConstants(const Mesh& mesh) {
|
||||||
|
const Vector3 min = mesh.GetBounds().GetMin();
|
||||||
|
const Vector3 max = mesh.GetBounds().GetMax();
|
||||||
|
const Vector3 center(
|
||||||
|
(min.x + max.x) * 0.5f,
|
||||||
|
(min.y + max.y) * 0.5f,
|
||||||
|
(min.z + max.z) * 0.5f);
|
||||||
|
const float sizeX = std::max(max.x - min.x, 0.001f);
|
||||||
|
const float sizeY = std::max(max.y - min.y, 0.001f);
|
||||||
|
const float sizeZ = std::max(max.z - min.z, 0.001f);
|
||||||
|
const float maxExtent = std::max(sizeX, std::max(sizeY, sizeZ));
|
||||||
|
const float uniformScale = 1.8f / maxExtent;
|
||||||
|
|
||||||
|
const Matrix4x4 centerTranslation =
|
||||||
|
Matrix4x4::Translation(Vector3(-center.x, -center.y, -center.z));
|
||||||
|
const Matrix4x4 rotation =
|
||||||
|
Matrix4x4::RotationY(-0.35f) * Matrix4x4::RotationX(-0.18f);
|
||||||
|
const Matrix4x4 scale =
|
||||||
|
Matrix4x4::Scale(Vector3(uniformScale, uniformScale, uniformScale));
|
||||||
|
const Matrix4x4 world =
|
||||||
|
Matrix4x4::Translation(Vector3(0.0f, -0.25f, 3.0f)) *
|
||||||
|
rotation *
|
||||||
|
scale *
|
||||||
|
centerTranslation;
|
||||||
|
|
||||||
|
SceneConstants constants = {};
|
||||||
|
constants.projection =
|
||||||
|
Matrix4x4::Perspective(45.0f * kPi / 180.0f,
|
||||||
|
static_cast<float>(kFrameWidth) / static_cast<float>(kFrameHeight),
|
||||||
|
0.1f,
|
||||||
|
100.0f)
|
||||||
|
.Transpose();
|
||||||
|
constants.view = Matrix4x4::Identity().Transpose();
|
||||||
|
constants.model = world.Transpose();
|
||||||
|
constants.cameraPosition = Vector4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
constants.lightDirection = Vector4(-0.30f, 0.85f, -0.40f, 0.0f);
|
||||||
|
constants.lightColor = Vector4(0.95f, 0.95f, 0.95f, 1.0f);
|
||||||
|
constants.ambientColor = Vector4(0.22f, 0.22f, 0.22f, 1.0f);
|
||||||
|
return constants;
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphicsPipelineDesc CreateBackpackPipelineDesc(RHIType type, RHIPipelineLayout* pipelineLayout) {
|
||||||
|
GraphicsPipelineDesc desc = {};
|
||||||
|
desc.pipelineLayout = pipelineLayout;
|
||||||
|
desc.topologyType = static_cast<uint32_t>(PrimitiveTopologyType::Triangle);
|
||||||
|
desc.renderTargetFormats[0] = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||||
|
desc.depthStencilFormat = static_cast<uint32_t>(Format::D24_UNorm_S8_UInt);
|
||||||
|
desc.sampleCount = 1;
|
||||||
|
|
||||||
|
desc.rasterizerState.fillMode = static_cast<uint32_t>(FillMode::Solid);
|
||||||
|
desc.rasterizerState.cullMode = static_cast<uint32_t>(CullMode::None);
|
||||||
|
desc.rasterizerState.frontFace = static_cast<uint32_t>(FrontFace::CounterClockwise);
|
||||||
|
desc.rasterizerState.depthClipEnable = true;
|
||||||
|
|
||||||
|
desc.depthStencilState.depthTestEnable = true;
|
||||||
|
desc.depthStencilState.depthWriteEnable = true;
|
||||||
|
desc.depthStencilState.depthFunc = static_cast<uint32_t>(ComparisonFunc::Less);
|
||||||
|
desc.depthStencilState.stencilEnable = false;
|
||||||
|
|
||||||
|
InputElementDesc position = {};
|
||||||
|
position.semanticName = "POSITION";
|
||||||
|
position.semanticIndex = 0;
|
||||||
|
position.format = static_cast<uint32_t>(Format::R32G32B32A32_Float);
|
||||||
|
position.inputSlot = 0;
|
||||||
|
position.alignedByteOffset = 0;
|
||||||
|
desc.inputLayout.elements.push_back(position);
|
||||||
|
|
||||||
|
InputElementDesc normal = {};
|
||||||
|
normal.semanticName = "NORMAL";
|
||||||
|
normal.semanticIndex = 0;
|
||||||
|
normal.format = static_cast<uint32_t>(Format::R32G32B32A32_Float);
|
||||||
|
normal.inputSlot = 0;
|
||||||
|
normal.alignedByteOffset = sizeof(float) * 4;
|
||||||
|
desc.inputLayout.elements.push_back(normal);
|
||||||
|
|
||||||
|
InputElementDesc texcoord = {};
|
||||||
|
texcoord.semanticName = "TEXCOORD";
|
||||||
|
texcoord.semanticIndex = 0;
|
||||||
|
texcoord.format = static_cast<uint32_t>(Format::R32G32_Float);
|
||||||
|
texcoord.inputSlot = 0;
|
||||||
|
texcoord.alignedByteOffset = sizeof(float) * 8;
|
||||||
|
desc.inputLayout.elements.push_back(texcoord);
|
||||||
|
|
||||||
|
const char kBackpackHlsl[] = R"(
|
||||||
|
Texture2D gBaseColorTexture : register(t0);
|
||||||
|
Texture2D gSpecularTexture : register(t1);
|
||||||
|
SamplerState gBaseColorSampler : register(s0);
|
||||||
|
SamplerState gSpecularSampler : register(s1);
|
||||||
|
|
||||||
|
cbuffer SceneData : register(b0) {
|
||||||
|
float4x4 gProjectionMatrix;
|
||||||
|
float4x4 gViewMatrix;
|
||||||
|
float4x4 gModelMatrix;
|
||||||
|
float4 gCameraPosition;
|
||||||
|
float4 gLightDirection;
|
||||||
|
float4 gLightColor;
|
||||||
|
float4 gAmbientColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VSInput {
|
||||||
|
float4 position : POSITION;
|
||||||
|
float4 normal : NORMAL;
|
||||||
|
float2 texcoord : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PSInput {
|
||||||
|
float4 position : SV_POSITION;
|
||||||
|
float3 worldPosition : TEXCOORD0;
|
||||||
|
float3 worldNormal : TEXCOORD1;
|
||||||
|
float2 texcoord : TEXCOORD2;
|
||||||
|
};
|
||||||
|
|
||||||
|
PSInput MainVS(VSInput input) {
|
||||||
|
PSInput output;
|
||||||
|
float4 worldPosition = mul(gModelMatrix, input.position);
|
||||||
|
float4 viewPosition = mul(gViewMatrix, worldPosition);
|
||||||
|
output.position = mul(gProjectionMatrix, viewPosition);
|
||||||
|
output.worldPosition = worldPosition.xyz;
|
||||||
|
output.worldNormal = mul((float3x3)gModelMatrix, input.normal.xyz);
|
||||||
|
output.texcoord = input.texcoord;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 MainPS(PSInput input, bool isFrontFace : SV_IsFrontFace) : SV_TARGET {
|
||||||
|
float3 normal = normalize(isFrontFace ? input.worldNormal : -input.worldNormal);
|
||||||
|
float3 lightDir = normalize(gLightDirection.xyz);
|
||||||
|
float3 viewDir = normalize(gCameraPosition.xyz - input.worldPosition);
|
||||||
|
float3 halfDir = normalize(lightDir + viewDir);
|
||||||
|
|
||||||
|
float3 baseColor = gBaseColorTexture.Sample(gBaseColorSampler, input.texcoord).rgb;
|
||||||
|
float specularMask = gSpecularTexture.Sample(gSpecularSampler, input.texcoord).r;
|
||||||
|
float diffuse = saturate(dot(normal, lightDir));
|
||||||
|
float specular = pow(saturate(dot(normal, halfDir)), 32.0f) * specularMask;
|
||||||
|
|
||||||
|
float3 color = baseColor * (gAmbientColor.rgb + gLightColor.rgb * diffuse);
|
||||||
|
color += gLightColor.rgb * (specular * 0.35f);
|
||||||
|
return float4(saturate(color), 1.0f);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
const char kBackpackVertexShader[] = R"(#version 430
|
||||||
|
layout(location = 0) in vec4 aPosition;
|
||||||
|
layout(location = 1) in vec4 aNormal;
|
||||||
|
layout(location = 2) in vec2 aTexCoord;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform SceneData {
|
||||||
|
mat4 gProjectionMatrix;
|
||||||
|
mat4 gViewMatrix;
|
||||||
|
mat4 gModelMatrix;
|
||||||
|
vec4 gCameraPosition;
|
||||||
|
vec4 gLightDirection;
|
||||||
|
vec4 gLightColor;
|
||||||
|
vec4 gAmbientColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec3 vWorldPosition;
|
||||||
|
out vec3 vWorldNormal;
|
||||||
|
out vec2 vTexCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 worldPosition = gModelMatrix * aPosition;
|
||||||
|
vec4 viewPosition = gViewMatrix * worldPosition;
|
||||||
|
gl_Position = gProjectionMatrix * viewPosition;
|
||||||
|
vWorldPosition = worldPosition.xyz;
|
||||||
|
vWorldNormal = mat3(gModelMatrix) * aNormal.xyz;
|
||||||
|
vTexCoord = aTexCoord;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
const char kBackpackFragmentShader[] = R"(#version 430
|
||||||
|
layout(std140, binding = 0) uniform SceneData {
|
||||||
|
mat4 gProjectionMatrix;
|
||||||
|
mat4 gViewMatrix;
|
||||||
|
mat4 gModelMatrix;
|
||||||
|
vec4 gCameraPosition;
|
||||||
|
vec4 gLightDirection;
|
||||||
|
vec4 gLightColor;
|
||||||
|
vec4 gAmbientColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 0) uniform sampler2D uBaseColorTexture;
|
||||||
|
layout(binding = 1) uniform sampler2D uSpecularTexture;
|
||||||
|
|
||||||
|
in vec3 vWorldPosition;
|
||||||
|
in vec3 vWorldNormal;
|
||||||
|
in vec2 vTexCoord;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 normal = normalize(gl_FrontFacing ? vWorldNormal : -vWorldNormal);
|
||||||
|
vec3 lightDir = normalize(gLightDirection.xyz);
|
||||||
|
vec3 viewDir = normalize(gCameraPosition.xyz - vWorldPosition);
|
||||||
|
vec3 halfDir = normalize(lightDir + viewDir);
|
||||||
|
|
||||||
|
vec3 baseColor = texture(uBaseColorTexture, vTexCoord).rgb;
|
||||||
|
float specularMask = texture(uSpecularTexture, vTexCoord).r;
|
||||||
|
float diffuse = max(dot(normal, lightDir), 0.0);
|
||||||
|
float specular = pow(max(dot(normal, halfDir), 0.0), 32.0) * specularMask;
|
||||||
|
|
||||||
|
vec3 color = baseColor * (gAmbientColor.rgb + gLightColor.rgb * diffuse);
|
||||||
|
color += gLightColor.rgb * (specular * 0.35);
|
||||||
|
fragColor = vec4(clamp(color, 0.0, 1.0), 1.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
if (type == RHIType::D3D12) {
|
||||||
|
desc.vertexShader.source.assign(kBackpackHlsl, kBackpackHlsl + strlen(kBackpackHlsl));
|
||||||
|
desc.vertexShader.sourceLanguage = XCEngine::RHI::ShaderLanguage::HLSL;
|
||||||
|
desc.vertexShader.entryPoint = L"MainVS";
|
||||||
|
desc.vertexShader.profile = L"vs_5_0";
|
||||||
|
|
||||||
|
desc.fragmentShader.source.assign(kBackpackHlsl, kBackpackHlsl + strlen(kBackpackHlsl));
|
||||||
|
desc.fragmentShader.sourceLanguage = XCEngine::RHI::ShaderLanguage::HLSL;
|
||||||
|
desc.fragmentShader.entryPoint = L"MainPS";
|
||||||
|
desc.fragmentShader.profile = L"ps_5_0";
|
||||||
|
} else {
|
||||||
|
desc.vertexShader.source.assign(kBackpackVertexShader,
|
||||||
|
kBackpackVertexShader + strlen(kBackpackVertexShader));
|
||||||
|
desc.vertexShader.sourceLanguage = XCEngine::RHI::ShaderLanguage::GLSL;
|
||||||
|
desc.vertexShader.profile = L"vs_4_30";
|
||||||
|
|
||||||
|
desc.fragmentShader.source.assign(kBackpackFragmentShader,
|
||||||
|
kBackpackFragmentShader + strlen(kBackpackFragmentShader));
|
||||||
|
desc.fragmentShader.sourceLanguage = XCEngine::RHI::ShaderLanguage::GLSL;
|
||||||
|
desc.fragmentShader.profile = L"fs_4_30";
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BackpackTest : public RHIIntegrationFixture {
|
||||||
|
protected:
|
||||||
|
void SetUp() override;
|
||||||
|
void TearDown() override;
|
||||||
|
void RenderFrame() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Texture* GetMaterialTexture(Material* material, const char* propertyName) const;
|
||||||
|
void InitializeBackpackResources();
|
||||||
|
void ShutdownBackpackResources();
|
||||||
|
void LoadBackpackMesh();
|
||||||
|
void InitializeGeometryResources();
|
||||||
|
void InitializeFallbackTextures();
|
||||||
|
void InitializeMaterialResources();
|
||||||
|
|
||||||
|
Mesh* mMesh = nullptr;
|
||||||
|
std::vector<BackpackVertex> mVertices;
|
||||||
|
RHIBuffer* mVertexBuffer = nullptr;
|
||||||
|
RHIResourceView* mVertexBufferView = nullptr;
|
||||||
|
RHIBuffer* mIndexBuffer = nullptr;
|
||||||
|
RHIResourceView* mIndexBufferView = nullptr;
|
||||||
|
RHITexture* mFallbackBaseColorTexture = nullptr;
|
||||||
|
RHIResourceView* mFallbackBaseColorView = nullptr;
|
||||||
|
RHITexture* mFallbackSpecularTexture = nullptr;
|
||||||
|
RHIResourceView* mFallbackSpecularView = nullptr;
|
||||||
|
RHISampler* mSampler = nullptr;
|
||||||
|
RHIDescriptorPool* mConstantPool = nullptr;
|
||||||
|
RHIDescriptorSet* mConstantSet = nullptr;
|
||||||
|
RHIDescriptorPool* mSamplerPool = nullptr;
|
||||||
|
RHIDescriptorSet* mSamplerSet = nullptr;
|
||||||
|
std::vector<MaterialResources> mMaterialResources;
|
||||||
|
RHIPipelineLayout* mPipelineLayout = nullptr;
|
||||||
|
RHIPipelineState* mPipelineState = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
void BackpackTest::SetUp() {
|
||||||
|
RHIIntegrationFixture::SetUp();
|
||||||
|
InitializeBackpackResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackpackTest::TearDown() {
|
||||||
|
ShutdownBackpackResources();
|
||||||
|
RHIIntegrationFixture::TearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture* BackpackTest::GetMaterialTexture(Material* material, const char* propertyName) const {
|
||||||
|
if (material == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResourceHandle<Texture> textureHandle = material->GetTexture(String(propertyName));
|
||||||
|
return textureHandle.IsValid() ? textureHandle.Get() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackpackTest::LoadBackpackMesh() {
|
||||||
|
const std::filesystem::path meshPath = ResolveRuntimePath("Res/models/backpack/backpack.obj");
|
||||||
|
const std::string meshPathString = meshPath.string();
|
||||||
|
|
||||||
|
MeshLoader loader;
|
||||||
|
MeshImportSettings settings;
|
||||||
|
settings.AddImportFlag(MeshImportFlags::FlipUVs);
|
||||||
|
|
||||||
|
LoadResult result = loader.Load(meshPathString.c_str(), &settings);
|
||||||
|
ASSERT_TRUE(result);
|
||||||
|
ASSERT_NE(result.resource, nullptr);
|
||||||
|
|
||||||
|
mMesh = static_cast<Mesh*>(result.resource);
|
||||||
|
ASSERT_NE(mMesh, nullptr);
|
||||||
|
ASSERT_GT(mMesh->GetVertexCount(), 0u);
|
||||||
|
ASSERT_GT(mMesh->GetIndexCount(), 0u);
|
||||||
|
ASSERT_GT(mMesh->GetSections().Size(), 0u);
|
||||||
|
|
||||||
|
Log("[TEST] Backpack mesh loaded: vertices=%u indices=%u sections=%u materials=%u textures=%u use32Bit=%d",
|
||||||
|
mMesh->GetVertexCount(),
|
||||||
|
mMesh->GetIndexCount(),
|
||||||
|
static_cast<uint32_t>(mMesh->GetSections().Size()),
|
||||||
|
static_cast<uint32_t>(mMesh->GetMaterials().Size()),
|
||||||
|
static_cast<uint32_t>(mMesh->GetTextures().Size()),
|
||||||
|
mMesh->IsUse32BitIndex() ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackpackTest::InitializeGeometryResources() {
|
||||||
|
ASSERT_NE(mMesh, nullptr);
|
||||||
|
ASSERT_EQ(mMesh->GetVertexStride(), sizeof(StaticMeshVertex));
|
||||||
|
|
||||||
|
const auto* sourceVertices = static_cast<const StaticMeshVertex*>(mMesh->GetVertexData());
|
||||||
|
ASSERT_NE(sourceVertices, nullptr);
|
||||||
|
|
||||||
|
mVertices.resize(mMesh->GetVertexCount());
|
||||||
|
for (uint32_t vertexIndex = 0; vertexIndex < mMesh->GetVertexCount(); ++vertexIndex) {
|
||||||
|
const StaticMeshVertex& source = sourceVertices[vertexIndex];
|
||||||
|
BackpackVertex& destination = mVertices[vertexIndex];
|
||||||
|
|
||||||
|
destination.position[0] = source.position.x;
|
||||||
|
destination.position[1] = source.position.y;
|
||||||
|
destination.position[2] = source.position.z;
|
||||||
|
destination.position[3] = 1.0f;
|
||||||
|
|
||||||
|
destination.normal[0] = source.normal.x;
|
||||||
|
destination.normal[1] = source.normal.y;
|
||||||
|
destination.normal[2] = source.normal.z;
|
||||||
|
destination.normal[3] = 0.0f;
|
||||||
|
|
||||||
|
destination.uv[0] = source.uv0.x;
|
||||||
|
destination.uv[1] = source.uv0.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferDesc vertexBufferDesc = {};
|
||||||
|
vertexBufferDesc.size = static_cast<uint64_t>(mVertices.size() * sizeof(BackpackVertex));
|
||||||
|
vertexBufferDesc.stride = sizeof(BackpackVertex);
|
||||||
|
vertexBufferDesc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
|
||||||
|
|
||||||
|
mVertexBuffer = GetDevice()->CreateBuffer(vertexBufferDesc);
|
||||||
|
ASSERT_NE(mVertexBuffer, nullptr);
|
||||||
|
mVertexBuffer->SetData(mVertices.data(), mVertices.size() * sizeof(BackpackVertex));
|
||||||
|
mVertexBuffer->SetStride(sizeof(BackpackVertex));
|
||||||
|
mVertexBuffer->SetBufferType(BufferType::Vertex);
|
||||||
|
|
||||||
|
ResourceViewDesc vertexViewDesc = {};
|
||||||
|
vertexViewDesc.dimension = ResourceViewDimension::Buffer;
|
||||||
|
vertexViewDesc.structureByteStride = sizeof(BackpackVertex);
|
||||||
|
mVertexBufferView = GetDevice()->CreateVertexBufferView(mVertexBuffer, vertexViewDesc);
|
||||||
|
ASSERT_NE(mVertexBufferView, nullptr);
|
||||||
|
|
||||||
|
BufferDesc indexBufferDesc = {};
|
||||||
|
indexBufferDesc.size = static_cast<uint64_t>(mMesh->GetIndexDataSize());
|
||||||
|
indexBufferDesc.stride = mMesh->IsUse32BitIndex() ? sizeof(uint32_t) : sizeof(uint16_t);
|
||||||
|
indexBufferDesc.bufferType = static_cast<uint32_t>(BufferType::Index);
|
||||||
|
|
||||||
|
mIndexBuffer = GetDevice()->CreateBuffer(indexBufferDesc);
|
||||||
|
ASSERT_NE(mIndexBuffer, nullptr);
|
||||||
|
mIndexBuffer->SetData(mMesh->GetIndexData(), mMesh->GetIndexDataSize());
|
||||||
|
mIndexBuffer->SetStride(indexBufferDesc.stride);
|
||||||
|
mIndexBuffer->SetBufferType(BufferType::Index);
|
||||||
|
|
||||||
|
ResourceViewDesc indexViewDesc = {};
|
||||||
|
indexViewDesc.dimension = ResourceViewDimension::Buffer;
|
||||||
|
indexViewDesc.format = static_cast<uint32_t>(mMesh->IsUse32BitIndex() ? Format::R32_UInt : Format::R16_UInt);
|
||||||
|
mIndexBufferView = GetDevice()->CreateIndexBufferView(mIndexBuffer, indexViewDesc);
|
||||||
|
ASSERT_NE(mIndexBufferView, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackpackTest::InitializeFallbackTextures() {
|
||||||
|
mFallbackBaseColorTexture = CreateSolidColorTexture(GetDevice(), 255, 255, 255, 255);
|
||||||
|
ASSERT_NE(mFallbackBaseColorTexture, nullptr);
|
||||||
|
|
||||||
|
ResourceViewDesc baseColorViewDesc = {};
|
||||||
|
baseColorViewDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||||
|
baseColorViewDesc.dimension = ResourceViewDimension::Texture2D;
|
||||||
|
baseColorViewDesc.mipLevel = 0;
|
||||||
|
mFallbackBaseColorView = GetDevice()->CreateShaderResourceView(mFallbackBaseColorTexture, baseColorViewDesc);
|
||||||
|
ASSERT_NE(mFallbackBaseColorView, nullptr);
|
||||||
|
|
||||||
|
mFallbackSpecularTexture = CreateSolidColorTexture(GetDevice(), 0, 0, 0, 255);
|
||||||
|
ASSERT_NE(mFallbackSpecularTexture, nullptr);
|
||||||
|
|
||||||
|
ResourceViewDesc specularViewDesc = {};
|
||||||
|
specularViewDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||||
|
specularViewDesc.dimension = ResourceViewDimension::Texture2D;
|
||||||
|
specularViewDesc.mipLevel = 0;
|
||||||
|
mFallbackSpecularView = GetDevice()->CreateShaderResourceView(mFallbackSpecularTexture, specularViewDesc);
|
||||||
|
ASSERT_NE(mFallbackSpecularView, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackpackTest::InitializeMaterialResources() {
|
||||||
|
ASSERT_NE(mMesh, nullptr);
|
||||||
|
ASSERT_NE(mFallbackBaseColorView, nullptr);
|
||||||
|
ASSERT_NE(mFallbackSpecularView, nullptr);
|
||||||
|
|
||||||
|
DescriptorSetLayoutBinding textureBindings[kTextureBindingCount] = {};
|
||||||
|
textureBindings[0].binding = 0;
|
||||||
|
textureBindings[0].type = static_cast<uint32_t>(DescriptorType::SRV);
|
||||||
|
textureBindings[0].count = 1;
|
||||||
|
textureBindings[1].binding = 1;
|
||||||
|
textureBindings[1].type = static_cast<uint32_t>(DescriptorType::SRV);
|
||||||
|
textureBindings[1].count = 1;
|
||||||
|
|
||||||
|
DescriptorSetLayoutDesc textureLayoutDesc = {};
|
||||||
|
textureLayoutDesc.bindings = textureBindings;
|
||||||
|
textureLayoutDesc.bindingCount = kTextureBindingCount;
|
||||||
|
|
||||||
|
const uint32_t materialCount =
|
||||||
|
std::max(1u, static_cast<uint32_t>(mMesh->GetMaterials().Size()));
|
||||||
|
mMaterialResources.resize(materialCount);
|
||||||
|
|
||||||
|
for (uint32_t materialIndex = 0; materialIndex < materialCount; ++materialIndex) {
|
||||||
|
Material* material =
|
||||||
|
materialIndex < mMesh->GetMaterials().Size()
|
||||||
|
? mMesh->GetMaterials()[materialIndex]
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
MaterialResources& resources = mMaterialResources[materialIndex];
|
||||||
|
|
||||||
|
Texture* baseColorTexture = GetMaterialTexture(material, "baseColorTexture");
|
||||||
|
if (baseColorTexture != nullptr) {
|
||||||
|
const bool created =
|
||||||
|
CreateTextureViewFromResourceTexture(
|
||||||
|
GetDevice(),
|
||||||
|
baseColorTexture,
|
||||||
|
resources.baseColorTexture,
|
||||||
|
resources.baseColorView);
|
||||||
|
ASSERT_TRUE(created);
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture* specularTexture = GetMaterialTexture(material, "specularTexture");
|
||||||
|
if (specularTexture != nullptr) {
|
||||||
|
const bool created =
|
||||||
|
CreateTextureViewFromResourceTexture(
|
||||||
|
GetDevice(),
|
||||||
|
specularTexture,
|
||||||
|
resources.specularTexture,
|
||||||
|
resources.specularView);
|
||||||
|
ASSERT_TRUE(created);
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorPoolDesc texturePoolDesc = {};
|
||||||
|
texturePoolDesc.type = DescriptorHeapType::CBV_SRV_UAV;
|
||||||
|
texturePoolDesc.descriptorCount = kTextureBindingCount;
|
||||||
|
texturePoolDesc.shaderVisible = true;
|
||||||
|
resources.texturePool = GetDevice()->CreateDescriptorPool(texturePoolDesc);
|
||||||
|
ASSERT_NE(resources.texturePool, nullptr);
|
||||||
|
|
||||||
|
resources.textureSet = resources.texturePool->AllocateSet(textureLayoutDesc);
|
||||||
|
ASSERT_NE(resources.textureSet, nullptr);
|
||||||
|
resources.textureSet->Update(0, resources.baseColorView != nullptr ? resources.baseColorView : mFallbackBaseColorView);
|
||||||
|
resources.textureSet->Update(1, resources.specularView != nullptr ? resources.specularView : mFallbackSpecularView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackpackTest::InitializeBackpackResources() {
|
||||||
|
LoadBackpackMesh();
|
||||||
|
InitializeGeometryResources();
|
||||||
|
InitializeFallbackTextures();
|
||||||
|
|
||||||
|
SamplerDesc samplerDesc = {};
|
||||||
|
samplerDesc.filter = static_cast<uint32_t>(FilterMode::Linear);
|
||||||
|
samplerDesc.addressU = static_cast<uint32_t>(TextureAddressMode::Wrap);
|
||||||
|
samplerDesc.addressV = static_cast<uint32_t>(TextureAddressMode::Wrap);
|
||||||
|
samplerDesc.addressW = static_cast<uint32_t>(TextureAddressMode::Wrap);
|
||||||
|
samplerDesc.mipLodBias = 0.0f;
|
||||||
|
samplerDesc.maxAnisotropy = 1;
|
||||||
|
samplerDesc.comparisonFunc = static_cast<uint32_t>(ComparisonFunc::Always);
|
||||||
|
samplerDesc.borderColorR = 0.0f;
|
||||||
|
samplerDesc.borderColorG = 0.0f;
|
||||||
|
samplerDesc.borderColorB = 0.0f;
|
||||||
|
samplerDesc.borderColorA = 0.0f;
|
||||||
|
samplerDesc.minLod = 0.0f;
|
||||||
|
samplerDesc.maxLod = 1000.0f;
|
||||||
|
mSampler = GetDevice()->CreateSampler(samplerDesc);
|
||||||
|
ASSERT_NE(mSampler, nullptr);
|
||||||
|
|
||||||
|
DescriptorPoolDesc constantPoolDesc = {};
|
||||||
|
constantPoolDesc.type = DescriptorHeapType::CBV_SRV_UAV;
|
||||||
|
constantPoolDesc.descriptorCount = 1;
|
||||||
|
constantPoolDesc.shaderVisible = false;
|
||||||
|
mConstantPool = GetDevice()->CreateDescriptorPool(constantPoolDesc);
|
||||||
|
ASSERT_NE(mConstantPool, nullptr);
|
||||||
|
|
||||||
|
DescriptorSetLayoutBinding constantBinding = {};
|
||||||
|
constantBinding.binding = 0;
|
||||||
|
constantBinding.type = static_cast<uint32_t>(DescriptorType::CBV);
|
||||||
|
constantBinding.count = 1;
|
||||||
|
|
||||||
|
DescriptorSetLayoutDesc constantLayoutDesc = {};
|
||||||
|
constantLayoutDesc.bindings = &constantBinding;
|
||||||
|
constantLayoutDesc.bindingCount = 1;
|
||||||
|
|
||||||
|
mConstantSet = mConstantPool->AllocateSet(constantLayoutDesc);
|
||||||
|
ASSERT_NE(mConstantSet, nullptr);
|
||||||
|
const SceneConstants sceneConstants = CreateSceneConstants(*mMesh);
|
||||||
|
mConstantSet->WriteConstant(0, &sceneConstants, sizeof(sceneConstants));
|
||||||
|
|
||||||
|
DescriptorPoolDesc samplerPoolDesc = {};
|
||||||
|
samplerPoolDesc.type = DescriptorHeapType::Sampler;
|
||||||
|
samplerPoolDesc.descriptorCount = kSamplerBindingCount;
|
||||||
|
samplerPoolDesc.shaderVisible = true;
|
||||||
|
mSamplerPool = GetDevice()->CreateDescriptorPool(samplerPoolDesc);
|
||||||
|
ASSERT_NE(mSamplerPool, nullptr);
|
||||||
|
|
||||||
|
DescriptorSetLayoutBinding samplerBindings[kSamplerBindingCount] = {};
|
||||||
|
samplerBindings[0].binding = 0;
|
||||||
|
samplerBindings[0].type = static_cast<uint32_t>(DescriptorType::Sampler);
|
||||||
|
samplerBindings[0].count = 1;
|
||||||
|
samplerBindings[1].binding = 1;
|
||||||
|
samplerBindings[1].type = static_cast<uint32_t>(DescriptorType::Sampler);
|
||||||
|
samplerBindings[1].count = 1;
|
||||||
|
|
||||||
|
DescriptorSetLayoutDesc samplerLayoutDesc = {};
|
||||||
|
samplerLayoutDesc.bindings = samplerBindings;
|
||||||
|
samplerLayoutDesc.bindingCount = kSamplerBindingCount;
|
||||||
|
|
||||||
|
mSamplerSet = mSamplerPool->AllocateSet(samplerLayoutDesc);
|
||||||
|
ASSERT_NE(mSamplerSet, nullptr);
|
||||||
|
mSamplerSet->UpdateSampler(0, mSampler);
|
||||||
|
mSamplerSet->UpdateSampler(1, mSampler);
|
||||||
|
|
||||||
|
InitializeMaterialResources();
|
||||||
|
ASSERT_FALSE(mMaterialResources.empty());
|
||||||
|
|
||||||
|
DescriptorSetLayoutBinding textureBindings[kTextureBindingCount] = {};
|
||||||
|
textureBindings[0].binding = 0;
|
||||||
|
textureBindings[0].type = static_cast<uint32_t>(DescriptorType::SRV);
|
||||||
|
textureBindings[0].count = 1;
|
||||||
|
textureBindings[1].binding = 1;
|
||||||
|
textureBindings[1].type = static_cast<uint32_t>(DescriptorType::SRV);
|
||||||
|
textureBindings[1].count = 1;
|
||||||
|
|
||||||
|
DescriptorSetLayoutDesc textureLayoutDesc = {};
|
||||||
|
textureLayoutDesc.bindings = textureBindings;
|
||||||
|
textureLayoutDesc.bindingCount = kTextureBindingCount;
|
||||||
|
|
||||||
|
DescriptorSetLayoutDesc setLayouts[kDescriptorSetCount] = {};
|
||||||
|
setLayouts[0] = constantLayoutDesc;
|
||||||
|
setLayouts[1] = textureLayoutDesc;
|
||||||
|
setLayouts[2] = samplerLayoutDesc;
|
||||||
|
|
||||||
|
RHIPipelineLayoutDesc pipelineLayoutDesc = {};
|
||||||
|
pipelineLayoutDesc.setLayouts = setLayouts;
|
||||||
|
pipelineLayoutDesc.setLayoutCount = kDescriptorSetCount;
|
||||||
|
mPipelineLayout = GetDevice()->CreatePipelineLayout(pipelineLayoutDesc);
|
||||||
|
ASSERT_NE(mPipelineLayout, nullptr);
|
||||||
|
|
||||||
|
GraphicsPipelineDesc pipelineDesc =
|
||||||
|
CreateBackpackPipelineDesc(GetBackendType(), mPipelineLayout);
|
||||||
|
mPipelineState = GetDevice()->CreatePipelineState(pipelineDesc);
|
||||||
|
ASSERT_NE(mPipelineState, nullptr);
|
||||||
|
ASSERT_TRUE(mPipelineState->IsValid());
|
||||||
|
|
||||||
|
Log("[TEST] Backpack resources initialized for backend %d", static_cast<int>(GetBackendType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackpackTest::ShutdownBackpackResources() {
|
||||||
|
if (mPipelineState != nullptr) {
|
||||||
|
mPipelineState->Shutdown();
|
||||||
|
delete mPipelineState;
|
||||||
|
mPipelineState = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPipelineLayout != nullptr) {
|
||||||
|
mPipelineLayout->Shutdown();
|
||||||
|
delete mPipelineLayout;
|
||||||
|
mPipelineLayout = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MaterialResources& resources : mMaterialResources) {
|
||||||
|
if (resources.textureSet != nullptr) {
|
||||||
|
resources.textureSet->Shutdown();
|
||||||
|
delete resources.textureSet;
|
||||||
|
resources.textureSet = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resources.texturePool != nullptr) {
|
||||||
|
resources.texturePool->Shutdown();
|
||||||
|
delete resources.texturePool;
|
||||||
|
resources.texturePool = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resources.baseColorView != nullptr) {
|
||||||
|
resources.baseColorView->Shutdown();
|
||||||
|
delete resources.baseColorView;
|
||||||
|
resources.baseColorView = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resources.baseColorTexture != nullptr) {
|
||||||
|
resources.baseColorTexture->Shutdown();
|
||||||
|
delete resources.baseColorTexture;
|
||||||
|
resources.baseColorTexture = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resources.specularView != nullptr) {
|
||||||
|
resources.specularView->Shutdown();
|
||||||
|
delete resources.specularView;
|
||||||
|
resources.specularView = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resources.specularTexture != nullptr) {
|
||||||
|
resources.specularTexture->Shutdown();
|
||||||
|
delete resources.specularTexture;
|
||||||
|
resources.specularTexture = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mMaterialResources.clear();
|
||||||
|
|
||||||
|
if (mSamplerSet != nullptr) {
|
||||||
|
mSamplerSet->Shutdown();
|
||||||
|
delete mSamplerSet;
|
||||||
|
mSamplerSet = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mSamplerPool != nullptr) {
|
||||||
|
mSamplerPool->Shutdown();
|
||||||
|
delete mSamplerPool;
|
||||||
|
mSamplerPool = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mConstantSet != nullptr) {
|
||||||
|
mConstantSet->Shutdown();
|
||||||
|
delete mConstantSet;
|
||||||
|
mConstantSet = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mConstantPool != nullptr) {
|
||||||
|
mConstantPool->Shutdown();
|
||||||
|
delete mConstantPool;
|
||||||
|
mConstantPool = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mSampler != nullptr) {
|
||||||
|
mSampler->Shutdown();
|
||||||
|
delete mSampler;
|
||||||
|
mSampler = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mFallbackBaseColorView != nullptr) {
|
||||||
|
mFallbackBaseColorView->Shutdown();
|
||||||
|
delete mFallbackBaseColorView;
|
||||||
|
mFallbackBaseColorView = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mFallbackBaseColorTexture != nullptr) {
|
||||||
|
mFallbackBaseColorTexture->Shutdown();
|
||||||
|
delete mFallbackBaseColorTexture;
|
||||||
|
mFallbackBaseColorTexture = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mFallbackSpecularView != nullptr) {
|
||||||
|
mFallbackSpecularView->Shutdown();
|
||||||
|
delete mFallbackSpecularView;
|
||||||
|
mFallbackSpecularView = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mFallbackSpecularTexture != nullptr) {
|
||||||
|
mFallbackSpecularTexture->Shutdown();
|
||||||
|
delete mFallbackSpecularTexture;
|
||||||
|
mFallbackSpecularTexture = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mVertexBufferView != nullptr) {
|
||||||
|
mVertexBufferView->Shutdown();
|
||||||
|
delete mVertexBufferView;
|
||||||
|
mVertexBufferView = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIndexBufferView != nullptr) {
|
||||||
|
mIndexBufferView->Shutdown();
|
||||||
|
delete mIndexBufferView;
|
||||||
|
mIndexBufferView = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mVertexBuffer != nullptr) {
|
||||||
|
mVertexBuffer->Shutdown();
|
||||||
|
delete mVertexBuffer;
|
||||||
|
mVertexBuffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIndexBuffer != nullptr) {
|
||||||
|
mIndexBuffer->Shutdown();
|
||||||
|
delete mIndexBuffer;
|
||||||
|
mIndexBuffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
mVertices.clear();
|
||||||
|
|
||||||
|
if (mMesh != nullptr) {
|
||||||
|
delete mMesh;
|
||||||
|
mMesh = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackpackTest::RenderFrame() {
|
||||||
|
ASSERT_NE(mPipelineState, nullptr);
|
||||||
|
ASSERT_NE(mPipelineLayout, nullptr);
|
||||||
|
ASSERT_NE(mConstantSet, nullptr);
|
||||||
|
ASSERT_NE(mSamplerSet, nullptr);
|
||||||
|
ASSERT_NE(mVertexBufferView, nullptr);
|
||||||
|
ASSERT_NE(mIndexBufferView, nullptr);
|
||||||
|
ASSERT_NE(mMesh, nullptr);
|
||||||
|
ASSERT_FALSE(mMaterialResources.empty());
|
||||||
|
|
||||||
|
RHICommandList* cmdList = GetCommandList();
|
||||||
|
RHICommandQueue* cmdQueue = GetCommandQueue();
|
||||||
|
ASSERT_NE(cmdList, nullptr);
|
||||||
|
ASSERT_NE(cmdQueue, nullptr);
|
||||||
|
|
||||||
|
cmdList->Reset();
|
||||||
|
SetRenderTargetForClear(true);
|
||||||
|
|
||||||
|
Viewport viewport = { 0.0f, 0.0f, static_cast<float>(kFrameWidth), static_cast<float>(kFrameHeight), 0.0f, 1.0f };
|
||||||
|
Rect scissorRect = { 0, 0, static_cast<int32_t>(kFrameWidth), static_cast<int32_t>(kFrameHeight) };
|
||||||
|
cmdList->SetViewport(viewport);
|
||||||
|
cmdList->SetScissorRect(scissorRect);
|
||||||
|
cmdList->Clear(0.05f, 0.05f, 0.08f, 1.0f, 1 | 2);
|
||||||
|
|
||||||
|
cmdList->SetPipelineState(mPipelineState);
|
||||||
|
cmdList->SetPrimitiveTopology(PrimitiveTopology::TriangleList);
|
||||||
|
|
||||||
|
RHIResourceView* vertexBuffers[] = { mVertexBufferView };
|
||||||
|
uint64_t offsets[] = { 0 };
|
||||||
|
uint32_t strides[] = { sizeof(BackpackVertex) };
|
||||||
|
cmdList->SetVertexBuffers(0, 1, vertexBuffers, offsets, strides);
|
||||||
|
cmdList->SetIndexBuffer(mIndexBufferView, 0);
|
||||||
|
|
||||||
|
for (size_t sectionIndex = 0; sectionIndex < mMesh->GetSections().Size(); ++sectionIndex) {
|
||||||
|
const MeshSection& section = mMesh->GetSections()[sectionIndex];
|
||||||
|
const uint32_t materialIndex =
|
||||||
|
section.materialID < mMaterialResources.size()
|
||||||
|
? section.materialID
|
||||||
|
: 0u;
|
||||||
|
|
||||||
|
RHIDescriptorSet* descriptorSets[] = {
|
||||||
|
mConstantSet,
|
||||||
|
mMaterialResources[materialIndex].textureSet,
|
||||||
|
mSamplerSet
|
||||||
|
};
|
||||||
|
cmdList->SetGraphicsDescriptorSets(0, 3, descriptorSets, mPipelineLayout);
|
||||||
|
cmdList->DrawIndexed(section.indexCount, 1, section.startIndex, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
EndRender();
|
||||||
|
|
||||||
|
cmdList->Close();
|
||||||
|
void* cmdLists[] = { cmdList };
|
||||||
|
cmdQueue->ExecuteCommandLists(1, cmdLists);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(BackpackTest, RenderBackpack) {
|
||||||
|
RHICommandQueue* cmdQueue = GetCommandQueue();
|
||||||
|
RHISwapChain* swapChain = GetSwapChain();
|
||||||
|
ASSERT_NE(cmdQueue, nullptr);
|
||||||
|
ASSERT_NE(swapChain, nullptr);
|
||||||
|
|
||||||
|
const int targetFrameCount = 30;
|
||||||
|
const char* screenshotFilename = GetScreenshotFilename(GetBackendType());
|
||||||
|
const int comparisonThreshold = GetComparisonThreshold(GetBackendType());
|
||||||
|
|
||||||
|
for (int frameCount = 0; frameCount <= targetFrameCount; ++frameCount) {
|
||||||
|
if (frameCount > 0) {
|
||||||
|
cmdQueue->WaitForPreviousFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log("[TEST] Backpack MainLoop: frame %d", frameCount);
|
||||||
|
BeginRender();
|
||||||
|
RenderFrame();
|
||||||
|
|
||||||
|
if (frameCount >= targetFrameCount) {
|
||||||
|
cmdQueue->WaitForIdle();
|
||||||
|
Log("[TEST] Backpack MainLoop: frame %d reached, capturing %s", frameCount, screenshotFilename);
|
||||||
|
ASSERT_TRUE(TakeScreenshot(screenshotFilename));
|
||||||
|
ASSERT_TRUE(CompareWithGoldenTemplate(screenshotFilename, "GT.ppm", static_cast<float>(comparisonThreshold)));
|
||||||
|
Log("[TEST] Backpack MainLoop: frame %d compare passed", frameCount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
swapChain->Present(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(D3D12, BackpackTest, ::testing::Values(RHIType::D3D12));
|
||||||
|
INSTANTIATE_TEST_SUITE_P(OpenGL, BackpackTest, ::testing::Values(RHIType::OpenGL));
|
||||||
|
|
||||||
|
GTEST_API_ int main(int argc, char** argv) {
|
||||||
|
Logger::Get().Initialize();
|
||||||
|
Logger::Get().AddSink(std::make_unique<ConsoleLogSink>());
|
||||||
|
Logger::Get().SetMinimumLevel(LogLevel::Debug);
|
||||||
|
|
||||||
|
testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
@@ -215,6 +215,38 @@ TEST_P(RHITestFixture, CommandList_SetIndexBuffer_WithRealView) {
|
|||||||
delete buffer;
|
delete buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(RHITestFixture, CommandList_SetIndexBuffer_WithUint16View) {
|
||||||
|
BufferDesc bufferDesc = {};
|
||||||
|
bufferDesc.size = 256;
|
||||||
|
bufferDesc.stride = sizeof(uint16_t);
|
||||||
|
bufferDesc.bufferType = static_cast<uint32_t>(BufferType::Index);
|
||||||
|
|
||||||
|
RHIBuffer* buffer = GetDevice()->CreateBuffer(bufferDesc);
|
||||||
|
ASSERT_NE(buffer, nullptr);
|
||||||
|
|
||||||
|
ResourceViewDesc viewDesc = {};
|
||||||
|
viewDesc.dimension = ResourceViewDimension::Buffer;
|
||||||
|
viewDesc.format = static_cast<uint32_t>(Format::R16_UInt);
|
||||||
|
RHIResourceView* ibv = GetDevice()->CreateIndexBufferView(buffer, viewDesc);
|
||||||
|
ASSERT_NE(ibv, nullptr);
|
||||||
|
|
||||||
|
CommandListDesc cmdDesc = {};
|
||||||
|
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||||
|
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
|
||||||
|
ASSERT_NE(cmdList, nullptr);
|
||||||
|
|
||||||
|
cmdList->Reset();
|
||||||
|
cmdList->SetIndexBuffer(ibv, 0);
|
||||||
|
cmdList->Close();
|
||||||
|
|
||||||
|
cmdList->Shutdown();
|
||||||
|
delete cmdList;
|
||||||
|
ibv->Shutdown();
|
||||||
|
delete ibv;
|
||||||
|
buffer->Shutdown();
|
||||||
|
delete buffer;
|
||||||
|
}
|
||||||
|
|
||||||
TEST_P(RHITestFixture, CommandList_SetStencilRef) {
|
TEST_P(RHITestFixture, CommandList_SetStencilRef) {
|
||||||
CommandListDesc cmdDesc = {};
|
CommandListDesc cmdDesc = {};
|
||||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||||
|
|||||||
Reference in New Issue
Block a user