chore: checkpoint current workspace changes
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user