refactor(tests): reorganize tests directory to match engine structure
- Created tests/Core/Asset/ with tests for IResource, ResourceTypes, ResourceHandle, ResourceCache, ResourceDependencyGraph, ResourceGUID
- Created tests/Core/IO/ with tests for IResourceLoader, ResourcePath, ResourceFileSystem, FileArchive, ResourcePackage
- Reorganized tests/Resources/ into subdirectories: Texture/, Mesh/, Material/, Shader/, AudioClip/
- Added CMakeLists.txt for each new test subdirectory
- Fixed Material.h missing ResourceManager.h include (lost during engine refactor)
- tests/Core/CMakeLists.txt updated to include Asset/ and IO/ subdirectories
2026-03-24 15:44:13 +08:00
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
#include <XCEngine/Resources/Material/Material.h>
|
|
|
|
|
#include <XCEngine/Resources/Shader/Shader.h>
|
|
|
|
|
#include <XCEngine/Resources/Texture/Texture.h>
|
|
|
|
|
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
|
|
|
|
#include <XCEngine/Core/Math/Vector2.h>
|
|
|
|
|
#include <XCEngine/Core/Math/Vector3.h>
|
|
|
|
|
#include <XCEngine/Core/Math/Vector4.h>
|
2026-04-08 19:18:07 +08:00
|
|
|
#include <XCEngine/RHI/RHIBuffer.h>
|
refactor(tests): reorganize tests directory to match engine structure
- Created tests/Core/Asset/ with tests for IResource, ResourceTypes, ResourceHandle, ResourceCache, ResourceDependencyGraph, ResourceGUID
- Created tests/Core/IO/ with tests for IResourceLoader, ResourcePath, ResourceFileSystem, FileArchive, ResourcePackage
- Reorganized tests/Resources/ into subdirectories: Texture/, Mesh/, Material/, Shader/, AudioClip/
- Added CMakeLists.txt for each new test subdirectory
- Fixed Material.h missing ResourceManager.h include (lost during engine refactor)
- tests/Core/CMakeLists.txt updated to include Asset/ and IO/ subdirectories
2026-03-24 15:44:13 +08:00
|
|
|
|
2026-04-06 18:55:26 +08:00
|
|
|
#include <initializer_list>
|
2026-04-08 19:18:07 +08:00
|
|
|
#include <string>
|
2026-04-06 18:55:26 +08:00
|
|
|
|
refactor(tests): reorganize tests directory to match engine structure
- Created tests/Core/Asset/ with tests for IResource, ResourceTypes, ResourceHandle, ResourceCache, ResourceDependencyGraph, ResourceGUID
- Created tests/Core/IO/ with tests for IResourceLoader, ResourcePath, ResourceFileSystem, FileArchive, ResourcePackage
- Reorganized tests/Resources/ into subdirectories: Texture/, Mesh/, Material/, Shader/, AudioClip/
- Added CMakeLists.txt for each new test subdirectory
- Fixed Material.h missing ResourceManager.h include (lost during engine refactor)
- tests/Core/CMakeLists.txt updated to include Asset/ and IO/ subdirectories
2026-03-24 15:44:13 +08:00
|
|
|
using namespace XCEngine::Resources;
|
|
|
|
|
using namespace XCEngine::Math;
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2026-04-08 19:18:07 +08:00
|
|
|
class MockMaterialBuffer final : public XCEngine::RHI::RHIBuffer {
|
|
|
|
|
public:
|
|
|
|
|
explicit MockMaterialBuffer(
|
|
|
|
|
uint64_t size = 256u,
|
|
|
|
|
uint32_t stride = 16u,
|
|
|
|
|
XCEngine::RHI::BufferType type = XCEngine::RHI::BufferType::Storage)
|
|
|
|
|
: m_size(size)
|
|
|
|
|
, m_stride(stride)
|
|
|
|
|
, m_type(type) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void* Map() override { return nullptr; }
|
|
|
|
|
void Unmap() override {}
|
|
|
|
|
void SetData(const void*, size_t, size_t) override {}
|
|
|
|
|
uint64_t GetSize() const override { return m_size; }
|
|
|
|
|
XCEngine::RHI::BufferType GetBufferType() const override { return m_type; }
|
|
|
|
|
void SetBufferType(XCEngine::RHI::BufferType type) override { m_type = type; }
|
|
|
|
|
uint32_t GetStride() const override { return m_stride; }
|
|
|
|
|
void SetStride(uint32_t stride) override { m_stride = stride; }
|
|
|
|
|
void* GetNativeHandle() override { return nullptr; }
|
|
|
|
|
XCEngine::RHI::ResourceStates GetState() const override { return m_state; }
|
|
|
|
|
void SetState(XCEngine::RHI::ResourceStates state) override { m_state = state; }
|
|
|
|
|
const std::string& GetName() const override { return m_name; }
|
|
|
|
|
void SetName(const std::string& name) override { m_name = name; }
|
|
|
|
|
void Shutdown() override {}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint64_t m_size = 0;
|
|
|
|
|
uint32_t m_stride = 0;
|
|
|
|
|
XCEngine::RHI::BufferType m_type = XCEngine::RHI::BufferType::Storage;
|
|
|
|
|
XCEngine::RHI::ResourceStates m_state = XCEngine::RHI::ResourceStates::Common;
|
|
|
|
|
std::string m_name;
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-03 16:49:30 +08:00
|
|
|
Shader* CreateMaterialSchemaShader() {
|
|
|
|
|
auto* shader = new Shader();
|
|
|
|
|
|
|
|
|
|
ShaderPropertyDesc baseColor = {};
|
|
|
|
|
baseColor.name = "_BaseColor";
|
|
|
|
|
baseColor.displayName = "Base Color";
|
|
|
|
|
baseColor.type = ShaderPropertyType::Color;
|
|
|
|
|
baseColor.defaultValue = "(1.0,0.5,0.25,1.0)";
|
|
|
|
|
baseColor.semantic = "BaseColor";
|
|
|
|
|
shader->AddProperty(baseColor);
|
|
|
|
|
|
|
|
|
|
ShaderPropertyDesc metallic = {};
|
|
|
|
|
metallic.name = "_Metallic";
|
|
|
|
|
metallic.displayName = "Metallic";
|
|
|
|
|
metallic.type = ShaderPropertyType::Float;
|
|
|
|
|
metallic.defaultValue = "0.7";
|
|
|
|
|
shader->AddProperty(metallic);
|
|
|
|
|
|
|
|
|
|
ShaderPropertyDesc baseMap = {};
|
|
|
|
|
baseMap.name = "_MainTex";
|
|
|
|
|
baseMap.displayName = "Base Map";
|
|
|
|
|
baseMap.type = ShaderPropertyType::Texture2D;
|
|
|
|
|
baseMap.defaultValue = "white";
|
|
|
|
|
baseMap.semantic = "BaseColorTexture";
|
|
|
|
|
shader->AddProperty(baseMap);
|
|
|
|
|
|
|
|
|
|
return shader;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 18:55:26 +08:00
|
|
|
Shader* CreateMaterialKeywordShader(std::initializer_list<const char*> keywords) {
|
|
|
|
|
auto* shader = new Shader();
|
|
|
|
|
ShaderPass pass = {};
|
|
|
|
|
pass.name = "ForwardLit";
|
|
|
|
|
shader->AddPass(pass);
|
|
|
|
|
|
|
|
|
|
ShaderKeywordDeclaration declaration = {};
|
|
|
|
|
declaration.type = ShaderKeywordDeclarationType::ShaderFeatureLocal;
|
|
|
|
|
declaration.options.PushBack("_");
|
|
|
|
|
for (const char* keyword : keywords) {
|
|
|
|
|
declaration.options.PushBack(keyword);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
shader->AddPassKeywordDeclaration("ForwardLit", declaration);
|
|
|
|
|
return shader;
|
|
|
|
|
}
|
|
|
|
|
|
refactor(tests): reorganize tests directory to match engine structure
- Created tests/Core/Asset/ with tests for IResource, ResourceTypes, ResourceHandle, ResourceCache, ResourceDependencyGraph, ResourceGUID
- Created tests/Core/IO/ with tests for IResourceLoader, ResourcePath, ResourceFileSystem, FileArchive, ResourcePackage
- Reorganized tests/Resources/ into subdirectories: Texture/, Mesh/, Material/, Shader/, AudioClip/
- Added CMakeLists.txt for each new test subdirectory
- Fixed Material.h missing ResourceManager.h include (lost during engine refactor)
- tests/Core/CMakeLists.txt updated to include Asset/ and IO/ subdirectories
2026-03-24 15:44:13 +08:00
|
|
|
TEST(Material, DefaultConstructor) {
|
|
|
|
|
Material material;
|
|
|
|
|
EXPECT_EQ(material.GetType(), ResourceType::Material);
|
|
|
|
|
EXPECT_FALSE(material.IsValid());
|
|
|
|
|
EXPECT_EQ(material.GetMemorySize(), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, GetType) {
|
|
|
|
|
Material material;
|
|
|
|
|
EXPECT_EQ(material.GetType(), ResourceType::Material);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetGetShader) {
|
|
|
|
|
Material material;
|
|
|
|
|
Shader* shader = new Shader();
|
|
|
|
|
|
|
|
|
|
ResourceHandle<Shader> handle(shader);
|
|
|
|
|
material.SetShader(handle);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(material.GetShader(), shader);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 00:30:49 +08:00
|
|
|
TEST(Material, DefaultRenderMetadata) {
|
|
|
|
|
Material material;
|
|
|
|
|
EXPECT_EQ(material.GetRenderQueue(), static_cast<XCEngine::Core::int32>(MaterialRenderQueue::Geometry));
|
2026-03-27 12:18:04 +08:00
|
|
|
EXPECT_EQ(material.GetRenderState().cullMode, MaterialCullMode::None);
|
|
|
|
|
EXPECT_FALSE(material.GetRenderState().blendEnable);
|
|
|
|
|
EXPECT_TRUE(material.GetRenderState().depthTestEnable);
|
|
|
|
|
EXPECT_TRUE(material.GetRenderState().depthWriteEnable);
|
|
|
|
|
EXPECT_EQ(material.GetRenderState().depthFunc, MaterialComparisonFunc::Less);
|
2026-03-27 00:30:49 +08:00
|
|
|
EXPECT_EQ(material.GetTagCount(), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetGetRenderQueue) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
material.SetRenderQueue(MaterialRenderQueue::Transparent);
|
|
|
|
|
EXPECT_EQ(material.GetRenderQueue(), static_cast<XCEngine::Core::int32>(MaterialRenderQueue::Transparent));
|
|
|
|
|
|
|
|
|
|
material.SetRenderQueue(2600);
|
|
|
|
|
EXPECT_EQ(material.GetRenderQueue(), 2600);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 12:18:04 +08:00
|
|
|
TEST(Material, SetGetRenderState) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
MaterialRenderState renderState;
|
|
|
|
|
renderState.cullMode = MaterialCullMode::Back;
|
|
|
|
|
renderState.blendEnable = true;
|
|
|
|
|
renderState.srcBlend = MaterialBlendFactor::SrcAlpha;
|
|
|
|
|
renderState.dstBlend = MaterialBlendFactor::InvSrcAlpha;
|
|
|
|
|
renderState.srcBlendAlpha = MaterialBlendFactor::One;
|
|
|
|
|
renderState.dstBlendAlpha = MaterialBlendFactor::InvSrcAlpha;
|
|
|
|
|
renderState.blendOp = MaterialBlendOp::Add;
|
|
|
|
|
renderState.blendOpAlpha = MaterialBlendOp::Add;
|
|
|
|
|
renderState.depthTestEnable = true;
|
|
|
|
|
renderState.depthWriteEnable = false;
|
|
|
|
|
renderState.depthFunc = MaterialComparisonFunc::LessEqual;
|
|
|
|
|
renderState.colorWriteMask = 0x7;
|
2026-04-08 16:09:15 +08:00
|
|
|
renderState.depthBiasFactor = 1.5f;
|
|
|
|
|
renderState.depthBiasUnits = 2;
|
|
|
|
|
renderState.stencil.enabled = true;
|
|
|
|
|
renderState.stencil.reference = 3;
|
|
|
|
|
renderState.stencil.readMask = 0x0F;
|
|
|
|
|
renderState.stencil.writeMask = 0xF0;
|
|
|
|
|
renderState.stencil.front.func = MaterialComparisonFunc::Equal;
|
|
|
|
|
renderState.stencil.front.passOp = MaterialStencilOp::Replace;
|
|
|
|
|
renderState.stencil.front.failOp = MaterialStencilOp::Keep;
|
|
|
|
|
renderState.stencil.front.depthFailOp = MaterialStencilOp::IncrSat;
|
|
|
|
|
renderState.stencil.back.func = MaterialComparisonFunc::NotEqual;
|
|
|
|
|
renderState.stencil.back.passOp = MaterialStencilOp::DecrWrap;
|
|
|
|
|
renderState.stencil.back.failOp = MaterialStencilOp::Invert;
|
|
|
|
|
renderState.stencil.back.depthFailOp = MaterialStencilOp::Zero;
|
2026-03-27 12:18:04 +08:00
|
|
|
|
|
|
|
|
material.SetRenderState(renderState);
|
|
|
|
|
|
|
|
|
|
const MaterialRenderState& result = material.GetRenderState();
|
|
|
|
|
EXPECT_EQ(result.cullMode, MaterialCullMode::Back);
|
|
|
|
|
EXPECT_TRUE(result.blendEnable);
|
|
|
|
|
EXPECT_EQ(result.srcBlend, MaterialBlendFactor::SrcAlpha);
|
|
|
|
|
EXPECT_EQ(result.dstBlend, MaterialBlendFactor::InvSrcAlpha);
|
|
|
|
|
EXPECT_EQ(result.srcBlendAlpha, MaterialBlendFactor::One);
|
|
|
|
|
EXPECT_EQ(result.dstBlendAlpha, MaterialBlendFactor::InvSrcAlpha);
|
|
|
|
|
EXPECT_EQ(result.blendOp, MaterialBlendOp::Add);
|
|
|
|
|
EXPECT_EQ(result.blendOpAlpha, MaterialBlendOp::Add);
|
|
|
|
|
EXPECT_TRUE(result.depthTestEnable);
|
|
|
|
|
EXPECT_FALSE(result.depthWriteEnable);
|
|
|
|
|
EXPECT_EQ(result.depthFunc, MaterialComparisonFunc::LessEqual);
|
|
|
|
|
EXPECT_EQ(result.colorWriteMask, 0x7);
|
2026-04-08 16:09:15 +08:00
|
|
|
EXPECT_FLOAT_EQ(result.depthBiasFactor, 1.5f);
|
|
|
|
|
EXPECT_EQ(result.depthBiasUnits, 2);
|
|
|
|
|
EXPECT_TRUE(result.stencil.enabled);
|
|
|
|
|
EXPECT_EQ(result.stencil.reference, 3u);
|
|
|
|
|
EXPECT_EQ(result.stencil.readMask, 0x0Fu);
|
|
|
|
|
EXPECT_EQ(result.stencil.writeMask, 0xF0u);
|
|
|
|
|
EXPECT_EQ(result.stencil.front.func, MaterialComparisonFunc::Equal);
|
|
|
|
|
EXPECT_EQ(result.stencil.front.passOp, MaterialStencilOp::Replace);
|
|
|
|
|
EXPECT_EQ(result.stencil.front.depthFailOp, MaterialStencilOp::IncrSat);
|
|
|
|
|
EXPECT_EQ(result.stencil.back.func, MaterialComparisonFunc::NotEqual);
|
|
|
|
|
EXPECT_EQ(result.stencil.back.passOp, MaterialStencilOp::DecrWrap);
|
|
|
|
|
EXPECT_EQ(result.stencil.back.failOp, MaterialStencilOp::Invert);
|
|
|
|
|
EXPECT_EQ(result.stencil.back.depthFailOp, MaterialStencilOp::Zero);
|
2026-03-27 00:30:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetGetTags) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
material.SetTag("LightMode", "ForwardBase");
|
|
|
|
|
material.SetTag("RenderType", "Opaque");
|
|
|
|
|
|
|
|
|
|
EXPECT_TRUE(material.HasTag("LightMode"));
|
|
|
|
|
EXPECT_EQ(material.GetTag("LightMode"), "ForwardBase");
|
|
|
|
|
EXPECT_EQ(material.GetTag("RenderType"), "Opaque");
|
|
|
|
|
EXPECT_EQ(material.GetTagCount(), 2u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetTagReplacesExistingValue) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
material.SetTag("LightMode", "ForwardBase");
|
|
|
|
|
material.SetTag("LightMode", "ShadowCaster");
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(material.GetTagCount(), 1u);
|
|
|
|
|
EXPECT_EQ(material.GetTag("LightMode"), "ShadowCaster");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, RemoveTag) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
material.SetTag("LightMode", "ForwardBase");
|
|
|
|
|
EXPECT_TRUE(material.HasTag("LightMode"));
|
|
|
|
|
|
|
|
|
|
material.RemoveTag("LightMode");
|
|
|
|
|
EXPECT_FALSE(material.HasTag("LightMode"));
|
|
|
|
|
EXPECT_TRUE(material.GetTag("LightMode").Empty());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, ClearTags) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
material.SetTag("LightMode", "ForwardBase");
|
|
|
|
|
material.SetTag("RenderType", "Opaque");
|
|
|
|
|
ASSERT_EQ(material.GetTagCount(), 2u);
|
|
|
|
|
|
|
|
|
|
material.ClearTags();
|
|
|
|
|
EXPECT_EQ(material.GetTagCount(), 0u);
|
|
|
|
|
EXPECT_FALSE(material.HasTag("LightMode"));
|
|
|
|
|
EXPECT_FALSE(material.HasTag("RenderType"));
|
|
|
|
|
}
|
|
|
|
|
|
refactor(tests): reorganize tests directory to match engine structure
- Created tests/Core/Asset/ with tests for IResource, ResourceTypes, ResourceHandle, ResourceCache, ResourceDependencyGraph, ResourceGUID
- Created tests/Core/IO/ with tests for IResourceLoader, ResourcePath, ResourceFileSystem, FileArchive, ResourcePackage
- Reorganized tests/Resources/ into subdirectories: Texture/, Mesh/, Material/, Shader/, AudioClip/
- Added CMakeLists.txt for each new test subdirectory
- Fixed Material.h missing ResourceManager.h include (lost during engine refactor)
- tests/Core/CMakeLists.txt updated to include Asset/ and IO/ subdirectories
2026-03-24 15:44:13 +08:00
|
|
|
TEST(Material, SetGetFloat) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
material.SetFloat("uTime", 1.5f);
|
|
|
|
|
EXPECT_FLOAT_EQ(material.GetFloat("uTime"), 1.5f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetGetFloat2) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
Vector2 value(1.0f, 2.0f);
|
|
|
|
|
material.SetFloat2("uUV", value);
|
|
|
|
|
|
|
|
|
|
Vector2 result = material.GetFloat2("uUV");
|
|
|
|
|
EXPECT_FLOAT_EQ(result.x, 1.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(result.y, 2.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetGetFloat3) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
Vector3 value(1.0f, 2.0f, 3.0f);
|
|
|
|
|
material.SetFloat3("uPosition", value);
|
|
|
|
|
|
|
|
|
|
Vector3 result = material.GetFloat3("uPosition");
|
|
|
|
|
EXPECT_FLOAT_EQ(result.x, 1.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(result.y, 2.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(result.z, 3.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetGetFloat4) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
Vector4 value(1.0f, 2.0f, 3.0f, 4.0f);
|
|
|
|
|
material.SetFloat4("uColor", value);
|
|
|
|
|
|
|
|
|
|
Vector4 result = material.GetFloat4("uColor");
|
|
|
|
|
EXPECT_FLOAT_EQ(result.x, 1.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(result.y, 2.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(result.z, 3.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(result.w, 4.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetGetInt) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
material.SetInt("uIndex", 42);
|
|
|
|
|
EXPECT_EQ(material.GetInt("uIndex"), 42);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetGetBool) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
material.SetBool("uEnabled", true);
|
|
|
|
|
EXPECT_TRUE(material.GetBool("uEnabled"));
|
|
|
|
|
|
|
|
|
|
material.SetBool("uEnabled", false);
|
|
|
|
|
EXPECT_FALSE(material.GetBool("uEnabled"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetGetTexture) {
|
|
|
|
|
Material material;
|
|
|
|
|
Texture* texture = new Texture();
|
|
|
|
|
|
|
|
|
|
ResourceHandle<Texture> handle(texture);
|
|
|
|
|
material.SetTexture("uDiffuse", handle);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(material.GetTexture("uDiffuse").Get(), texture);
|
2026-03-26 16:22:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetTextureReplacesExistingBinding) {
|
|
|
|
|
Material material;
|
|
|
|
|
Texture* firstTexture = new Texture();
|
|
|
|
|
Texture* secondTexture = new Texture();
|
|
|
|
|
|
|
|
|
|
material.SetTexture("uDiffuse", ResourceHandle<Texture>(firstTexture));
|
|
|
|
|
material.SetTexture("uDiffuse", ResourceHandle<Texture>(secondTexture));
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(material.GetTextureBindingCount(), 1u);
|
|
|
|
|
EXPECT_EQ(material.GetTexture("uDiffuse").Get(), secondTexture);
|
refactor(tests): reorganize tests directory to match engine structure
- Created tests/Core/Asset/ with tests for IResource, ResourceTypes, ResourceHandle, ResourceCache, ResourceDependencyGraph, ResourceGUID
- Created tests/Core/IO/ with tests for IResourceLoader, ResourcePath, ResourceFileSystem, FileArchive, ResourcePackage
- Reorganized tests/Resources/ into subdirectories: Texture/, Mesh/, Material/, Shader/, AudioClip/
- Added CMakeLists.txt for each new test subdirectory
- Fixed Material.h missing ResourceManager.h include (lost during engine refactor)
- tests/Core/CMakeLists.txt updated to include Asset/ and IO/ subdirectories
2026-03-24 15:44:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-03 16:49:30 +08:00
|
|
|
TEST(Material, SetTextureAssetRefStoresStableBindingMetadata) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
AssetRef textureRef;
|
|
|
|
|
textureRef.assetGuid = AssetGUID(1, 2);
|
|
|
|
|
textureRef.localID = kMainAssetLocalID;
|
|
|
|
|
textureRef.resourceType = ResourceType::Texture;
|
|
|
|
|
|
|
|
|
|
material.SetTextureAssetRef("uDiffuse", textureRef, "Assets/diffuse.bmp");
|
|
|
|
|
|
|
|
|
|
ASSERT_EQ(material.GetTextureBindingCount(), 1u);
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("uDiffuse"));
|
|
|
|
|
EXPECT_EQ(material.GetTextureBindingName(0), "uDiffuse");
|
|
|
|
|
EXPECT_EQ(material.GetTextureBindingPath(0), "Assets/diffuse.bmp");
|
|
|
|
|
|
|
|
|
|
const AssetRef storedRef = material.GetTextureBindingAssetRef(0);
|
|
|
|
|
EXPECT_EQ(storedRef.assetGuid, textureRef.assetGuid);
|
|
|
|
|
EXPECT_EQ(storedRef.localID, textureRef.localID);
|
|
|
|
|
EXPECT_EQ(storedRef.resourceType, textureRef.resourceType);
|
|
|
|
|
EXPECT_FALSE(material.GetTextureBindingLoadedTexture(0).IsValid());
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 17:13:53 +08:00
|
|
|
TEST(Material, ChangeVersionIncrementsWhenMaterialMutates) {
|
|
|
|
|
Material material;
|
|
|
|
|
const XCEngine::Core::uint64 initialVersion = material.GetChangeVersion();
|
|
|
|
|
|
|
|
|
|
material.SetFloat("uTime", 1.0f);
|
|
|
|
|
const XCEngine::Core::uint64 afterFloatVersion = material.GetChangeVersion();
|
|
|
|
|
EXPECT_GT(afterFloatVersion, initialVersion);
|
|
|
|
|
|
|
|
|
|
material.SetTexture("uDiffuse", ResourceHandle<Texture>(new Texture()));
|
|
|
|
|
EXPECT_GT(material.GetChangeVersion(), afterFloatVersion);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-08 19:18:07 +08:00
|
|
|
TEST(Material, SetBufferStoresRuntimeOnlyBindingMetadata) {
|
|
|
|
|
Material material;
|
|
|
|
|
MockMaterialBuffer buffer(512u, 32u);
|
|
|
|
|
MaterialBufferBindingViewDesc viewDesc = {};
|
|
|
|
|
viewDesc.firstElement = 4u;
|
|
|
|
|
viewDesc.elementCount = 8u;
|
|
|
|
|
viewDesc.structureByteStride = 32u;
|
|
|
|
|
|
|
|
|
|
material.SetBuffer("VolumeNodes", &buffer, viewDesc);
|
|
|
|
|
|
|
|
|
|
ASSERT_EQ(material.GetBufferBindingCount(), 1u);
|
|
|
|
|
EXPECT_EQ(material.GetBuffer("VolumeNodes"), &buffer);
|
|
|
|
|
const MaterialBufferBinding* binding = material.FindBufferBinding("VolumeNodes");
|
|
|
|
|
ASSERT_NE(binding, nullptr);
|
|
|
|
|
EXPECT_EQ(binding->name, "VolumeNodes");
|
|
|
|
|
EXPECT_EQ(binding->buffer, &buffer);
|
|
|
|
|
EXPECT_EQ(binding->viewDesc.firstElement, 4u);
|
|
|
|
|
EXPECT_EQ(binding->viewDesc.elementCount, 8u);
|
|
|
|
|
EXPECT_EQ(binding->viewDesc.structureByteStride, 32u);
|
|
|
|
|
EXPECT_FALSE(material.HasProperty("VolumeNodes"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SetBufferNullRemovesRuntimeBinding) {
|
|
|
|
|
Material material;
|
|
|
|
|
MockMaterialBuffer buffer;
|
|
|
|
|
|
|
|
|
|
material.SetBuffer("VolumeNodes", &buffer);
|
|
|
|
|
ASSERT_EQ(material.GetBufferBindingCount(), 1u);
|
|
|
|
|
|
|
|
|
|
material.SetBuffer("VolumeNodes", nullptr);
|
|
|
|
|
EXPECT_EQ(material.GetBufferBindingCount(), 0u);
|
|
|
|
|
EXPECT_EQ(material.GetBuffer("VolumeNodes"), nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, ClearAllPropertiesDoesNotRemoveRuntimeBufferBindings) {
|
|
|
|
|
Material material;
|
|
|
|
|
MockMaterialBuffer buffer;
|
|
|
|
|
|
|
|
|
|
material.SetFloat("uTime", 1.0f);
|
|
|
|
|
material.SetBuffer("VolumeNodes", &buffer);
|
|
|
|
|
ASSERT_TRUE(material.HasProperty("uTime"));
|
|
|
|
|
ASSERT_EQ(material.GetBufferBindingCount(), 1u);
|
|
|
|
|
|
|
|
|
|
material.ClearAllProperties();
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(material.HasProperty("uTime"));
|
|
|
|
|
EXPECT_EQ(material.GetBufferBindingCount(), 1u);
|
|
|
|
|
EXPECT_EQ(material.GetBuffer("VolumeNodes"), &buffer);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 17:13:53 +08:00
|
|
|
TEST(Material, UpdateConstantBufferPacksNumericPropertiesIntoStableSlots) {
|
|
|
|
|
Material material;
|
|
|
|
|
material.SetFloat("alpha", 3.5f);
|
|
|
|
|
material.SetFloat4("beta", Vector4(1.0f, 2.0f, 3.0f, 4.0f));
|
|
|
|
|
material.SetInt("gamma", 7);
|
|
|
|
|
|
2026-04-03 16:49:30 +08:00
|
|
|
const auto& constantLayout = material.GetConstantLayout();
|
|
|
|
|
ASSERT_EQ(constantLayout.Size(), 3u);
|
|
|
|
|
EXPECT_EQ(constantLayout[0].name, "alpha");
|
|
|
|
|
EXPECT_EQ(constantLayout[0].offset, 0u);
|
|
|
|
|
EXPECT_EQ(constantLayout[0].size, 4u);
|
|
|
|
|
EXPECT_EQ(constantLayout[0].alignedSize, 16u);
|
|
|
|
|
EXPECT_EQ(constantLayout[1].name, "beta");
|
|
|
|
|
EXPECT_EQ(constantLayout[1].offset, 16u);
|
|
|
|
|
EXPECT_EQ(constantLayout[1].size, 16u);
|
|
|
|
|
EXPECT_EQ(constantLayout[2].name, "gamma");
|
|
|
|
|
EXPECT_EQ(constantLayout[2].offset, 32u);
|
|
|
|
|
EXPECT_EQ(constantLayout[2].size, 4u);
|
|
|
|
|
|
|
|
|
|
const MaterialConstantFieldDesc* betaField = material.FindConstantField("beta");
|
|
|
|
|
ASSERT_NE(betaField, nullptr);
|
|
|
|
|
EXPECT_EQ(betaField->offset, 16u);
|
|
|
|
|
EXPECT_EQ(betaField->alignedSize, 16u);
|
|
|
|
|
|
2026-04-02 17:13:53 +08:00
|
|
|
const auto& constantBufferData = material.GetConstantBufferData();
|
|
|
|
|
ASSERT_EQ(constantBufferData.Size(), 48u);
|
|
|
|
|
|
|
|
|
|
const float* alphaSlot = reinterpret_cast<const float*>(constantBufferData.Data());
|
|
|
|
|
EXPECT_FLOAT_EQ(alphaSlot[0], 3.5f);
|
|
|
|
|
EXPECT_FLOAT_EQ(alphaSlot[1], 0.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(alphaSlot[2], 0.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(alphaSlot[3], 0.0f);
|
|
|
|
|
|
|
|
|
|
const float* betaSlot = reinterpret_cast<const float*>(constantBufferData.Data() + 16);
|
|
|
|
|
EXPECT_FLOAT_EQ(betaSlot[0], 1.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(betaSlot[1], 2.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(betaSlot[2], 3.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(betaSlot[3], 4.0f);
|
|
|
|
|
|
|
|
|
|
const XCEngine::Core::int32* gammaSlot =
|
|
|
|
|
reinterpret_cast<const XCEngine::Core::int32*>(constantBufferData.Data() + 32);
|
|
|
|
|
EXPECT_EQ(gammaSlot[0], 7);
|
|
|
|
|
EXPECT_EQ(gammaSlot[1], 0);
|
|
|
|
|
EXPECT_EQ(gammaSlot[2], 0);
|
|
|
|
|
EXPECT_EQ(gammaSlot[3], 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, RemoveTexturePropertyAlsoRemovesTextureBinding) {
|
|
|
|
|
Material material;
|
|
|
|
|
Texture* texture = new Texture();
|
|
|
|
|
|
|
|
|
|
material.SetTexture("uDiffuse", ResourceHandle<Texture>(texture));
|
|
|
|
|
ASSERT_EQ(material.GetTextureBindingCount(), 1u);
|
|
|
|
|
|
|
|
|
|
material.RemoveProperty("uDiffuse");
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(material.HasProperty("uDiffuse"));
|
|
|
|
|
EXPECT_EQ(material.GetTextureBindingCount(), 0u);
|
|
|
|
|
EXPECT_EQ(material.GetTexture("uDiffuse").Get(), nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, ReplacingTexturePropertyWithScalarRemovesTextureBinding) {
|
|
|
|
|
Material material;
|
|
|
|
|
Texture* texture = new Texture();
|
|
|
|
|
|
|
|
|
|
material.SetTexture("uMain", ResourceHandle<Texture>(texture));
|
|
|
|
|
ASSERT_EQ(material.GetTextureBindingCount(), 1u);
|
|
|
|
|
|
|
|
|
|
material.SetFloat("uMain", 2.0f);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(material.GetTextureBindingCount(), 0u);
|
|
|
|
|
EXPECT_EQ(material.GetTexture("uMain").Get(), nullptr);
|
|
|
|
|
EXPECT_FLOAT_EQ(material.GetFloat("uMain"), 2.0f);
|
|
|
|
|
}
|
|
|
|
|
|
refactor(tests): reorganize tests directory to match engine structure
- Created tests/Core/Asset/ with tests for IResource, ResourceTypes, ResourceHandle, ResourceCache, ResourceDependencyGraph, ResourceGUID
- Created tests/Core/IO/ with tests for IResourceLoader, ResourcePath, ResourceFileSystem, FileArchive, ResourcePackage
- Reorganized tests/Resources/ into subdirectories: Texture/, Mesh/, Material/, Shader/, AudioClip/
- Added CMakeLists.txt for each new test subdirectory
- Fixed Material.h missing ResourceManager.h include (lost during engine refactor)
- tests/Core/CMakeLists.txt updated to include Asset/ and IO/ subdirectories
2026-03-24 15:44:13 +08:00
|
|
|
TEST(Material, HasProperty) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(material.HasProperty("uTime"));
|
|
|
|
|
|
|
|
|
|
material.SetFloat("uTime", 1.0f);
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("uTime"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, RemoveProperty) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
material.SetFloat("uTime", 1.0f);
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("uTime"));
|
|
|
|
|
|
|
|
|
|
material.RemoveProperty("uTime");
|
|
|
|
|
EXPECT_FALSE(material.HasProperty("uTime"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, ClearAllProperties) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
material.SetFloat("uTime", 1.0f);
|
|
|
|
|
material.SetFloat3("uPosition", Vector3(1.0f, 2.0f, 3.0f));
|
|
|
|
|
material.SetInt("uIndex", 1);
|
|
|
|
|
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("uTime"));
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("uPosition"));
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("uIndex"));
|
|
|
|
|
|
|
|
|
|
material.ClearAllProperties();
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(material.HasProperty("uTime"));
|
|
|
|
|
EXPECT_FALSE(material.HasProperty("uPosition"));
|
|
|
|
|
EXPECT_FALSE(material.HasProperty("uIndex"));
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 16:49:30 +08:00
|
|
|
TEST(Material, SetShaderSeedsDefaultsAndRemovePropertyRestoresShaderDefault) {
|
|
|
|
|
Material material;
|
|
|
|
|
Shader* shader = CreateMaterialSchemaShader();
|
|
|
|
|
|
|
|
|
|
material.SetShader(ResourceHandle<Shader>(shader));
|
|
|
|
|
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("_BaseColor"));
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("_Metallic"));
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("_MainTex"));
|
|
|
|
|
EXPECT_EQ(material.GetFloat4("_BaseColor"), Vector4(1.0f, 0.5f, 0.25f, 1.0f));
|
|
|
|
|
EXPECT_FLOAT_EQ(material.GetFloat("_Metallic"), 0.7f);
|
|
|
|
|
EXPECT_EQ(material.GetTextureBindingCount(), 0u);
|
|
|
|
|
|
|
|
|
|
material.SetFloat4("_BaseColor", Vector4(0.2f, 0.3f, 0.4f, 0.5f));
|
|
|
|
|
EXPECT_EQ(material.GetFloat4("_BaseColor"), Vector4(0.2f, 0.3f, 0.4f, 0.5f));
|
|
|
|
|
|
|
|
|
|
material.RemoveProperty("_BaseColor");
|
|
|
|
|
EXPECT_EQ(material.GetFloat4("_BaseColor"), Vector4(1.0f, 0.5f, 0.25f, 1.0f));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, ClearAllPropertiesWithShaderRestoresSchemaDefaults) {
|
|
|
|
|
Material material;
|
|
|
|
|
Shader* shader = CreateMaterialSchemaShader();
|
|
|
|
|
|
|
|
|
|
material.SetShader(ResourceHandle<Shader>(shader));
|
|
|
|
|
material.SetFloat("_Metallic", 0.15f);
|
|
|
|
|
material.SetTexture("_MainTex", ResourceHandle<Texture>(new Texture()));
|
|
|
|
|
ASSERT_EQ(material.GetTextureBindingCount(), 1u);
|
|
|
|
|
|
|
|
|
|
material.ClearAllProperties();
|
|
|
|
|
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("_BaseColor"));
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("_Metallic"));
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("_MainTex"));
|
|
|
|
|
EXPECT_EQ(material.GetFloat4("_BaseColor"), Vector4(1.0f, 0.5f, 0.25f, 1.0f));
|
|
|
|
|
EXPECT_FLOAT_EQ(material.GetFloat("_Metallic"), 0.7f);
|
|
|
|
|
EXPECT_EQ(material.GetTextureBindingCount(), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, ShaderSchemaRejectsUnknownAndTypeMismatchedAssignments) {
|
|
|
|
|
Material material;
|
|
|
|
|
Shader* shader = CreateMaterialSchemaShader();
|
|
|
|
|
|
|
|
|
|
material.SetShader(ResourceHandle<Shader>(shader));
|
|
|
|
|
const Vector4 defaultBaseColor = material.GetFloat4("_BaseColor");
|
|
|
|
|
const float defaultMetallic = material.GetFloat("_Metallic");
|
|
|
|
|
|
|
|
|
|
material.SetFloat("_BaseColor", 0.1f);
|
|
|
|
|
material.SetFloat4("_Metallic", Vector4(1.0f, 2.0f, 3.0f, 4.0f));
|
|
|
|
|
material.SetFloat("UnknownProperty", 5.0f);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(material.GetFloat4("_BaseColor"), defaultBaseColor);
|
|
|
|
|
EXPECT_FLOAT_EQ(material.GetFloat("_Metallic"), defaultMetallic);
|
|
|
|
|
EXPECT_FALSE(material.HasProperty("UnknownProperty"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SwitchingShaderResyncsPropertiesAgainstNewSchema) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
auto* shaderA = new Shader();
|
|
|
|
|
ShaderPropertyDesc sharedA = {};
|
|
|
|
|
sharedA.name = "Shared";
|
|
|
|
|
sharedA.type = ShaderPropertyType::Float;
|
|
|
|
|
sharedA.defaultValue = "1.0";
|
|
|
|
|
shaderA->AddProperty(sharedA);
|
|
|
|
|
ShaderPropertyDesc onlyA = {};
|
|
|
|
|
onlyA.name = "OnlyA";
|
|
|
|
|
onlyA.type = ShaderPropertyType::Float;
|
|
|
|
|
onlyA.defaultValue = "2.0";
|
|
|
|
|
shaderA->AddProperty(onlyA);
|
|
|
|
|
|
|
|
|
|
auto* shaderB = new Shader();
|
|
|
|
|
ShaderPropertyDesc sharedB = {};
|
|
|
|
|
sharedB.name = "Shared";
|
|
|
|
|
sharedB.type = ShaderPropertyType::Float;
|
|
|
|
|
sharedB.defaultValue = "4.0";
|
|
|
|
|
shaderB->AddProperty(sharedB);
|
|
|
|
|
ShaderPropertyDesc onlyB = {};
|
|
|
|
|
onlyB.name = "OnlyB";
|
|
|
|
|
onlyB.type = ShaderPropertyType::Color;
|
|
|
|
|
onlyB.defaultValue = "(0.1,0.2,0.3,0.4)";
|
|
|
|
|
shaderB->AddProperty(onlyB);
|
|
|
|
|
|
|
|
|
|
material.SetFloat("Legacy", 9.0f);
|
|
|
|
|
material.SetShader(ResourceHandle<Shader>(shaderA));
|
|
|
|
|
material.SetFloat("Shared", 5.0f);
|
|
|
|
|
material.SetFloat("OnlyA", 8.0f);
|
|
|
|
|
|
|
|
|
|
material.SetShader(ResourceHandle<Shader>(shaderB));
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(material.HasProperty("Legacy"));
|
|
|
|
|
EXPECT_FALSE(material.HasProperty("OnlyA"));
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("Shared"));
|
|
|
|
|
EXPECT_TRUE(material.HasProperty("OnlyB"));
|
|
|
|
|
EXPECT_FLOAT_EQ(material.GetFloat("Shared"), 5.0f);
|
|
|
|
|
EXPECT_EQ(material.GetFloat4("OnlyB"), Vector4(0.1f, 0.2f, 0.3f, 0.4f));
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-08 19:18:07 +08:00
|
|
|
TEST(Material, SwitchingShaderDropsUnknownRuntimeBufferBindings) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
auto* shaderA = new Shader();
|
|
|
|
|
ShaderPass passA = {};
|
|
|
|
|
passA.name = "ForwardLit";
|
|
|
|
|
ShaderResourceBindingDesc nodesBinding = {};
|
|
|
|
|
nodesBinding.name = "VolumeNodes";
|
|
|
|
|
nodesBinding.type = ShaderResourceType::StructuredBuffer;
|
|
|
|
|
nodesBinding.set = 2u;
|
|
|
|
|
nodesBinding.binding = 0u;
|
|
|
|
|
passA.resources.PushBack(nodesBinding);
|
|
|
|
|
shaderA->AddPass(passA);
|
|
|
|
|
|
|
|
|
|
auto* shaderB = new Shader();
|
|
|
|
|
ShaderPass passB = {};
|
|
|
|
|
passB.name = "ForwardLit";
|
|
|
|
|
ShaderResourceBindingDesc bricksBinding = {};
|
|
|
|
|
bricksBinding.name = "VolumeBricks";
|
|
|
|
|
bricksBinding.type = ShaderResourceType::StructuredBuffer;
|
|
|
|
|
bricksBinding.set = 2u;
|
|
|
|
|
bricksBinding.binding = 0u;
|
|
|
|
|
passB.resources.PushBack(bricksBinding);
|
|
|
|
|
shaderB->AddPass(passB);
|
|
|
|
|
|
|
|
|
|
MockMaterialBuffer nodesBuffer;
|
|
|
|
|
MockMaterialBuffer bricksBuffer;
|
|
|
|
|
material.SetShader(ResourceHandle<Shader>(shaderA));
|
|
|
|
|
material.SetBuffer("VolumeNodes", &nodesBuffer);
|
|
|
|
|
material.SetBuffer("VolumeBricks", &bricksBuffer);
|
|
|
|
|
|
|
|
|
|
material.SetShader(ResourceHandle<Shader>(shaderB));
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(material.GetBuffer("VolumeNodes"), nullptr);
|
|
|
|
|
EXPECT_EQ(material.GetBuffer("VolumeBricks"), &bricksBuffer);
|
|
|
|
|
EXPECT_EQ(material.GetBufferBindingCount(), 1u);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 16:49:30 +08:00
|
|
|
TEST(Material, UpdateConstantBufferFollowsShaderSchemaOrderInsteadOfAlphabeticalOrder) {
|
|
|
|
|
Material material;
|
|
|
|
|
|
|
|
|
|
auto* shader = new Shader();
|
|
|
|
|
ShaderPropertyDesc beta = {};
|
|
|
|
|
beta.name = "beta";
|
|
|
|
|
beta.type = ShaderPropertyType::Float;
|
|
|
|
|
beta.defaultValue = "0.0";
|
|
|
|
|
shader->AddProperty(beta);
|
|
|
|
|
|
|
|
|
|
ShaderPropertyDesc alpha = {};
|
|
|
|
|
alpha.name = "alpha";
|
|
|
|
|
alpha.type = ShaderPropertyType::Float;
|
|
|
|
|
alpha.defaultValue = "0.0";
|
|
|
|
|
shader->AddProperty(alpha);
|
|
|
|
|
|
|
|
|
|
material.SetShader(ResourceHandle<Shader>(shader));
|
|
|
|
|
material.SetFloat("alpha", 10.0f);
|
|
|
|
|
material.SetFloat("beta", 20.0f);
|
|
|
|
|
|
|
|
|
|
const auto& constantLayout = material.GetConstantLayout();
|
|
|
|
|
ASSERT_EQ(constantLayout.Size(), 2u);
|
|
|
|
|
EXPECT_EQ(constantLayout[0].name, "beta");
|
|
|
|
|
EXPECT_EQ(constantLayout[0].offset, 0u);
|
|
|
|
|
EXPECT_EQ(constantLayout[1].name, "alpha");
|
|
|
|
|
EXPECT_EQ(constantLayout[1].offset, 16u);
|
|
|
|
|
|
|
|
|
|
const auto& constantBufferData = material.GetConstantBufferData();
|
|
|
|
|
ASSERT_EQ(constantBufferData.Size(), 32u);
|
|
|
|
|
|
|
|
|
|
const float* firstSlot = reinterpret_cast<const float*>(constantBufferData.Data());
|
|
|
|
|
const float* secondSlot = reinterpret_cast<const float*>(constantBufferData.Data() + 16);
|
|
|
|
|
EXPECT_FLOAT_EQ(firstSlot[0], 20.0f);
|
|
|
|
|
EXPECT_FLOAT_EQ(secondSlot[0], 10.0f);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 18:55:26 +08:00
|
|
|
TEST(Material, KeywordsFormStableValidatedSet) {
|
|
|
|
|
Material material;
|
|
|
|
|
Shader* shader = CreateMaterialKeywordShader({"XC_MAIN_LIGHT_SHADOWS", "XC_ALPHA_TEST"});
|
|
|
|
|
material.SetShader(ResourceHandle<Shader>(shader));
|
|
|
|
|
|
|
|
|
|
material.EnableKeyword("XC_MAIN_LIGHT_SHADOWS");
|
|
|
|
|
material.EnableKeyword("XC_ALPHA_TEST");
|
|
|
|
|
material.EnableKeyword("XC_ALPHA_TEST");
|
|
|
|
|
material.EnableKeyword("_");
|
|
|
|
|
material.EnableKeyword("XC_UNKNOWN");
|
|
|
|
|
|
|
|
|
|
ASSERT_EQ(material.GetKeywordCount(), 2u);
|
|
|
|
|
EXPECT_EQ(material.GetKeyword(0), "XC_ALPHA_TEST");
|
|
|
|
|
EXPECT_EQ(material.GetKeyword(1), "XC_MAIN_LIGHT_SHADOWS");
|
|
|
|
|
EXPECT_TRUE(material.IsKeywordEnabled("XC_ALPHA_TEST"));
|
|
|
|
|
EXPECT_TRUE(material.IsKeywordEnabled("XC_MAIN_LIGHT_SHADOWS"));
|
|
|
|
|
EXPECT_FALSE(material.IsKeywordEnabled("XC_UNKNOWN"));
|
|
|
|
|
|
|
|
|
|
material.DisableKeyword("XC_ALPHA_TEST");
|
|
|
|
|
ASSERT_EQ(material.GetKeywordCount(), 1u);
|
|
|
|
|
EXPECT_EQ(material.GetKeyword(0), "XC_MAIN_LIGHT_SHADOWS");
|
|
|
|
|
|
|
|
|
|
material.ClearKeywords();
|
|
|
|
|
EXPECT_EQ(material.GetKeywordCount(), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(Material, SwitchingShaderDropsUndeclaredKeywords) {
|
|
|
|
|
Material material;
|
|
|
|
|
Shader* shaderA = CreateMaterialKeywordShader({"XC_MAIN_LIGHT_SHADOWS", "XC_ALPHA_TEST"});
|
|
|
|
|
Shader* shaderB = CreateMaterialKeywordShader({"XC_ALPHA_TEST", "XC_EMISSION"});
|
|
|
|
|
|
|
|
|
|
material.SetShader(ResourceHandle<Shader>(shaderA));
|
|
|
|
|
material.EnableKeyword("XC_MAIN_LIGHT_SHADOWS");
|
|
|
|
|
material.EnableKeyword("XC_ALPHA_TEST");
|
|
|
|
|
ASSERT_EQ(material.GetKeywordCount(), 2u);
|
|
|
|
|
|
|
|
|
|
material.SetShader(ResourceHandle<Shader>(shaderB));
|
|
|
|
|
|
|
|
|
|
ASSERT_EQ(material.GetKeywordCount(), 1u);
|
|
|
|
|
EXPECT_FALSE(material.IsKeywordEnabled("XC_MAIN_LIGHT_SHADOWS"));
|
|
|
|
|
EXPECT_TRUE(material.IsKeywordEnabled("XC_ALPHA_TEST"));
|
|
|
|
|
|
|
|
|
|
material.EnableKeyword("XC_EMISSION");
|
|
|
|
|
ASSERT_EQ(material.GetKeywordCount(), 2u);
|
|
|
|
|
EXPECT_EQ(material.GetKeyword(0), "XC_ALPHA_TEST");
|
|
|
|
|
EXPECT_EQ(material.GetKeyword(1), "XC_EMISSION");
|
|
|
|
|
}
|
|
|
|
|
|
refactor(tests): reorganize tests directory to match engine structure
- Created tests/Core/Asset/ with tests for IResource, ResourceTypes, ResourceHandle, ResourceCache, ResourceDependencyGraph, ResourceGUID
- Created tests/Core/IO/ with tests for IResourceLoader, ResourcePath, ResourceFileSystem, FileArchive, ResourcePackage
- Reorganized tests/Resources/ into subdirectories: Texture/, Mesh/, Material/, Shader/, AudioClip/
- Added CMakeLists.txt for each new test subdirectory
- Fixed Material.h missing ResourceManager.h include (lost during engine refactor)
- tests/Core/CMakeLists.txt updated to include Asset/ and IO/ subdirectories
2026-03-24 15:44:13 +08:00
|
|
|
} // namespace
|