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::R8G8_UNorm: return DXGI_FORMAT_R8G8_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::R32G32B32A32_Float: return DXGI_FORMAT_R32G32B32A32_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_R8G8_UNORM: return Format::R8G8_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_R32G32B32A32_FLOAT: return Format::R32G32B32A32_Float;
|
||||
case DXGI_FORMAT_R16_FLOAT: return Format::R16_Float;
|
||||
|
||||
@@ -206,6 +206,8 @@ private:
|
||||
unsigned int m_currentVAO;
|
||||
unsigned int m_currentProgram;
|
||||
unsigned int m_internalVAO;
|
||||
unsigned int m_currentIndexType;
|
||||
uint64_t m_currentIndexOffset;
|
||||
OpenGLPipelineState* m_currentPipelineState;
|
||||
std::vector<unsigned int> m_enabledVertexAttributes;
|
||||
OpenGLShader* m_currentShader;
|
||||
|
||||
@@ -299,6 +299,7 @@ enum class Format : uint32_t {
|
||||
R8_UNorm,
|
||||
R8G8_UNorm,
|
||||
R8G8B8A8_UNorm,
|
||||
R16_UInt,
|
||||
R16G16B16A16_Float,
|
||||
R32G32B32A32_Float,
|
||||
R16_Float,
|
||||
|
||||
@@ -58,11 +58,35 @@ bool GetOpenGLVertexAttribFormat(Format format, OpenGLVertexAttribFormat& attrib
|
||||
case Format::R32G32B32A32_UInt:
|
||||
attributeFormat = { 4, GL_UNSIGNED_INT, GL_FALSE, true };
|
||||
return true;
|
||||
case Format::R16_UInt:
|
||||
attributeFormat = { 1, GL_UNSIGNED_SHORT, GL_FALSE, true };
|
||||
return true;
|
||||
default:
|
||||
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
|
||||
|
||||
OpenGLCommandList::OpenGLCommandList()
|
||||
@@ -70,6 +94,8 @@ OpenGLCommandList::OpenGLCommandList()
|
||||
, m_currentVAO(0)
|
||||
, m_currentProgram(0)
|
||||
, m_internalVAO(0)
|
||||
, m_currentIndexType(GL_UNSIGNED_INT)
|
||||
, m_currentIndexOffset(0)
|
||||
, m_currentPipelineState(nullptr)
|
||||
, m_currentShader(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) {
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
|
||||
SetIndexBuffer(buffer, type, 0);
|
||||
}
|
||||
|
||||
void OpenGLCommandList::SetIndexBuffer(unsigned int buffer, unsigned int type, size_t offset) {
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
|
||||
(void)offset;
|
||||
m_currentIndexType = type;
|
||||
m_currentIndexOffset = static_cast<uint64_t>(offset);
|
||||
}
|
||||
|
||||
void OpenGLCommandList::BindVertexArray(unsigned int vao) {
|
||||
@@ -137,7 +164,8 @@ void OpenGLCommandList::BindVertexArray(unsigned int vao, unsigned int indexBuff
|
||||
m_currentVAO = vao;
|
||||
glBindVertexArray(vao);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
(void)indexType;
|
||||
m_currentIndexType = indexType;
|
||||
m_currentIndexOffset = 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
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 OpenGLCommandList::DrawIndexedInstanced(PrimitiveType type, unsigned int indexCount, unsigned int instanceCount, unsigned int startIndex, int baseVertex, unsigned int startInstance) {
|
||||
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)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) {
|
||||
m_primitiveType = ToOpenGL(type);
|
||||
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);
|
||||
(void)drawCount;
|
||||
(void)stride;
|
||||
@@ -477,6 +514,8 @@ void OpenGLCommandList::Shutdown() {
|
||||
void OpenGLCommandList::Reset() {
|
||||
ReleaseComposedFramebuffer();
|
||||
m_currentPipelineState = nullptr;
|
||||
m_currentIndexOffset = 0;
|
||||
m_currentIndexType = GL_UNSIGNED_INT;
|
||||
}
|
||||
|
||||
void OpenGLCommandList::Close() {
|
||||
@@ -827,7 +866,8 @@ void OpenGLCommandList::SetIndexBuffer(RHIResourceView* buffer, uint64_t offset)
|
||||
|
||||
GLuint glBuffer = view->GetBuffer();
|
||||
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) {
|
||||
@@ -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) {
|
||||
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) {
|
||||
glDrawElementsInstanced(GL_TRIANGLES, static_cast<GLsizei>(indexCount), GL_UNSIGNED_INT,
|
||||
reinterpret_cast<const void*>(startIndex * sizeof(GLuint)), static_cast<GLsizei>(instanceCount));
|
||||
const uint64_t indexOffset = m_currentIndexOffset + static_cast<uint64_t>(startIndex) * GetIndexTypeSize(m_currentIndexType);
|
||||
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) {
|
||||
|
||||
@@ -8,3 +8,4 @@ add_subdirectory(minimal)
|
||||
add_subdirectory(triangle)
|
||||
add_subdirectory(quad)
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
CommandListDesc cmdDesc = {};
|
||||
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
|
||||
|
||||
Reference in New Issue
Block a user