yum-mirror/glslang

git clone https://git.yummers.dev/yum-mirror/glslang

Rohan Gargglsl: implement Y-inversion for GLSL to SPIR-Vfa2e5029

main
7.5 KiB225 linesraw
1// Copyright (C) 2026 The Khronos Group Inc.
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions
7// are met:
8//
9//    Redistributions of source code must retain the above copyright
10//    notice, this list of conditions and the following disclaimer.
11//
12//    Redistributions in binary form must reproduce the above
13//    copyright notice, this list of conditions and the following
14//    disclaimer in the documentation and/or other materials provided
15//    with the distribution.
16//
17//    Neither the name of The Khronos Group Inc. nor the names of its
18//    contributors may be used to endorse or promote products derived
19//    from this software without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32// POSSIBILITY OF SUCH DAMAGE.
33
34#include "TestFixture.h"
35#include "glslang/Public/ResourceLimits.h"
36#include "glslang/Public/ShaderLang.h"
37#include "SPIRV/GlslangToSpv.h"
38#include "SPIRV/disassemble.h"
39#include <gtest/gtest.h>
40#include <sstream>
41#include <string>
42#include <vector>
43
44namespace glslangtest {
45
46class InvertYTest : public ::testing::Test {
47protected:
48    struct CompileResult {
49        bool success;
50        std::string spirvText;
51        std::string infoLog;
52    };
53
54    CompileResult compileGlsl(const std::string& source, EShLanguage stage, bool invertY)
55    {
56        glslang::TShader shader(stage);
57        glslang::TProgram program;
58
59        const char* src = source.c_str();
60        shader.setStrings(&src, 1);
61        shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100);
62        shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
63        shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
64        shader.setInvertY(invertY);
65
66        EShMessages msgs = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
67        if (!shader.parse(GetDefaultResources(), 450, false, msgs)) {
68            return {false, "", shader.getInfoLog()};
69        }
70
71        program.addShader(&shader);
72        if (!program.link(msgs)) {
73            return {false, "", program.getInfoLog()};
74        }
75
76        std::vector<uint32_t> spirv;
77        glslang::GlslangToSpv(*program.getIntermediate(stage), spirv);
78
79        std::ostringstream dis;
80        spv::Disassemble(dis, spirv);
81        return {true, dis.str(), ""};
82    }
83
84    // Helper function to check if the given SPIR-V string contains a specific pattern.
85    bool containsPattern(const std::string& spirvText, const std::string& pattern)
86    {
87        return spirvText.find(pattern) != std::string::npos;
88    }
89
90    // Helper function to check if the given SPIR-V string contains a FNegate instruction.
91    bool containsFNegate(const std::string& spirvText) { return containsPattern(spirvText, "FNegate"); }
92};
93
94// A minimal vertex shader that writes gl_Position.
95static const char* kSimpleVert = R"glsl(
96#version 450
97layout(location = 0) in vec3 pos;
98void main() {
99    gl_Position = vec4(pos, 1.0);
100}
101)glsl";
102
103// A vertex shader that writes gl_Position multiple times to exercise all
104// assignment sites (braces ensure each branch is a sequence node).
105static const char* kMultiAssignVert = R"glsl(
106#version 450
107layout(location = 0) in vec3 pos;
108layout(location = 1) in float w;
109void main() {
110    if (w > 0.0) {
111        gl_Position = vec4(pos, w);
112    } else {
113        gl_Position = vec4(-pos, -w);
114    }
115}
116)glsl";
117
118// A geometry shader (takes triangles, emits a single vertex).
119static const char* kSimpleGeom = R"glsl(
120#version 450
121layout(triangles) in;
122layout(triangle_strip, max_vertices = 3) out;
123void main() {
124    gl_Position = gl_in[0].gl_Position;
125    EmitVertex();
126}
127)glsl";
128
129// A tessellation evaluation shader.
130static const char* kSimpleTese = R"glsl(
131#version 450
132layout(triangles, equal_spacing, ccw) in;
133void main() {
134    gl_Position = gl_in[0].gl_Position;
135}
136)glsl";
137
138// --- Vertex stage ---
139
140TEST_F(InvertYTest, VertexWithInvertYContainsFNegate)
141{
142    auto r = compileGlsl(kSimpleVert, EShLangVertex, true);
143    ASSERT_TRUE(r.success) << r.infoLog;
144    EXPECT_TRUE(containsFNegate(r.spirvText))
145        << "Expected OpFNegate in SPIR-V when setInvertY(true) is used.\n"
146        << r.spirvText;
147}
148
149TEST_F(InvertYTest, VertexWithoutInvertYNoFNegate)
150{
151    auto r = compileGlsl(kSimpleVert, EShLangVertex, false);
152    ASSERT_TRUE(r.success) << r.infoLog;
153    EXPECT_FALSE(containsFNegate(r.spirvText))
154        << "Expected no OpFNegate in SPIR-V when setInvertY(false) is used.\n"
155        << r.spirvText;
156}
157
158TEST_F(InvertYTest, VertexMultipleAssignmentsAllInverted)
159{
160    auto r = compileGlsl(kMultiAssignVert, EShLangVertex, true);
161    ASSERT_TRUE(r.success) << r.infoLog;
162    EXPECT_TRUE(containsFNegate(r.spirvText))
163        << "Expected OpFNegate in SPIR-V for shader with multiple gl_Position assignments.\n"
164        << r.spirvText;
165}
166
167// --- Geometry stage ---
168
169TEST_F(InvertYTest, GeometryWithInvertYContainsFNegate)
170{
171    auto r = compileGlsl(kSimpleGeom, EShLangGeometry, true);
172    ASSERT_TRUE(r.success) << r.infoLog;
173    EXPECT_TRUE(containsFNegate(r.spirvText))
174        << "Expected OpFNegate in geometry shader SPIR-V when setInvertY(true).\n"
175        << r.spirvText;
176}
177
178TEST_F(InvertYTest, GeometryWithoutInvertYNoFNegate)
179{
180    auto r = compileGlsl(kSimpleGeom, EShLangGeometry, false);
181    ASSERT_TRUE(r.success) << r.infoLog;
182    EXPECT_FALSE(containsFNegate(r.spirvText))
183        << "Expected no OpFNegate in geometry shader SPIR-V when setInvertY(false).\n"
184        << r.spirvText;
185}
186
187// --- Tessellation evaluation stage ---
188
189TEST_F(InvertYTest, TessEvalWithInvertYContainsFNegate)
190{
191    auto r = compileGlsl(kSimpleTese, EShLangTessEvaluation, true);
192    ASSERT_TRUE(r.success) << r.infoLog;
193    EXPECT_TRUE(containsFNegate(r.spirvText))
194        << "Expected OpFNegate in tessellation evaluation shader SPIR-V when setInvertY(true).\n"
195        << r.spirvText;
196}
197
198TEST_F(InvertYTest, TessEvalWithoutInvertYNoFNegate)
199{
200    auto r = compileGlsl(kSimpleTese, EShLangTessEvaluation, false);
201    ASSERT_TRUE(r.success) << r.infoLog;
202    EXPECT_FALSE(containsFNegate(r.spirvText))
203        << "Expected no OpFNegate in tessellation evaluation shader SPIR-V when setInvertY(false).\n"
204        << r.spirvText;
205}
206
207// --- Compute stage should not be affected ---
208
209TEST_F(InvertYTest, ComputeWithInvertYFlagNoFNegate)
210{
211    // Compute shaders have no gl_Position; the flag must be a no-op.
212    const char* src = R"glsl(
213#version 450
214layout(local_size_x = 1) in;
215layout(binding = 0) buffer Out { float val; };
216void main() { val = 1.0; }
217)glsl";
218    auto r = compileGlsl(src, EShLangCompute, true);
219    ASSERT_TRUE(r.success) << r.infoLog;
220    EXPECT_FALSE(containsFNegate(r.spirvText))
221        << "OpFNegate should not appear in a compute shader even with setInvertY(true).\n"
222        << r.spirvText;
223}
224
225} // namespace glslangtest