From cb59412fd962ef9cf18e557d002dc9605c316b37 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 21 Jul 2025 17:24:01 +0800 Subject: [PATCH] [Chore] (MG_Util/Program): test in/out linkage across stages --- MobileGL/MG_Test/Program/CMakeLists.txt | 2 +- MobileGL/MG_Test/Program/ProgramTest.cpp | 167 +++++++++++++++--- .../ShaderTranspiler/ShaderCompiler.cpp | 41 +++++ .../MG_Util/ShaderTranspiler/ShaderCompiler.h | 1 + 4 files changed, 190 insertions(+), 21 deletions(-) diff --git a/MobileGL/MG_Test/Program/CMakeLists.txt b/MobileGL/MG_Test/Program/CMakeLists.txt index 322797a0..c951fb9d 100644 --- a/MobileGL/MG_Test/Program/CMakeLists.txt +++ b/MobileGL/MG_Test/Program/CMakeLists.txt @@ -19,7 +19,7 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(LINK_LIBRARIES ${VULKAN_LIB_DIR}/glslang$<$:d>.lib - ${VULKAN_LIB_DIR}/spirv-cross-c$<$:d>.lib + ${VULKAN_LIB_DIR}/spirv-cross-c-shared$<$:d>.lib ${VULKAN_LIB_DIR}/GenericCodeGen$<$:d>.lib ${VULKAN_LIB_DIR}/glslang-default-resource-limits$<$:d>.lib ${VULKAN_LIB_DIR}/MachineIndependent$<$:d>.lib diff --git a/MobileGL/MG_Test/Program/ProgramTest.cpp b/MobileGL/MG_Test/Program/ProgramTest.cpp index d0be04c2..3798616b 100644 --- a/MobileGL/MG_Test/Program/ProgramTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramTest.cpp @@ -4,34 +4,84 @@ #include #include "Includes.h" - using namespace MobileGL; class ProgramTest : public ::testing::Test { protected: }; +struct InterfaceVariable { + std::string name; + uint32_t location; + + bool operator<(const InterfaceVariable& other) const { + return location < other.location; + } + + bool operator==(const InterfaceVariable& other) const { + return location == other.location && name == other.name; + } +}; + +static std::vector GetShaderInterface(const std::vector& spirv, spvc_resource_type resource_type) { + spvc_context context = nullptr; + spvc_context_create(&context); + + spvc_parsed_ir ir = nullptr; + spvc_context_parse_spirv(context, spirv.data(), spirv.size(), &ir); + + spvc_compiler compiler = nullptr; + spvc_context_create_compiler(context, SPVC_BACKEND_NONE, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler); + + spvc_resources resources = nullptr; + spvc_compiler_create_shader_resources(compiler, &resources); + + const spvc_reflected_resource *list = nullptr; + size_t count = 0; + spvc_resources_get_resource_list_for_type(resources, resource_type, &list, &count); + + std::vector variables; + for (size_t i = 0; i < count; ++i) { + unsigned int builtin; + if (spvc_compiler_has_decoration(compiler, list[i].id, SpvDecorationBuiltIn)) { + continue; + } + + InterfaceVariable var; + var.name = list[i].name; + var.location = spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationLocation); + variables.push_back(var); + } + + spvc_context_destroy(context); + + std::sort(variables.begin(), variables.end()); + + return variables; +} + TEST_F(ProgramTest, Sanity) { ASSERT_TRUE(true); } const char* vs = R"(#version 150 -in vec3 Position; -in vec2 UV0; -in vec4 Color; +in vec4 Position; -uniform mat4 ModelViewMat; uniform mat4 ProjMat; +uniform vec2 InSize; +uniform vec2 OutSize; -out vec2 texCoord0; -out vec4 vertexColor; +out vec2 texCoord; +out vec2 oneTexel; -void main() { - gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0); +void main(){ + vec4 outPos = ProjMat * vec4(Position.xy, 0.0, 1.0); + gl_Position = vec4(outPos.xy, 0.2, 1.0); - texCoord0 = UV0; - vertexColor = Color; + oneTexel = 1.0 / InSize; + + texCoord = Position.xy / OutSize; })"; TEST_F(ProgramTest, CompileSimpleVertexShader) { @@ -49,21 +99,41 @@ TEST_F(ProgramTest, CompileSimpleVertexShader) { const char* fs = R"(#version 150 -uniform sampler2D Sampler0; +uniform sampler2D InSampler; -uniform vec4 ColorModulator; +in vec2 texCoord; +in vec2 oneTexel; -in vec2 texCoord0; -in vec4 vertexColor; +uniform vec2 InSize; + +uniform vec3 Gray; +uniform vec3 RedMatrix; +uniform vec3 GreenMatrix; +uniform vec3 BlueMatrix; +uniform vec3 Offset; +uniform vec3 ColorScale; +uniform float Saturation; out vec4 fragColor; void main() { - vec4 color = texture(Sampler0, texCoord0) * vertexColor; - if (color.a == 0.0) { - discard; - } - fragColor = color * ColorModulator; + vec4 InTexel = texture(InSampler, texCoord); + + // Color Matrix + float RedValue = dot(InTexel.rgb, RedMatrix); + float GreenValue = dot(InTexel.rgb, GreenMatrix); + float BlueValue = dot(InTexel.rgb, BlueMatrix); + vec3 OutColor = vec3(RedValue, GreenValue, BlueValue); + + // Offset & Scale + OutColor = (OutColor * ColorScale) + Offset; + + // Saturation + float Luma = dot(OutColor, Gray); + vec3 Chroma = OutColor - Luma; + OutColor = (Chroma * Saturation) + Luma; + + fragColor = vec4(OutColor, 1.0); })"; TEST_F(ProgramTest, CompileSimpleFragmentShader) { @@ -112,3 +182,60 @@ TEST_F(ProgramTest, CompileAndLinkProgram) { FAIL() << "errc: " << program_res.error().errc << "\nlog: " << program_res.error().log; } } + +TEST_F(ProgramTest, DecompProgram) { + using namespace MG_Util::ShaderTranspiler; + ShaderAttrib vs_attrib { + .shaderType = GL_VERTEX_SHADER, + .sourceStr = vs + }; + auto vs_res = ShaderCompiler::CompileShader(vs_attrib); + if (!vs_res) { + ASSERT_NE(vs_res.error().errc, 0); + FAIL() << "errc: " << vs_res.error().errc << "\nlog: " << vs_res.error().log; + } + + ShaderAttrib fs_attrib { + .shaderType = GL_FRAGMENT_SHADER, + .sourceStr = fs +}; + auto fs_res = ShaderCompiler::CompileShader(fs_attrib); + if (!fs_res) { + ASSERT_NE(fs_res.error().errc, 0); + FAIL() << "errc: " << fs_res.error().errc << "\nlog: " << fs_res.error().log; + } + + ProgramAttrib programAttrib { + .shaderTypes = { GL_VERTEX_SHADER, GL_FRAGMENT_SHADER }, + .shaders = { vs_res.value(), fs_res.value() } + }; + + auto program_res = ShaderCompiler::LinkProgram(programAttrib); + if (!program_res) { + ASSERT_NE(program_res.error().errc, 0); + FAIL() << "errc: " << program_res.error().errc << "\nlog: " << program_res.error().log; + } + + auto spirvs = program_res.value(); + + for (SizeT i = 0; i < spirvs.size(); ++i) { + std::cout << "Decompiling " << MG_Util::ConvertGLEnumToString(programAttrib.shaderTypes[i]) << std::endl; + auto src = ShaderCompiler::DecompileShader(spirvs[i]); + if (!src) { + ASSERT_NE(src.error().errc, 0); + FAIL() << "errc: " << src.error().errc << "\nlog: " << src.error().log; + } else { + std::cout << src.value() << std::endl; + } + } + + // spirv link check + auto vs_outputs = GetShaderInterface(spirvs[0], SPVC_RESOURCE_TYPE_STAGE_OUTPUT); + auto fs_inputs = GetShaderInterface(spirvs[1], SPVC_RESOURCE_TYPE_STAGE_INPUT); + + ASSERT_EQ(vs_outputs.size(), fs_inputs.size()); + + for (size_t i = 0; i < vs_outputs.size(); ++i) { + EXPECT_EQ(vs_outputs[i].location, fs_inputs[i].location); + } +} diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp index 50222274..2e32e564 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp @@ -196,6 +196,47 @@ namespace MobileGL { return allSpirv; } + + Result ShaderCompiler::DecompileShader(Vector spirv) { + spvc_context context = nullptr; + spvc_parsed_ir ir = nullptr; + spvc_compiler compiler_glsl = nullptr; + spvc_compiler_options options = nullptr; + spvc_resources resources = nullptr; + const spvc_reflected_resource *list = nullptr; + const char *result = nullptr; + size_t count; + + const SpvId *p_spirv = spirv.data(); + size_t word_count = spirv.size(); + + spvc_context_create(&context); + spvc_context_parse_spirv(context, p_spirv, word_count, &ir); + spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler_glsl); + spvc_compiler_create_shader_resources(compiler_glsl, &resources); + spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, &list, &count); + spvc_compiler_create_compiler_options(compiler_glsl, &options); + + spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 450); + spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_FALSE); + spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, SPVC_TRUE); + spvc_compiler_install_compiler_options(compiler_glsl, options); + spvc_compiler_compile(compiler_glsl, &result); + + if (!result) { + ResultInfo r; + r.log += "Failed to compile the shader to GLSL: \n"; + r.log += spvc_context_get_last_error_string(context); + r.errc = -5; + return std::unexpected(r); + } + + std::string glsl = result; + + spvc_context_destroy(context); + + return glsl; + } } } } diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h index 671c6338..c9c91f5c 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h @@ -12,6 +12,7 @@ namespace ShaderTranspiler { public: static Result> CompileShader(const ShaderAttrib& attrib); static Result>> LinkProgram(const ProgramAttrib& attrib); + static Result DecompileShader(Vector spirv); }; } }