mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
- Layout only: token streams match up to split literals, continuations and using order - The size totals fall, so the structure baseline is lowered
133 lines
5.7 KiB
C++
133 lines
5.7 KiB
C++
// MobileGL - MobileGL/MG_Test/ShaderTranspiler/PixelCenterIntegerTest.cpp
|
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
|
// Licensed under the GNU Lesser General Public License v3.0:
|
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// End of Source File Header
|
|
|
|
// `layout(pixel_center_integer) in vec4 gl_FragCoord;` compiles to the PixelCenterInteger
|
|
// execution mode, which the Vulkan environment forbids: DirectVulkan's validation refused every
|
|
// such program (KHR-GL45.direct_state_access.textures_* draw through one). The lowered module
|
|
// must validate, carry no such mode, and shift exactly the x/y reads of the builtin.
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "Includes.h"
|
|
#include "Init.h"
|
|
#include <MG_Util/ShaderTranspiler/ShaderCompiler.h>
|
|
#include <MG_Util/ShaderTranspiler/Types.h>
|
|
|
|
#include "spirv-tools/libspirv.hpp"
|
|
|
|
using namespace MobileGL;
|
|
using MobileGL::MG_Util::ShaderTranspiler::ShaderCompiler;
|
|
|
|
namespace {
|
|
Vector<Uint32> CompileFragment(const char* fragment) {
|
|
using namespace MG_Util::ShaderTranspiler;
|
|
constexpr const char* kVertex = R"(#version 330 core
|
|
layout(location = 0) in vec2 position;
|
|
void main() { gl_Position = vec4(position, 0.0, 1.0); }
|
|
)";
|
|
Vector<SharedPtr<glslang::TShader>> shaders;
|
|
for (const auto& [stage, source] :
|
|
Vector<Pair<GLenum, const char*>>{{GL_VERTEX_SHADER, kVertex}, {GL_FRAGMENT_SHADER, fragment}}) {
|
|
ShaderAttrib attrib{.shaderType = stage, .sourceStr = source};
|
|
auto shader = ShaderCompiler::CompileShader(attrib);
|
|
EXPECT_TRUE(shader) << (shader ? String{} : shader.error().log);
|
|
if (!shader) return {};
|
|
shaders.push_back(shader.value());
|
|
}
|
|
ProgramAttrib programAttrib{.shaders = shaders};
|
|
auto program = ShaderCompiler::LinkProgram(programAttrib);
|
|
EXPECT_TRUE(program) << (program ? String{} : program.error().log);
|
|
if (!program) return {};
|
|
ProgramBinaryAttrib binaryAttrib{.shaderTypes = {GL_VERTEX_SHADER, GL_FRAGMENT_SHADER},
|
|
.program = *program.value()};
|
|
auto binary = ShaderCompiler::GetSpirvBinaryFromProgram(binaryAttrib);
|
|
EXPECT_TRUE(binary) << (binary ? String{} : binary.error().log);
|
|
if (!binary || binary->size() != 2) return {};
|
|
Vector<Uint32> module = binary.value()[1];
|
|
EXPECT_TRUE(ShaderCompiler::SanitizeAndOptimizeBinary(module, module, true, true));
|
|
return module;
|
|
}
|
|
|
|
String Disassemble(const Vector<Uint32>& spirv) {
|
|
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_1);
|
|
String text;
|
|
EXPECT_TRUE(tools.Disassemble(spirv, &text));
|
|
return text;
|
|
}
|
|
|
|
String ValidationError(const Vector<Uint32>& spirv) {
|
|
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_1);
|
|
String error;
|
|
tools.SetMessageConsumer([&](spv_message_level_t, const char*, const spv_position_t&, const char* message) {
|
|
error += message != nullptr ? message : "";
|
|
});
|
|
return tools.Validate(spirv) ? String{} : (error.empty() ? String{"invalid"} : error);
|
|
}
|
|
|
|
SizeT CountOccurrences(const String& text, const String& needle) {
|
|
SizeT count = 0;
|
|
for (SizeT at = text.find(needle); at != String::npos; at = text.find(needle, at + needle.size()))
|
|
++count;
|
|
return count;
|
|
}
|
|
} // namespace
|
|
|
|
// The direct_state_access shape: the whole vector converted to integer texel coordinates.
|
|
TEST(LowerPixelCenterInteger, ShiftsWholeVectorReadsAndDropsTheMode) {
|
|
MobileGL::Initialize();
|
|
const Vector<Uint32> input = CompileFragment(R"(#version 330 core
|
|
layout(pixel_center_integer) in vec4 gl_FragCoord;
|
|
uniform sampler2D data;
|
|
out vec4 color;
|
|
void main() { color = texelFetch(data, ivec2(gl_FragCoord.xy), 0); }
|
|
)");
|
|
ASSERT_FALSE(input.empty());
|
|
ASSERT_NE(Disassemble(input).find("PixelCenterInteger"), String::npos);
|
|
EXPECT_FALSE(ValidationError(input).empty()) << "the unlowered module is what Vulkan refuses";
|
|
|
|
Vector<Uint32> output;
|
|
ASSERT_TRUE(ShaderCompiler::LowerPixelCenterIntegerForVulkan(input, output, true));
|
|
EXPECT_EQ(ValidationError(output), String{});
|
|
const String text = Disassemble(output);
|
|
EXPECT_EQ(text.find("PixelCenterInteger"), String::npos) << text;
|
|
EXPECT_EQ(CountOccurrences(text, "OpFSub"), 1u) << text;
|
|
EXPECT_NE(text.find("OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0 %float_0"), String::npos) << text;
|
|
}
|
|
|
|
// Single components arrive through an access chain: x and y move, z and w keep their values.
|
|
TEST(LowerPixelCenterInteger, ShiftsOnlyTheXAndYComponents) {
|
|
MobileGL::Initialize();
|
|
const Vector<Uint32> input = CompileFragment(R"(#version 330 core
|
|
layout(pixel_center_integer) in vec4 gl_FragCoord;
|
|
out vec4 color;
|
|
void main() { color = vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w); }
|
|
)");
|
|
ASSERT_FALSE(input.empty());
|
|
|
|
Vector<Uint32> output;
|
|
ASSERT_TRUE(ShaderCompiler::LowerPixelCenterIntegerForVulkan(input, output, true));
|
|
EXPECT_EQ(ValidationError(output), String{});
|
|
const String text = Disassemble(output);
|
|
EXPECT_EQ(text.find("PixelCenterInteger"), String::npos) << text;
|
|
EXPECT_EQ(CountOccurrences(text, "OpFSub"), 2u) << text;
|
|
}
|
|
|
|
// A module without the mode is left alone.
|
|
TEST(LowerPixelCenterInteger, LeavesDefaultCentredModulesUnchanged) {
|
|
MobileGL::Initialize();
|
|
const Vector<Uint32> input = CompileFragment(R"(#version 330 core
|
|
out vec4 color;
|
|
void main() { color = vec4(gl_FragCoord.xy, 0.0, 1.0); }
|
|
)");
|
|
ASSERT_FALSE(input.empty());
|
|
|
|
Vector<Uint32> output;
|
|
ASSERT_TRUE(ShaderCompiler::LowerPixelCenterIntegerForVulkan(input, output, true));
|
|
EXPECT_EQ(Disassemble(output), Disassemble(input));
|
|
}
|