chore: checkpoint current workspace changes

This commit is contained in:
2026-04-11 22:14:02 +08:00
parent 3e55f8c204
commit 8848cfd958
227 changed files with 34027 additions and 6711 deletions

View File

@@ -1,6 +1,8 @@
#include <gtest/gtest.h>
#include <XCEngine/Core/Asset/AssetDatabase.h>
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Core/Asset/AssetRef.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Core/Math/Bounds.h>
@@ -446,7 +448,18 @@ TEST(GaussianSplatLoader, AssetDatabaseImportsSyntheticPlyAndLinearizesData) {
ASSERT_TRUE(database.EnsureArtifact("Assets/sample.ply", ResourceType::GaussianSplat, resolved));
ASSERT_TRUE(resolved.artifactReady);
EXPECT_TRUE(fs::exists(resolved.artifactMainPath.CStr()));
EXPECT_FALSE(resolved.artifactMainEntryPath.Empty());
EXPECT_NE(resolved.artifactMainEntryPath, resolved.artifactMainPath);
EXPECT_EQ(fs::path(resolved.artifactMainPath.CStr()).extension().generic_string(), ".xcgsplat");
XCEngine::Containers::Array<XCEngine::Core::uint8> artifactPayload;
EXPECT_TRUE(ReadArtifactContainerMainEntryPayload(
resolved.artifactMainPath,
ResourceType::GaussianSplat,
artifactPayload));
ASSERT_GE(artifactPayload.Size(), sizeof(GaussianSplatArtifactFileHeader));
GaussianSplatArtifactFileHeader artifactFileHeader = {};
std::memcpy(&artifactFileHeader, artifactPayload.Data(), sizeof(artifactFileHeader));
EXPECT_EQ(std::memcmp(artifactFileHeader.magic, "XCGSP01", 7), 0);
GaussianSplatLoader loader;
LoadResult result = loader.Load(resolved.artifactMainPath);
@@ -485,6 +498,12 @@ TEST(GaussianSplatLoader, AssetDatabaseImportsSyntheticPlyAndLinearizesData) {
EXPECT_NEAR(gaussianSplat->GetSHRecords()[1].coefficients[44], vertices[1].sh[44], 1e-6f);
delete gaussianSplat;
LoadResult entryResult = loader.Load(resolved.artifactMainEntryPath);
ASSERT_TRUE(entryResult);
ASSERT_NE(entryResult.resource, nullptr);
delete entryResult.resource;
database.Shutdown();
fs::remove_all(projectRoot);
}

View File

@@ -1,4 +1,5 @@
#include <gtest/gtest.h>
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Core/Asset/AssetDatabase.h>
#include <XCEngine/Resources/BuiltinResources.h>
@@ -8,6 +9,7 @@
#include <XCEngine/Core/Containers/Array.h>
#include <chrono>
#include <cstring>
#include <cstdio>
#include <filesystem>
#include <fstream>
@@ -16,6 +18,7 @@
using namespace XCEngine::Resources;
using namespace XCEngine::Containers;
namespace Core = XCEngine::Core;
namespace {
@@ -31,6 +34,20 @@ void WriteArtifactString(std::ofstream& output, const XCEngine::Containers::Stri
}
}
void AppendBytes(Array<Core::uint8>& payload, const void* data, size_t size) {
const size_t oldSize = payload.Size();
payload.Resize(oldSize + size);
std::memcpy(payload.Data() + oldSize, data, size);
}
void AppendArtifactString(Array<Core::uint8>& payload, const String& value) {
const Core::uint32 length = static_cast<Core::uint32>(value.Length());
AppendBytes(payload, &length, sizeof(length));
if (length > 0) {
AppendBytes(payload, value.CStr(), length);
}
}
bool PumpAsyncLoadsUntilIdle(ResourceManager& manager,
std::chrono::milliseconds timeout = std::chrono::milliseconds(4000)) {
const auto deadline = std::chrono::steady_clock::now() + timeout;
@@ -769,6 +786,10 @@ TEST(MaterialLoader, AssetDatabaseCreatesMaterialArtifact) {
EXPECT_TRUE(resolvedAsset.artifactReady);
EXPECT_EQ(fs::path(resolvedAsset.artifactMainPath.CStr()).extension().string(), ".xcmat");
EXPECT_TRUE(fs::exists(resolvedAsset.artifactMainPath.CStr()));
const fs::path artifactPath(resolvedAsset.artifactMainPath.CStr());
ASSERT_TRUE(artifactPath.has_parent_path());
EXPECT_EQ(artifactPath.parent_path().filename().string(),
artifactPath.stem().string().substr(0, 2));
database.Shutdown();
fs::remove_all(projectRoot);
@@ -1227,4 +1248,54 @@ TEST(MaterialLoader, LoadMaterialArtifactDefersTexturePayloadUntilRequested) {
fs::remove_all(projectRoot);
}
TEST(MaterialLoader, LoadMaterialArtifactFromContainerMainEntryPath) {
namespace fs = std::filesystem;
const fs::path projectRoot = fs::temp_directory_path() / "xc_material_container_main_entry_test";
fs::remove_all(projectRoot);
fs::create_directories(projectRoot);
ArtifactContainerWriter writer;
ArtifactContainerEntry mainEntry;
mainEntry.name = "main";
mainEntry.resourceType = ResourceType::Material;
mainEntry.localID = kMainAssetLocalID;
MaterialArtifactFileHeader fileHeader;
AppendBytes(mainEntry.payload, &fileHeader, sizeof(fileHeader));
AppendArtifactString(mainEntry.payload, "ContainerMaterial");
AppendArtifactString(mainEntry.payload, "Assets/container.material");
AppendArtifactString(mainEntry.payload, "");
MaterialArtifactHeader header;
header.renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
AppendBytes(mainEntry.payload, &header, sizeof(header));
writer.AddEntry(std::move(mainEntry));
const String containerPath =
(projectRoot / "Library" / "Artifacts" / "ab" / "container.xcmat").string().c_str();
String errorMessage;
ASSERT_TRUE(writer.WriteToFile(containerPath, &errorMessage))
<< errorMessage.CStr();
MaterialLoader loader;
const String virtualPath = BuildArtifactContainerEntryPath(containerPath, "main");
EXPECT_TRUE(loader.CanLoad(virtualPath));
LoadResult result = loader.Load(virtualPath);
ASSERT_TRUE(result) << result.errorMessage.CStr();
ASSERT_NE(result.resource, nullptr);
auto* material = static_cast<Material*>(result.resource);
ASSERT_NE(material, nullptr);
EXPECT_TRUE(material->IsValid());
EXPECT_EQ(material->GetPath(), "Assets/container.material");
EXPECT_EQ(material->GetRenderQueue(), static_cast<Core::int32>(MaterialRenderQueue::Geometry));
EXPECT_FALSE(material->HasRenderStateOverride());
delete material;
fs::remove_all(projectRoot);
}
} // namespace

View File

@@ -25,6 +25,7 @@ target_link_libraries(shader_tests
target_include_directories(shader_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/engine/src
${CMAKE_SOURCE_DIR}/tests/Fixtures
)

View File

@@ -142,6 +142,37 @@ TEST(Shader, ApplyShaderStageVariantCarriesMatchedBackendCompiledBinary) {
EXPECT_EQ(compileDesc.compiledBinary[2], 0x09);
}
TEST(Shader, ApplyShaderStageVariantTreatsOpenGLHlslPrecompiledBinaryAsVulkanSpirv) {
ShaderPass pass = {};
pass.name = "GaussianSplat";
ShaderStageVariant variant = {};
variant.stage = ShaderType::Vertex;
variant.language = ShaderLanguage::HLSL;
variant.backend = ShaderBackend::Generic;
variant.entryPoint = "MainVS";
variant.profile = "vs_5_1";
variant.sourceCode = "float4 MainVS() : SV_POSITION { return 0; }";
Array<XCEngine::Core::uint8> openglPayload;
openglPayload.PushBack(0x0A);
openglPayload.PushBack(0x0B);
openglPayload.PushBack(0x0C);
variant.SetCompiledBinaryForBackend(ShaderBackend::OpenGL, openglPayload);
XCEngine::RHI::ShaderCompileDesc compileDesc = {};
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
pass,
ShaderBackend::OpenGL,
variant,
compileDesc);
EXPECT_EQ(compileDesc.compiledBinaryBackend, XCEngine::RHI::ShaderBinaryBackend::Vulkan);
ASSERT_EQ(compileDesc.compiledBinary.size(), 3u);
EXPECT_EQ(compileDesc.compiledBinary[0], 0x0A);
EXPECT_EQ(compileDesc.compiledBinary[2], 0x0C);
}
TEST(Shader, FindsBackendSpecificVariantAndFallsBackToGeneric) {
Shader shader;

View File

@@ -1,11 +1,13 @@
#include <gtest/gtest.h>
#include <XCEngine/Core/Asset/AssetDatabase.h>
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Resources/BuiltinResources.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Resources/Shader/ShaderLoader.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include <XCEngine/Core/Containers/Array.h>
#include <XCEngine/RHI/ShaderCompiler/SpirvShaderCompiler.h>
#include "Rendering/Internal/ShaderVariantUtils.h"
#include <chrono>
@@ -18,6 +20,8 @@
using namespace XCEngine::Resources;
using namespace XCEngine::Containers;
namespace Containers = XCEngine::Containers;
namespace Core = XCEngine::Core;
namespace {
@@ -29,6 +33,19 @@ void WriteTextFile(const std::filesystem::path& path, const std::string& content
}
ShaderArtifactFileHeader ReadShaderArtifactFileHeader(const std::filesystem::path& path) {
Containers::Array<Core::uint8> payload;
if (ReadArtifactContainerMainEntryPayload(
path.generic_string().c_str(),
ResourceType::Shader,
payload)) {
EXPECT_GE(payload.Size(), sizeof(ShaderArtifactFileHeader));
ShaderArtifactFileHeader header = {};
if (payload.Size() >= sizeof(ShaderArtifactFileHeader)) {
std::memcpy(&header, payload.Data(), sizeof(header));
}
return header;
}
ShaderArtifactFileHeader header = {};
std::ifstream input(path, std::ios::binary);
EXPECT_TRUE(input.is_open());
@@ -37,6 +54,33 @@ ShaderArtifactFileHeader ReadShaderArtifactFileHeader(const std::filesystem::pat
return header;
}
void WriteShaderArtifactFileHeader(const std::filesystem::path& path,
const ShaderArtifactFileHeader& header) {
Containers::Array<Core::uint8> payload;
if (ReadArtifactContainerMainEntryPayload(
path.generic_string().c_str(),
ResourceType::Shader,
payload)) {
ASSERT_GE(payload.Size(), sizeof(ShaderArtifactFileHeader));
std::memcpy(payload.Data(), &header, sizeof(header));
ArtifactContainerWriter writer;
ArtifactContainerEntry entry;
entry.name = "main";
entry.resourceType = ResourceType::Shader;
entry.localID = kMainAssetLocalID;
entry.payload = payload;
writer.AddEntry(std::move(entry));
ASSERT_TRUE(writer.WriteToFile(path.generic_string().c_str()));
return;
}
std::fstream output(path, std::ios::binary | std::ios::in | std::ios::out);
ASSERT_TRUE(output.is_open());
output.write(reinterpret_cast<const char*>(&header), sizeof(header));
ASSERT_TRUE(static_cast<bool>(output));
}
const ShaderPassTagEntry* FindPassTag(const ShaderPass* pass, const char* name) {
if (pass == nullptr || name == nullptr) {
return nullptr;
@@ -699,6 +743,202 @@ TEST(ShaderLoader, LoadShaderAuthoringBuildsComputeOnlyPassConstantBufferBinding
fs::remove_all(shaderRoot);
}
TEST(ShaderLoader, LoadShaderAuthoringBuildsForwardLitExtraTextureBindings) {
namespace fs = std::filesystem;
const fs::path shaderRoot = fs::temp_directory_path() / "xc_shader_authoring_forward_extra_textures";
const fs::path shaderPath = shaderRoot / "forward_extra_textures.shader";
fs::remove_all(shaderRoot);
fs::create_directories(shaderRoot);
WriteTextFile(
shaderPath,
R"(Shader "ForwardExtraTextures"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" [Semantic(BaseColorTexture)]
_LightMap ("Light Map", 2D) = "white"
}
SubShader
{
Pass
{
Name "ForwardLit"
Tags { "LightMode" = "ForwardLit" }
HLSLPROGRAM
#pragma vertex MainVS
#pragma fragment MainPS
Texture2D BaseColorTexture;
Texture2D _LightMap;
SamplerState LinearClampSampler;
float4 MainVS() : SV_POSITION
{
return 0;
}
float4 MainPS() : SV_TARGET
{
return _LightMap.Sample(LinearClampSampler, float2(0.0f, 0.0f));
}
ENDHLSL
}
}
}
)");
ShaderLoader loader;
LoadResult result = loader.Load(shaderPath.string().c_str());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
EXPECT_EQ(pass->resources.Size(), 9u);
const ShaderResourceBindingDesc* lightMapBinding =
shader->FindPassResourceBinding("ForwardLit", "_LightMap");
ASSERT_NE(lightMapBinding, nullptr);
EXPECT_EQ(lightMapBinding->type, ShaderResourceType::Texture2D);
EXPECT_EQ(lightMapBinding->set, 4u);
EXPECT_EQ(lightMapBinding->binding, 1u);
delete shader;
fs::remove_all(shaderRoot);
}
TEST(ShaderLoader, LoadNahidaProjectCharacterToonShader) {
namespace fs = std::filesystem;
const fs::path shaderPath =
fs::path(__FILE__).parent_path().parent_path().parent_path().parent_path() /
"project/Assets/Shaders/XCCharacterToon.shader";
ASSERT_TRUE(fs::exists(shaderPath)) << shaderPath.string();
ShaderLoader loader;
LoadResult result = loader.Load(shaderPath.string().c_str());
ASSERT_TRUE(result) << result.errorMessage.CStr();
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
EXPECT_EQ(shader->GetProperties().Size(), 43u);
EXPECT_EQ(pass->resources.Size(), 14u);
EXPECT_NE(shader->FindPassResourceBinding("ForwardLit", "_LightMap"), nullptr);
EXPECT_NE(shader->FindPassResourceBinding("ForwardLit", "_NormalMap"), nullptr);
EXPECT_NE(shader->FindPassResourceBinding("ForwardLit", "_FaceLightMap"), nullptr);
EXPECT_NE(shader->FindPassResourceBinding("ForwardLit", "_FaceShadow"), nullptr);
EXPECT_NE(shader->FindPassResourceBinding("ForwardLit", "_ShadowRamp"), nullptr);
EXPECT_NE(shader->FindPassResourceBinding("ForwardLit", "_MetalMap"), nullptr);
EXPECT_NE(shader->FindPass("DepthOnly"), nullptr);
EXPECT_NE(shader->FindPass("ShadowCaster"), nullptr);
delete shader;
}
TEST(ShaderLoader, NahidaProjectCharacterToonShaderCompilesSurfaceAndFaceRuntimeVariants) {
namespace fs = std::filesystem;
const fs::path shaderPath =
fs::path(__FILE__).parent_path().parent_path().parent_path().parent_path() /
"project/Assets/Shaders/XCCharacterToon.shader";
ASSERT_TRUE(fs::exists(shaderPath)) << shaderPath.string();
ShaderLoader loader;
LoadResult result = loader.Load(shaderPath.string().c_str());
ASSERT_TRUE(result) << result.errorMessage.CStr();
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
auto compileVariant =
[&pass](
const ShaderStageVariant& variant,
ShaderBackend backend,
std::string* outGlslSource = nullptr) {
XCEngine::RHI::ShaderCompileDesc compileDesc = {};
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
*pass,
backend,
variant,
compileDesc);
XCEngine::RHI::CompiledSpirvShader spirvShader = {};
std::string errorMessage;
EXPECT_TRUE(
XCEngine::RHI::CompileSpirvShader(
compileDesc,
XCEngine::RHI::SpirvTargetEnvironment::Vulkan,
spirvShader,
&errorMessage))
<< errorMessage;
if (outGlslSource != nullptr) {
EXPECT_TRUE(XCEngine::RHI::TranspileSpirvToOpenGLGLSL(spirvShader, *outGlslSource, &errorMessage))
<< errorMessage;
}
};
ShaderKeywordSet surfaceKeywords = {};
surfaceKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
surfaceKeywords.enabledKeywords.PushBack("_NORMAL_MAP");
surfaceKeywords.enabledKeywords.PushBack("_RIM");
surfaceKeywords.enabledKeywords.PushBack("_SPECULAR");
const ShaderStageVariant* surfaceVertex = shader->FindVariant(
"ForwardLit",
ShaderType::Vertex,
ShaderBackend::OpenGL,
surfaceKeywords);
ASSERT_NE(surfaceVertex, nullptr);
const ShaderStageVariant* surfaceFragment = shader->FindVariant(
"ForwardLit",
ShaderType::Fragment,
ShaderBackend::OpenGL,
surfaceKeywords);
ASSERT_NE(surfaceFragment, nullptr);
std::string surfaceVertexGlsl;
compileVariant(*surfaceVertex, ShaderBackend::OpenGL, &surfaceVertexGlsl);
EXPECT_NE(surfaceVertexGlsl.find("layout(location = 3) in vec3"), std::string::npos);
EXPECT_NE(surfaceVertexGlsl.find("layout(location = 4) in vec3"), std::string::npos);
std::string surfaceFragmentGlsl;
compileVariant(*surfaceFragment, ShaderBackend::OpenGL, &surfaceFragmentGlsl);
EXPECT_NE(surfaceFragmentGlsl.find("sampler2D SPIRV_Cross_Combined_NormalMapLinearClampSampler"), std::string::npos);
EXPECT_NE(surfaceFragmentGlsl.find("sampler2D SPIRV_Cross_Combined_MetalMapLinearClampSampler"), std::string::npos);
ShaderKeywordSet faceKeywords = {};
faceKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
faceKeywords.enabledKeywords.PushBack("_IS_FACE");
faceKeywords.enabledKeywords.PushBack("_RIM");
const ShaderStageVariant* faceFragment = shader->FindVariant(
"ForwardLit",
ShaderType::Fragment,
ShaderBackend::OpenGL,
faceKeywords);
ASSERT_NE(faceFragment, nullptr);
std::string faceFragmentGlsl;
compileVariant(*faceFragment, ShaderBackend::OpenGL, &faceFragmentGlsl);
EXPECT_NE(faceFragmentGlsl.find("sampler2D SPIRV_Cross_Combined_FaceLightMapLinearClampSampler"), std::string::npos);
EXPECT_NE(faceFragmentGlsl.find("sampler2D SPIRV_Cross_Combined_FaceShadowLinearClampSampler"), std::string::npos);
delete shader;
}
TEST(ShaderLoader, LoadShaderAuthoringRejectsPassMixingComputeAndGraphicsPragmas) {
namespace fs = std::filesystem;
@@ -2089,9 +2329,11 @@ TEST(ShaderLoader, AssetDatabaseCreatesShaderArtifactFromAuthoringAndLoaderReads
ASSERT_TRUE(resolvedAsset.artifactReady);
EXPECT_EQ(fs::path(resolvedAsset.artifactMainPath.CStr()).extension().string(), ".xcshader");
EXPECT_TRUE(fs::exists(resolvedAsset.artifactMainPath.CStr()));
EXPECT_FALSE(resolvedAsset.artifactMainEntryPath.Empty());
EXPECT_NE(resolvedAsset.artifactMainEntryPath, resolvedAsset.artifactMainPath);
ShaderLoader loader;
LoadResult result = loader.Load(resolvedAsset.artifactMainPath.CStr());
LoadResult result = loader.Load(resolvedAsset.artifactMainEntryPath.CStr());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
@@ -2112,6 +2354,10 @@ TEST(ShaderLoader, AssetDatabaseCreatesShaderArtifactFromAuthoringAndLoaderReads
shader->FindVariant("ForwardLit", ShaderType::Fragment, ShaderBackend::OpenGL);
ASSERT_NE(fragmentVariant, nullptr);
EXPECT_NE(std::string(fragmentVariant->sourceCode.CStr()).find("ARTIFACT_AUTHORING_PS"), std::string::npos);
const Array<XCEngine::Core::uint8>* d3d12Binary =
fragmentVariant->GetCompiledBinaryForBackend(ShaderBackend::D3D12);
ASSERT_NE(d3d12Binary, nullptr);
EXPECT_FALSE(d3d12Binary->Empty());
ShaderKeywordSet enabledKeywords = {};
enabledKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
@@ -2181,12 +2427,7 @@ TEST(ShaderLoader, AssetDatabaseReimportsLegacyShaderArtifactHeaderBeforeLoad) {
std::memcpy(legacyHeader.magic, "XCSHD04", 7);
legacyHeader.magic[7] = '\0';
legacyHeader.schemaVersion = 4u;
{
std::fstream output(firstResolve.artifactMainPath.CStr(), std::ios::binary | std::ios::in | std::ios::out);
ASSERT_TRUE(output.is_open());
output.write(reinterpret_cast<const char*>(&legacyHeader), sizeof(legacyHeader));
ASSERT_TRUE(static_cast<bool>(output));
}
WriteShaderArtifactFileHeader(firstResolve.artifactMainPath.CStr(), legacyHeader);
AssetDatabase::ResolvedAsset secondResolve;
ASSERT_TRUE(database.EnsureArtifact("Assets/Shaders/lit.shader", ResourceType::Shader, secondResolve));
@@ -2194,7 +2435,7 @@ TEST(ShaderLoader, AssetDatabaseReimportsLegacyShaderArtifactHeaderBeforeLoad) {
ASSERT_TRUE(secondResolve.artifactReady);
const ShaderArtifactFileHeader currentHeader = ReadShaderArtifactFileHeader(secondResolve.artifactMainPath.CStr());
EXPECT_EQ(std::string(currentHeader.magic, currentHeader.magic + 7), std::string("XCSHD05"));
EXPECT_EQ(std::string(currentHeader.magic, currentHeader.magic + 7), std::string("XCSHD06"));
EXPECT_EQ(currentHeader.schemaVersion, kShaderArtifactSchemaVersion);
ShaderLoader loader;
@@ -3200,7 +3441,7 @@ TEST(ShaderLoader, LoadBuiltinObjectIdOutlineShaderBuildsAuthoringVariants) {
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->variants.Size(), 2u);
ASSERT_EQ(pass->tags.Size(), 1u);
EXPECT_TRUE(pass->resources.Empty());
EXPECT_FALSE(pass->resources.Empty());
EXPECT_TRUE(pass->hasFixedFunctionState);
EXPECT_EQ(pass->fixedFunctionState.cullMode, MaterialCullMode::None);
EXPECT_FALSE(pass->fixedFunctionState.depthWriteEnable);

View File

@@ -1,4 +1,6 @@
#include <gtest/gtest.h>
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Core/Asset/AssetDatabase.h>
#include <XCEngine/Core/Asset/AssetRef.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
@@ -8,12 +10,14 @@
#include <XCEngine/Core/Containers/Array.h>
#include <chrono>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <thread>
using namespace XCEngine::Resources;
using namespace XCEngine::Containers;
namespace Core = XCEngine::Core;
namespace {
@@ -103,9 +107,14 @@ TEST(TextureLoader, AssetDatabaseCreatesTextureArtifactAndReusesItWithoutReimpor
ASSERT_TRUE(firstResolve.exists);
ASSERT_TRUE(firstResolve.artifactReady);
EXPECT_TRUE(fs::exists(projectRoot / "Assets" / "checker.bmp.meta"));
EXPECT_TRUE(fs::exists(projectRoot / "Library" / "SourceAssetDB" / "assets.db"));
EXPECT_TRUE(fs::exists(projectRoot / "Library" / "ArtifactDB" / "artifacts.db"));
EXPECT_TRUE(fs::exists(projectRoot / "Library" / "assets.db"));
EXPECT_TRUE(fs::exists(projectRoot / "Library" / "artifacts.db"));
EXPECT_TRUE(fs::exists(firstResolve.artifactMainPath.CStr()));
const fs::path firstArtifactPath(firstResolve.artifactMainPath.CStr());
ASSERT_TRUE(firstArtifactPath.has_filename());
ASSERT_TRUE(firstArtifactPath.has_parent_path());
EXPECT_EQ(firstArtifactPath.parent_path().filename().string(),
firstArtifactPath.stem().string().substr(0, 2));
std::ifstream metaFile(projectRoot / "Assets" / "checker.bmp.meta");
ASSERT_TRUE(metaFile.is_open());
@@ -223,4 +232,64 @@ TEST(TextureLoader, ResourceManagerLoadsLibraryArtifactTextureWithoutReimporting
fs::remove_all(projectRoot);
}
TEST(TextureLoader, LoadTextureArtifactFromContainerEntryPath) {
namespace fs = std::filesystem;
const fs::path projectRoot = fs::temp_directory_path() / "xc_texture_container_entry_path_test";
fs::remove_all(projectRoot);
fs::create_directories(projectRoot);
ArtifactContainerWriter writer;
TextureArtifactHeader header;
header.textureType = static_cast<Core::uint32>(TextureType::Texture2D);
header.textureFormat = static_cast<Core::uint32>(TextureFormat::RGBA8_UNORM);
header.width = 2;
header.height = 2;
header.depth = 1;
header.mipLevels = 1;
header.arraySize = 1;
header.pixelDataSize = 16;
ArtifactContainerEntry textureEntry;
textureEntry.name = "texture_0.xctex";
textureEntry.resourceType = ResourceType::Texture;
textureEntry.payload.Resize(sizeof(TextureArtifactHeader) + static_cast<size_t>(header.pixelDataSize));
std::memcpy(textureEntry.payload.Data(), &header, sizeof(TextureArtifactHeader));
Core::uint8* pixelBytes = textureEntry.payload.Data() + sizeof(TextureArtifactHeader);
const Core::uint8 pixelData[16] = {
255, 0, 0, 255,
0, 255, 0, 255,
0, 0, 255, 255,
255, 255, 255, 255
};
std::memcpy(pixelBytes, pixelData, sizeof(pixelData));
writer.AddEntry(std::move(textureEntry));
const String containerPath =
(projectRoot / "Library" / "Artifacts" / "ab" / "artifact.xcmodel").string().c_str();
String errorMessage;
ASSERT_TRUE(writer.WriteToFile(containerPath, &errorMessage))
<< errorMessage.CStr();
TextureLoader loader;
const String virtualPath =
BuildArtifactContainerEntryPath(containerPath, "texture_0.xctex");
LoadResult result = loader.Load(virtualPath);
ASSERT_TRUE(result) << result.errorMessage.CStr();
ASSERT_NE(result.resource, nullptr);
auto* texture = static_cast<Texture*>(result.resource);
EXPECT_EQ(texture->GetWidth(), 2u);
EXPECT_EQ(texture->GetHeight(), 2u);
EXPECT_EQ(texture->GetTextureType(), TextureType::Texture2D);
EXPECT_EQ(texture->GetFormat(), TextureFormat::RGBA8_UNORM);
EXPECT_EQ(texture->GetPixelDataSize(), 16u);
EXPECT_EQ(texture->GetPath(), virtualPath);
delete texture;
fs::remove_all(projectRoot);
}
} // namespace

View File

@@ -1,5 +1,6 @@
#include <gtest/gtest.h>
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include <XCEngine/Core/Asset/AssetDatabase.h>
#include <XCEngine/Core/Asset/AssetImportService.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
@@ -147,9 +148,19 @@ TEST(UIDocumentLoader, AssetDatabaseImportsViewArtifactAndReimportsWhenDependenc
ASSERT_TRUE(firstResolve.artifactReady);
EXPECT_EQ(fs::path(firstResolve.artifactMainPath.CStr()).extension().string(), ".xcuiasset");
EXPECT_TRUE(fs::exists(firstResolve.artifactMainPath.CStr()));
EXPECT_TRUE(IsArtifactContainerFile(firstResolve.artifactMainPath));
EXPECT_FALSE(firstResolve.artifactMainEntryPath.Empty());
EXPECT_NE(firstResolve.artifactMainEntryPath, firstResolve.artifactMainPath);
XCEngine::Containers::Array<XCEngine::Core::uint8> artifactPayload;
ASSERT_TRUE(ReadArtifactContainerMainEntryPayload(
firstResolve.artifactMainPath,
ResourceType::UIView,
artifactPayload));
EXPECT_FALSE(artifactPayload.Empty());
UIViewLoader loader;
LoadResult firstLoad = loader.Load(firstResolve.artifactMainPath.CStr());
LoadResult firstLoad = loader.Load(firstResolve.artifactMainEntryPath.CStr());
ASSERT_TRUE(firstLoad);
auto* firstView = static_cast<UIView*>(firstLoad.resource);
ASSERT_NE(firstView, nullptr);

View File

@@ -1,5 +1,6 @@
#include <gtest/gtest.h>
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include <XCEngine/Resources/UI/UIDocumentCompiler.h>
#include <XCEngine/Resources/UI/UIDocumentLoaders.h>
@@ -103,6 +104,13 @@ TEST(UISchemaDocument, CompileAndArtifactLoadPopulateSchemaDefinition) {
WriteUIDocumentArtifact(artifactPath.string().c_str(), compileResult, &artifactWriteError))
<< artifactWriteError.CStr();
XCEngine::Containers::Array<XCEngine::Core::uint8> artifactPayload;
ASSERT_TRUE(ReadArtifactContainerMainEntryPayload(
artifactPath.string().c_str(),
ResourceType::UISchema,
artifactPayload));
EXPECT_FALSE(artifactPayload.Empty());
UIDocumentCompileResult artifactResult = {};
ASSERT_TRUE(LoadUIDocumentArtifact(
artifactPath.string().c_str(),

View File

@@ -1,6 +1,8 @@
#include <gtest/gtest.h>
#include <XCEngine/Core/Asset/AssetDatabase.h>
#include <XCEngine/Core/Asset/ArtifactContainer.h>
#include <XCEngine/Core/Asset/ArtifactFormats.h>
#include <XCEngine/Core/Asset/AssetRef.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Core/Math/Bounds.h>
@@ -159,40 +161,58 @@ TEST(VolumeFieldLoader, AssetDatabaseCreatesVolumeArtifactAndReusesItWithoutReim
fs::copy_file(fixturePath, volumePath, fs::copy_options::overwrite_existing);
const GeneratedNanoVDBVolume generated = ReadTestNanoVDBFileMetadata(fixturePath);
AssetDatabase database;
database.Initialize(projectRoot.string().c_str());
{
AssetDatabase database;
database.Initialize(projectRoot.string().c_str());
AssetDatabase::ResolvedAsset firstResolve;
ASSERT_TRUE(database.EnsureArtifact("Assets/cloud.nvdb", ResourceType::VolumeField, firstResolve));
ASSERT_TRUE(firstResolve.exists);
ASSERT_TRUE(firstResolve.artifactReady);
EXPECT_TRUE(fs::exists(firstResolve.artifactMainPath.CStr()));
EXPECT_EQ(fs::path(firstResolve.artifactMainPath.CStr()).extension().generic_string(), ".xcvol");
AssetDatabase::ResolvedAsset firstResolve;
ASSERT_TRUE(database.EnsureArtifact("Assets/cloud.nvdb", ResourceType::VolumeField, firstResolve));
ASSERT_TRUE(firstResolve.exists);
ASSERT_TRUE(firstResolve.artifactReady);
EXPECT_TRUE(fs::exists(firstResolve.artifactMainPath.CStr()));
EXPECT_FALSE(firstResolve.artifactMainEntryPath.Empty());
EXPECT_NE(firstResolve.artifactMainEntryPath, firstResolve.artifactMainPath);
EXPECT_EQ(fs::path(firstResolve.artifactMainPath.CStr()).extension().generic_string(), ".xcvol");
XCEngine::Containers::Array<XCEngine::Core::uint8> artifactPayload;
EXPECT_TRUE(ReadArtifactContainerMainEntryPayload(
firstResolve.artifactMainPath,
ResourceType::VolumeField,
artifactPayload));
ASSERT_GE(artifactPayload.Size(), sizeof(VolumeFieldArtifactHeader));
VolumeFieldArtifactHeader artifactHeader = {};
std::memcpy(&artifactHeader, artifactPayload.Data(), sizeof(artifactHeader));
EXPECT_EQ(std::memcmp(artifactHeader.magic, "XCVOL02", 7), 0);
VolumeFieldLoader loader;
LoadResult artifactLoad = loader.Load(firstResolve.artifactMainPath);
ASSERT_TRUE(artifactLoad);
ASSERT_NE(artifactLoad.resource, nullptr);
auto* artifactVolume = static_cast<VolumeField*>(artifactLoad.resource);
EXPECT_EQ(artifactVolume->GetStorageKind(), VolumeStorageKind::NanoVDB);
EXPECT_EQ(artifactVolume->GetPayloadSize(), generated.payloadSize);
ExpectVector3Near(artifactVolume->GetBounds().GetMin(), generated.bounds.GetMin());
ExpectVector3Near(artifactVolume->GetBounds().GetMax(), generated.bounds.GetMax());
ExpectVector3Near(artifactVolume->GetVoxelSize(), generated.voxelSize);
ExpectIndexBoundsEq(artifactVolume->GetIndexBounds(), generated.indexBounds);
EXPECT_EQ(artifactVolume->GetGridType(), generated.gridType);
EXPECT_EQ(artifactVolume->GetGridClass(), generated.gridClass);
delete artifactVolume;
VolumeFieldLoader loader;
LoadResult artifactLoad = loader.Load(firstResolve.artifactMainPath);
ASSERT_TRUE(artifactLoad);
ASSERT_NE(artifactLoad.resource, nullptr);
auto* artifactVolume = static_cast<VolumeField*>(artifactLoad.resource);
EXPECT_EQ(artifactVolume->GetStorageKind(), VolumeStorageKind::NanoVDB);
EXPECT_EQ(artifactVolume->GetPayloadSize(), generated.payloadSize);
ExpectVector3Near(artifactVolume->GetBounds().GetMin(), generated.bounds.GetMin());
ExpectVector3Near(artifactVolume->GetBounds().GetMax(), generated.bounds.GetMax());
ExpectVector3Near(artifactVolume->GetVoxelSize(), generated.voxelSize);
ExpectIndexBoundsEq(artifactVolume->GetIndexBounds(), generated.indexBounds);
EXPECT_EQ(artifactVolume->GetGridType(), generated.gridType);
EXPECT_EQ(artifactVolume->GetGridClass(), generated.gridClass);
delete artifactVolume;
const auto originalArtifactWriteTime = fs::last_write_time(firstResolve.artifactMainPath.CStr());
std::this_thread::sleep_for(50ms);
LoadResult entryLoad = loader.Load(firstResolve.artifactMainEntryPath);
ASSERT_TRUE(entryLoad);
ASSERT_NE(entryLoad.resource, nullptr);
delete entryLoad.resource;
AssetDatabase::ResolvedAsset secondResolve;
ASSERT_TRUE(database.EnsureArtifact("Assets/cloud.nvdb", ResourceType::VolumeField, secondResolve));
EXPECT_EQ(firstResolve.artifactMainPath, secondResolve.artifactMainPath);
EXPECT_EQ(originalArtifactWriteTime, fs::last_write_time(secondResolve.artifactMainPath.CStr()));
const auto originalArtifactWriteTime = fs::last_write_time(firstResolve.artifactMainPath.CStr());
std::this_thread::sleep_for(50ms);
database.Shutdown();
AssetDatabase::ResolvedAsset secondResolve;
ASSERT_TRUE(database.EnsureArtifact("Assets/cloud.nvdb", ResourceType::VolumeField, secondResolve));
EXPECT_EQ(firstResolve.artifactMainPath, secondResolve.artifactMainPath);
EXPECT_EQ(originalArtifactWriteTime, fs::last_write_time(secondResolve.artifactMainPath.CStr()));
database.Shutdown();
}
fs::remove_all(projectRoot);
}
#else