xref: /aosp_15_r20/external/angle/src/tests/compiler_tests/Precise_test.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // Precise_test.cpp:
7 //   Test that precise produces the right number of NoContraction decorations in the generated
8 //   SPIR-V.
9 //
10 
11 #include "GLSLANG/ShaderLang.h"
12 #include "angle_gl.h"
13 #include "common/spirv/spirv_instruction_parser_autogen.h"
14 #include "gtest/gtest.h"
15 
16 namespace spirv = angle::spirv;
17 
18 namespace
19 {
20 class PreciseTest : public testing::TestWithParam<bool>
21 {
22   public:
SetUp()23     void SetUp() override
24     {
25         std::map<ShShaderOutput, std::string> shaderOutputList = {
26             {SH_SPIRV_VULKAN_OUTPUT, "SH_SPIRV_VULKAN_OUTPUT"}};
27 
28         Initialize(shaderOutputList);
29     }
30 
TearDown()31     void TearDown() override
32     {
33         for (auto shaderOutputType : mShaderOutputList)
34         {
35             DestroyCompiler(shaderOutputType.first);
36         }
37     }
38 
Initialize(std::map<ShShaderOutput,std::string> & shaderOutputList)39     void Initialize(std::map<ShShaderOutput, std::string> &shaderOutputList)
40     {
41         mShaderOutputList = std::move(shaderOutputList);
42 
43         for (auto shaderOutputType : mShaderOutputList)
44         {
45             sh::InitBuiltInResources(&mResourceList[shaderOutputType.first]);
46             mCompilerList[shaderOutputType.first] = nullptr;
47         }
48     }
49 
DestroyCompiler(ShShaderOutput shaderOutputType)50     void DestroyCompiler(ShShaderOutput shaderOutputType)
51     {
52         if (mCompilerList[shaderOutputType])
53         {
54             sh::Destruct(mCompilerList[shaderOutputType]);
55             mCompilerList[shaderOutputType] = nullptr;
56         }
57     }
58 
InitializeCompiler()59     void InitializeCompiler()
60     {
61         for (auto shaderOutputType : mShaderOutputList)
62         {
63             InitializeCompiler(shaderOutputType.first);
64         }
65     }
66 
InitializeCompiler(ShShaderOutput shaderOutputType)67     void InitializeCompiler(ShShaderOutput shaderOutputType)
68     {
69         DestroyCompiler(shaderOutputType);
70 
71         mCompilerList[shaderOutputType] = sh::ConstructCompiler(
72             GL_VERTEX_SHADER, SH_GLES3_2_SPEC, shaderOutputType, &mResourceList[shaderOutputType]);
73         ASSERT_TRUE(mCompilerList[shaderOutputType] != nullptr)
74             << "Compiler for " << mShaderOutputList[shaderOutputType]
75             << " could not be constructed.";
76     }
77 
TestShaderCompile(ShShaderOutput shaderOutputType,const char * shaderSource)78     testing::AssertionResult TestShaderCompile(ShShaderOutput shaderOutputType,
79                                                const char *shaderSource)
80     {
81         const char *shaderStrings[] = {shaderSource};
82 
83         ShCompileOptions options = {};
84         options.objectCode       = true;
85 
86         bool success = sh::Compile(mCompilerList[shaderOutputType], shaderStrings, 1, options);
87         if (success)
88         {
89             return ::testing::AssertionSuccess()
90                    << "Compilation success(" << mShaderOutputList[shaderOutputType] << ")";
91         }
92         return ::testing::AssertionFailure() << sh::GetInfoLog(mCompilerList[shaderOutputType]);
93     }
94 
TestShaderCompile(const char * shaderSource,size_t expectedNoContractionDecorationCount)95     void TestShaderCompile(const char *shaderSource, size_t expectedNoContractionDecorationCount)
96     {
97         for (auto shaderOutputType : mShaderOutputList)
98         {
99             EXPECT_TRUE(TestShaderCompile(shaderOutputType.first, shaderSource));
100 
101             const spirv::Blob &blob =
102                 sh::GetObjectBinaryBlob(mCompilerList[shaderOutputType.first]);
103             ValidateDecorations(blob, expectedNoContractionDecorationCount);
104         }
105     }
106 
107     void ValidateDecorations(const spirv::Blob &blob, size_t expectedNoContractionDecorationCount);
108 
109   private:
110     std::map<ShShaderOutput, std::string> mShaderOutputList;
111     std::map<ShShaderOutput, ShHandle> mCompilerList;
112     std::map<ShShaderOutput, ShBuiltInResources> mResourceList;
113 };
114 
115 // Parse the SPIR-V and verify that there are as many NoContraction decorations as expected.
ValidateDecorations(const spirv::Blob & blob,size_t expectedNoContractionDecorationCount)116 void PreciseTest::ValidateDecorations(const spirv::Blob &blob,
117                                       size_t expectedNoContractionDecorationCount)
118 {
119     size_t currentWord                  = spirv::kHeaderIndexInstructions;
120     size_t noContractionDecorationCount = 0;
121 
122     while (currentWord < blob.size())
123     {
124         uint32_t wordCount;
125         spv::Op opCode;
126         const uint32_t *instruction = &blob[currentWord];
127         spirv::GetInstructionOpAndLength(instruction, &opCode, &wordCount);
128 
129         currentWord += wordCount;
130 
131         // Early out when the decorations section is visited.
132         if (opCode == spv::OpTypeVoid || opCode == spv::OpTypeInt || opCode == spv::OpTypeFloat ||
133             opCode == spv::OpTypeBool)
134         {
135             break;
136         }
137 
138         if (opCode == spv::OpMemberDecorate)
139         {
140             spirv::IdRef type;
141             spirv::LiteralInteger member;
142             spv::Decoration decoration;
143             spirv::ParseMemberDecorate(instruction, &type, &member, &decoration, nullptr);
144 
145             // NoContraction should be applied to arithmetic instructions, and should not be seen on
146             // block members.
147             EXPECT_NE(decoration, spv::DecorationNoContraction);
148         }
149         else if (opCode == spv::OpDecorate)
150         {
151             spirv::IdRef target;
152             spv::Decoration decoration;
153             spirv::ParseDecorate(instruction, &target, &decoration, nullptr);
154 
155             if (decoration == spv::DecorationNoContraction)
156             {
157                 ++noContractionDecorationCount;
158             }
159         }
160     }
161 
162     EXPECT_EQ(noContractionDecorationCount, expectedNoContractionDecorationCount);
163 }
164 
165 // Test that precise on a local variable works.
TEST_F(PreciseTest,LocalVariable)166 TEST_F(PreciseTest, LocalVariable)
167 {
168     constexpr char kVS[] = R"(#version 320 es
169 
170 uniform float u;
171 
172 void main()
173 {
174     float f1 = u, f2 = u;   // f1 is precise, but f2 isn't.
175 
176     f1 += 1.0;              // NoContraction
177     f2 += 1.0;
178 
179     float f3 = f1 * f1;     // NoContraction
180     f3 /= 2.0;              // NoContraction
181 
182     int i1 = int(f3);       // i1 is precise
183     ++i1;                   // NoContraction
184     --i1;                   // NoContraction
185     i1++;                   // NoContraction
186     i1--;                   // NoContraction
187 
188     int i2 = i1 % 3;
189     f2 -= float(i2);
190 
191     precise float p = float(i1) / 1.5;  // NoContraction
192 
193     gl_Position = vec4(p, f2, 0, 0);
194 })";
195 
196     InitializeCompiler();
197     TestShaderCompile(kVS, 8);
198 }
199 
200 // Test that precise on gl_Position works.
TEST_F(PreciseTest,Position)201 TEST_F(PreciseTest, Position)
202 {
203     constexpr char kVS[] = R"(#version 320 es
204 
205 uniform float u;
206 
207 out float o;
208 
209 precise gl_Position;
210 
211 void main()
212 {
213     mat4 m1 = mat4(u);      // m1 is precise, even if not all components are used to determine the
214                             // gl_Position.
215     vec4 v1 = vec4(u);      // v1 is precise
216 
217     vec4 v2 = m1 * v1;
218     v1 *= m1;               // NoContraction
219     m1 *= m1;               // NoContraction
220     m1 *= u;                // NoContraction
221     v1 *= u;                // NoContraction
222 
223     float f1 = dot(v1, v1);
224     float f2 = dot(v1, v1); // NoContraction
225 
226     gl_Position = vec4(m1[0][0], v1[0], f2, 0);
227     o = f1;
228 })";
229 
230     InitializeCompiler();
231     TestShaderCompile(kVS, 5);
232 }
233 
234 // Test that precise on function parameters and return value works.
TEST_F(PreciseTest,Functions)235 TEST_F(PreciseTest, Functions)
236 {
237     constexpr char kVS[] = R"(#version 320 es
238 
239 uniform float u;
240 
241 struct S1
242 {
243     float f;
244     int i;
245 };
246 
247 precise float f1(S1 s, out int io)
248 {
249     float f = s.f;          // f is precise
250     f *= float(s.i);        // NoContraction
251 
252     io = s.i;
253     ++io;
254 
255     return s.f / f;         // NoContraction
256 }
257 
258 void f2(S1 s, precise out S1 so)
259 {
260     float f = s.f;          // f is precise
261     f /= float(s.i);        // NoContraction
262 
263     int i = s.i;            // i is precise
264     ++i;                    // NoContraction
265 
266     so = S1(f, i);
267 }
268 
269 void main()
270 {
271     precise S1 s1;
272     S1 s2;
273 
274     int i;
275     float f = f1(s1, i);    // f1's return value being precise doesn't affect f
276 
277     f2(s1, s2);             // f2's out parameter being precise doesn't affect s2
278 
279     i /= 2;
280     f *= 2.0;
281     s2.f += float(s2.i);
282 
283     gl_Position = vec4(s1.f);
284 })";
285 
286     InitializeCompiler();
287     TestShaderCompile(kVS, 4);
288 }
289 
290 }  // anonymous namespace
291