1 //
2 // Copyright 2016 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 // ShaderImage_test.cpp:
7 // Tests for images
8 //
9
10 #include "GLSLANG/ShaderLang.h"
11 #include "angle_gl.h"
12 #include "compiler/translator/StaticType.h"
13 #include "gtest/gtest.h"
14 #include "tests/test_utils/ShaderCompileTreeTest.h"
15 #include "tests/test_utils/compiler_test.h"
16
17 using namespace sh;
18
19 namespace
20 {
21
22 // Checks that the imageStore call with mangled name imageStoreMangledName exists in the AST.
23 // Further each argument is checked whether it matches the expected properties given the compiled
24 // shader.
CheckImageStoreCall(TIntermNode * astRoot,const TString & imageStoreMangledName,TBasicType imageType,int storeLocationNominalSize,TBasicType storeValueType,int storeValueNominalSize)25 void CheckImageStoreCall(TIntermNode *astRoot,
26 const TString &imageStoreMangledName,
27 TBasicType imageType,
28 int storeLocationNominalSize,
29 TBasicType storeValueType,
30 int storeValueNominalSize)
31 {
32 const TIntermAggregate *imageStoreFunctionCall =
33 FindFunctionCallNode(astRoot, imageStoreMangledName);
34 ASSERT_NE(nullptr, imageStoreFunctionCall);
35
36 const TIntermSequence *storeArguments = imageStoreFunctionCall->getSequence();
37 ASSERT_EQ(3u, storeArguments->size());
38
39 const TIntermTyped *storeArgument1Typed = (*storeArguments)[0]->getAsTyped();
40 ASSERT_EQ(imageType, storeArgument1Typed->getBasicType());
41
42 const TIntermTyped *storeArgument2Typed = (*storeArguments)[1]->getAsTyped();
43 ASSERT_EQ(EbtInt, storeArgument2Typed->getBasicType());
44 ASSERT_EQ(storeLocationNominalSize, storeArgument2Typed->getNominalSize());
45
46 const TIntermTyped *storeArgument3Typed = (*storeArguments)[2]->getAsTyped();
47 ASSERT_EQ(storeValueType, storeArgument3Typed->getBasicType());
48 ASSERT_EQ(storeValueNominalSize, storeArgument3Typed->getNominalSize());
49 }
50
51 // Checks that the imageLoad call with mangled name imageLoadMangledName exists in the AST.
52 // Further each argument is checked whether it matches the expected properties given the compiled
53 // shader.
CheckImageLoadCall(TIntermNode * astRoot,const TString & imageLoadMangledName,TBasicType imageType,int loadLocationNominalSize)54 void CheckImageLoadCall(TIntermNode *astRoot,
55 const TString &imageLoadMangledName,
56 TBasicType imageType,
57 int loadLocationNominalSize)
58 {
59 const TIntermAggregate *imageLoadFunctionCall =
60 FindFunctionCallNode(astRoot, imageLoadMangledName);
61 ASSERT_NE(nullptr, imageLoadFunctionCall);
62
63 const TIntermSequence *loadArguments = imageLoadFunctionCall->getSequence();
64 ASSERT_EQ(2u, loadArguments->size());
65
66 const TIntermTyped *loadArgument1Typed = (*loadArguments)[0]->getAsTyped();
67 ASSERT_EQ(imageType, loadArgument1Typed->getBasicType());
68
69 const TIntermTyped *loadArgument2Typed = (*loadArguments)[1]->getAsTyped();
70 ASSERT_EQ(EbtInt, loadArgument2Typed->getBasicType());
71 ASSERT_EQ(loadLocationNominalSize, loadArgument2Typed->getNominalSize());
72 }
73
74 // Checks whether the image is properly exported as a uniform by the compiler.
CheckExportedImageUniform(const std::vector<sh::ShaderVariable> & uniforms,size_t uniformIndex,::GLenum imageTypeGL,const TString & imageName)75 void CheckExportedImageUniform(const std::vector<sh::ShaderVariable> &uniforms,
76 size_t uniformIndex,
77 ::GLenum imageTypeGL,
78 const TString &imageName)
79 {
80 ASSERT_EQ(1u, uniforms.size());
81
82 const auto &imageUniform = uniforms[uniformIndex];
83 ASSERT_EQ(imageTypeGL, imageUniform.type);
84 ASSERT_STREQ(imageUniform.name.c_str(), imageName.c_str());
85 }
86
87 // Checks whether the image is saved in the AST as a node with the correct properties given the
88 // shader.
CheckImageDeclaration(TIntermNode * astRoot,const ImmutableString & imageName,TBasicType imageType,TLayoutImageInternalFormat internalFormat,bool readonly,bool writeonly,bool coherent,bool restrictQualifier,bool volatileQualifier,int binding)89 void CheckImageDeclaration(TIntermNode *astRoot,
90 const ImmutableString &imageName,
91 TBasicType imageType,
92 TLayoutImageInternalFormat internalFormat,
93 bool readonly,
94 bool writeonly,
95 bool coherent,
96 bool restrictQualifier,
97 bool volatileQualifier,
98 int binding)
99 {
100 const TIntermSymbol *myImageNode = FindSymbolNode(astRoot, imageName);
101 ASSERT_NE(nullptr, myImageNode);
102
103 ASSERT_EQ(imageType, myImageNode->getBasicType());
104 const TType &myImageType = myImageNode->getType();
105 TLayoutQualifier myImageLayoutQualifier = myImageType.getLayoutQualifier();
106 ASSERT_EQ(internalFormat, myImageLayoutQualifier.imageInternalFormat);
107 TMemoryQualifier myImageMemoryQualifier = myImageType.getMemoryQualifier();
108 ASSERT_EQ(readonly, myImageMemoryQualifier.readonly);
109 ASSERT_EQ(writeonly, myImageMemoryQualifier.writeonly);
110 ASSERT_EQ(coherent, myImageMemoryQualifier.coherent);
111 ASSERT_EQ(restrictQualifier, myImageMemoryQualifier.restrictQualifier);
112 ASSERT_EQ(volatileQualifier, myImageMemoryQualifier.volatileQualifier);
113 ASSERT_EQ(binding, myImageType.getLayoutQualifier().binding);
114 }
115
116 } // namespace
117
118 class ShaderImageTest : public ShaderCompileTreeTest
119 {
120 public:
ShaderImageTest()121 ShaderImageTest() {}
122
123 protected:
SetUp()124 void SetUp() override { ShaderCompileTreeTest::SetUp(); }
125
getShaderType() const126 ::GLenum getShaderType() const override { return GL_COMPUTE_SHADER; }
getShaderSpec() const127 ShShaderSpec getShaderSpec() const override { return SH_GLES3_1_SPEC; }
128 };
129
130 // Test that an image2D is properly parsed and exported as a uniform.
TEST_F(ShaderImageTest,Image2DDeclaration)131 TEST_F(ShaderImageTest, Image2DDeclaration)
132 {
133 const std::string &shaderString =
134 "#version 310 es\n"
135 "layout(local_size_x = 4) in;\n"
136 "layout(rgba32f, binding = 1) uniform highp readonly image2D myImage;\n"
137 "void main() {\n"
138 " ivec2 sz = imageSize(myImage);\n"
139 "}";
140 if (!compile(shaderString))
141 {
142 FAIL() << "Shader compilation failed" << mInfoLog;
143 }
144
145 CheckExportedImageUniform(getUniforms(), 0, GL_IMAGE_2D, "myImage");
146 CheckImageDeclaration(mASTRoot, ImmutableString("myImage"), EbtImage2D, EiifRGBA32F, true,
147 false, false, false, false, 1);
148 }
149
150 // Test that an image3D is properly parsed and exported as a uniform.
TEST_F(ShaderImageTest,Image3DDeclaration)151 TEST_F(ShaderImageTest, Image3DDeclaration)
152 {
153 const std::string &shaderString =
154 "#version 310 es\n"
155 "layout(local_size_x = 4) in;\n"
156 "layout(rgba32ui, binding = 3) uniform highp writeonly readonly uimage3D myImage;\n"
157 "void main() {\n"
158 " ivec3 sz = imageSize(myImage);\n"
159 "}";
160 if (!compile(shaderString))
161 {
162 FAIL() << "Shader compilation failed" << mInfoLog;
163 }
164
165 CheckExportedImageUniform(getUniforms(), 0, GL_UNSIGNED_INT_IMAGE_3D, "myImage");
166 CheckImageDeclaration(mASTRoot, ImmutableString("myImage"), EbtUImage3D, EiifRGBA32UI, true,
167 true, false, false, false, 3);
168 }
169
170 // Check that imageLoad calls get correctly parsed.
TEST_F(ShaderImageTest,ImageLoad)171 TEST_F(ShaderImageTest, ImageLoad)
172 {
173 const std::string &shaderString =
174 "#version 310 es\n"
175 "layout(local_size_x = 4) in;\n"
176 "layout(rgba32f) uniform highp readonly image2D my2DImageInput;\n"
177 "layout(rgba32i) uniform highp readonly iimage3D my3DImageInput;\n"
178 "layout(rgba32f) uniform highp writeonly image2D imageOutput;\n"
179 "void main() {\n"
180 " vec4 result = imageLoad(my2DImageInput, ivec2(gl_LocalInvocationID.xy));\n"
181 " ivec4 result2 = imageLoad(my3DImageInput, ivec3(gl_LocalInvocationID.xyz));\n"
182 " // Ensure the imageLoad calls are not dead-code eliminated\n"
183 " imageStore(imageOutput, ivec2(0), result + vec4(result2));\n"
184 "}";
185 if (!compile(shaderString))
186 {
187 FAIL() << "Shader compilation failed" << mInfoLog;
188 }
189
190 // imageLoad call with image2D passed
191 std::string mangledName2D = "imageLoad(";
192 mangledName2D += StaticType::GetBasic<EbtImage2D, EbpUndefined>()->getMangledName();
193 mangledName2D += StaticType::GetBasic<EbtInt, EbpUndefined, 2>()->getMangledName();
194 CheckImageLoadCall(mASTRoot, mangledName2D.c_str(), EbtImage2D, 2);
195
196 // imageLoad call with image3D passed
197 std::string mangledName3D = "imageLoad(";
198 mangledName3D += StaticType::GetBasic<EbtIImage3D, EbpUndefined>()->getMangledName();
199 mangledName3D += StaticType::GetBasic<EbtInt, EbpUndefined, 3>()->getMangledName();
200 CheckImageLoadCall(mASTRoot, mangledName3D.c_str(), EbtIImage3D, 3);
201 }
202
203 // Check that imageStore calls get correctly parsed.
TEST_F(ShaderImageTest,ImageStore)204 TEST_F(ShaderImageTest, ImageStore)
205 {
206 const std::string &shaderString =
207 "#version 310 es\n"
208 "layout(local_size_x = 4) in;\n"
209 "layout(rgba32f) uniform highp writeonly image2D my2DImageOutput;\n"
210 "layout(rgba32ui) uniform highp writeonly uimage2DArray my2DImageArrayOutput;\n"
211 "void main() {\n"
212 " imageStore(my2DImageOutput, ivec2(gl_LocalInvocationID.xy), vec4(0.0));\n"
213 " imageStore(my2DImageArrayOutput, ivec3(gl_LocalInvocationID.xyz), uvec4(0));\n"
214 "}";
215 if (!compile(shaderString))
216 {
217 FAIL() << "Shader compilation failed" << mInfoLog;
218 }
219
220 // imageStore call with image2D
221 std::string mangledName2D = "imageStore(";
222 mangledName2D += StaticType::GetBasic<EbtImage2D, EbpUndefined>()->getMangledName();
223 mangledName2D += StaticType::GetBasic<EbtInt, EbpUndefined, 2>()->getMangledName();
224 mangledName2D += StaticType::GetBasic<EbtFloat, EbpUndefined, 4>()->getMangledName();
225 CheckImageStoreCall(mASTRoot, mangledName2D.c_str(), EbtImage2D, 2, EbtFloat, 4);
226
227 // imageStore call with image2DArray
228 std::string mangledName2DArray = "imageStore(";
229 mangledName2DArray += StaticType::GetBasic<EbtUImage2DArray, EbpUndefined>()->getMangledName();
230 mangledName2DArray += StaticType::GetBasic<EbtInt, EbpUndefined, 3>()->getMangledName();
231 mangledName2DArray += StaticType::GetBasic<EbtUInt, EbpUndefined, 4>()->getMangledName();
232 CheckImageStoreCall(mASTRoot, mangledName2DArray.c_str(), EbtUImage2DArray, 3, EbtUInt, 4);
233 }
234
235 // Check that memory qualifiers are correctly parsed.
TEST_F(ShaderImageTest,ImageMemoryQualifiers)236 TEST_F(ShaderImageTest, ImageMemoryQualifiers)
237 {
238 const std::string &shaderString =
239 "#version 310 es\n"
240 "layout(local_size_x = 4) in;"
241 "layout(rgba32f) uniform highp coherent readonly image2D image1;\n"
242 "layout(rgba32f) uniform highp volatile writeonly image2D image2;\n"
243 "layout(rgba32f) uniform highp volatile restrict readonly writeonly image2D image3;\n"
244 "void main() {\n"
245 " imageSize(image1);\n"
246 " imageSize(image2);\n"
247 " imageSize(image3);\n"
248 "}";
249 if (!compile(shaderString))
250 {
251 FAIL() << "Shader compilation failed" << mInfoLog;
252 }
253
254 CheckImageDeclaration(mASTRoot, ImmutableString("image1"), EbtImage2D, EiifRGBA32F, true, false,
255 true, false, false, -1);
256 CheckImageDeclaration(mASTRoot, ImmutableString("image2"), EbtImage2D, EiifRGBA32F, false, true,
257 true, false, true, -1);
258 CheckImageDeclaration(mASTRoot, ImmutableString("image3"), EbtImage2D, EiifRGBA32F, true, true,
259 true, true, true, -1);
260 }
261