2 * Copyright © 2019 Advanced Micro Devices, Inc.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 /** @file image_functions.c
26 * Test the new GLSL image functions.
28 * Two categories of tests in this file:
29 * - for the functions existing both in ARB_shader_image_load_store and
30 * EXT_shader_image_load_store, we simply build a program to verify that
32 * - for the 2 functions that only exist in EXT (atomicIncWrap and atomicDecWrap),
33 * we verify their behavior. Note: on nvidia this test needs to be used with
37 #include "piglit-util-gl.h"
39 PIGLIT_GL_TEST_CONFIG_BEGIN
41 config
.supports_gl_core_version
= 32;
43 PIGLIT_GL_TEST_CONFIG_END
46 const char* function_name
;
47 unsigned (*compute_ref_value
) (int num_exec
, unsigned wrap
);
49 GLuint (*create_texture
) (unsigned tex_width
);
50 void (*read_texture
) (void* data
, size_t s
);
54 create_texture(unsigned tex_width
)
58 glGenTextures(1, &texture
);
59 glBindTexture(GL_TEXTURE_1D
, texture
);
61 glTexParameteri(GL_TEXTURE_1D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
62 glTexParameteri(GL_TEXTURE_1D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
63 glTexParameteri(GL_TEXTURE_1D
, GL_TEXTURE_WRAP_S
, GL_REPEAT
);
64 glTexParameteri(GL_TEXTURE_1D
, GL_TEXTURE_WRAP_T
, GL_REPEAT
);
66 data
= (int*) malloc(tex_width
* sizeof(int));
67 memset(data
, 0, tex_width
* sizeof(int));
68 glTexImage1D(GL_TEXTURE_1D
, 0, GL_R32I
, tex_width
, 0, GL_RED_INTEGER
, GL_INT
, data
);
75 read_texture(void* data
, size_t s
)
77 glGetTexImage(GL_TEXTURE_1D
, 0,
84 create_buffer_texture(unsigned tex_width
)
87 GLuint texture
, buffer
;
88 glGenTextures(1, &texture
);
89 glBindTexture(GL_TEXTURE_BUFFER
, texture
);
91 data
= (int*) malloc(tex_width
* sizeof(int));
92 memset(data
, 0, tex_width
* sizeof(int));
94 glGenBuffers(1, &buffer
);
95 glBindBuffer(GL_ARRAY_BUFFER
, buffer
);
96 glBufferStorage(GL_ARRAY_BUFFER
, tex_width
* sizeof(int), data
, GL_MAP_READ_BIT
);
98 glTexBuffer(GL_TEXTURE_BUFFER
, GL_R32I
, buffer
);
105 read_buffer_texture(void* data
, size_t s
)
108 glGetIntegerv(GL_TEXTURE_BINDING_BUFFER
, &buffer
);
109 glGetBufferSubData(GL_ARRAY_BUFFER
, 0, s
, data
);
110 glDeleteBuffers(1, (GLuint
*) &buffer
);
113 static enum piglit_result
114 run_test(void * _data
)
117 "attribute vec4 piglit_vertex;\n"
120 "gl_Position = piglit_vertex;\n"
123 static const char* fs_template
=
125 "#extension GL_EXT_shader_image_load_store : enable\n"
126 "uniform int index;\n"
127 "uniform uint wrap_value;\n"
128 "layout(size1x32) uniform %s image;\n"
130 " uint res = %s(image, index, wrap_value);\n"
131 " gl_FragColor.rgb = vec3(float(res) / float(wrap_value));\n"
132 " gl_FragColor.a = 1.0;\n"
137 const struct test_data
* test
= (const struct test_data
*) _data
;
139 sprintf(fs
, fs_template
, test
->type
, test
->function_name
);
141 GLint program
= piglit_build_simple_program(vs
, fs
);
142 GLint image_location
= glGetUniformLocation(program
, "image");
143 GLint wrap_location
= glGetUniformLocation(program
, "wrap_value");
144 GLint index_location
= glGetUniformLocation(program
, "index");
146 int texture_size
[] = {32, 50, 60, 128};
147 int wrap_value
[] = {12, 29, 17, 41};
148 GLint read_back
[128];
149 for (int i
= 0; i
< ARRAY_SIZE(texture_size
); i
++) {
150 GLuint texture
= test
->create_texture(texture_size
[i
]);
151 int index
= rand() % texture_size
[i
];
152 glBindImageTextureEXT(0, texture
, 0, GL_FALSE
, 0, GL_READ_WRITE
, GL_R32I
);
154 glUseProgram(program
);
155 glUniform1i(image_location
, 0);
156 glUniform1ui(wrap_location
, wrap_value
[i
]);
157 glUniform1i(index_location
, index
);
159 piglit_draw_rect(-1, -1, 2.0, 2.0);
161 glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT
|
162 GL_BUFFER_UPDATE_BARRIER_BIT
|
163 GL_PIXEL_BUFFER_BARRIER_BIT
|
164 GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
);
166 test
->read_texture(read_back
, texture_size
[i
] * sizeof(int));
168 GLuint ref_value
= test
->compute_ref_value(piglit_width
* piglit_height
, wrap_value
[i
]);
170 /* The first component of the pixel at index has been written to by all invocations */
171 pass
= pass
&& read_back
[index
] == ref_value
;
173 /* All other pixels/components should be untouched */
174 for (int j
= 0; j
< texture_size
[i
]; j
++) {
175 pass
= pass
&& (j
== index
|| read_back
[j
] == 0);
177 glDeleteTextures(1, &texture
);
179 piglit_present_results();
182 glDeleteProgram(program
);
183 return pass
&& piglit_check_gl_error(GL_NO_ERROR
) ?
184 PIGLIT_PASS
: PIGLIT_FAIL
;
187 static enum piglit_result
188 run_compile_test(void * data
)
190 /* Type of the uniform variable used in imageStore */
191 static const char* v_value_type
[] = { "vec4", "ivec4", "uvec4" };
192 /* Type of the uniform variable used in the atomic operations */
193 static const char* i_value_type
[] = { "float", "int", "uint" };
194 /* Layout qualifier for the image */
195 static const char* qualifiers
[] = {
196 "size1x8", "size1x16", "size1x32", "size2x32", "size4x32"
198 /* 'coord' variable */
199 static const char* coords
[] = {
201 "ivec2 coord = ivec2(0)",
202 "ivec3 coord = ivec3(0)",
204 /* Types of the image variable using a 'int' for the coord */
205 static const char* types_int
[] = {
206 "image1D", "iimage1D", "uimage1D", "imageBuffer", NULL
,
208 /* Types of the image variable using a 'ivec2' for the coord */
209 static const char* types_ivec2
[] = {
210 "image2D", "iimage2D", "uimage2D",
211 "image2DRect", "iimage2DRect", "uimage2DRect",
212 "image1DArray", "iimage1DArray", "uimage1DArray",
215 /* Types of the image variable using a 'ivec3' for the coord */
216 static const char* types_ivec3
[] = {
217 "image3D", "iimage3D", "uimage3D",
218 "imageCube", "iimageCube", "uimageCube",
219 "image2DArray", "iimage2DArray", "uimage2DArray",
220 "imageCubeArray", "iimageCubeArray", "uimageCubeArray",
223 /* Fragment shader template */
224 static const char* fs_template
=
226 "#extension GL_ARB_ES3_1_compatibility: %s\n"
227 "#extension GL_EXT_shader_image_load_store : enable\n"
228 "uniform uint wrap_value;\n"
229 "uniform %s v_value;\n"
230 "uniform %s i_value;\n"
231 "layout(%s) uniform %s image;\n"
238 enum piglit_result res
= PIGLIT_PASS
;
240 static const char** types
[] = {
241 types_int
, types_ivec2
, types_ivec3
244 const bool atomicOp
= strstr(data
, "Atomic") != NULL
;
245 const bool atomicOpWrap
= atomicOp
&& strstr(data
, "Wrap") != NULL
;
246 const bool atomicOpExchange
= atomicOp
&& strstr(data
, "Exchange") != NULL
;
247 bool enable_arb_es31_compat
= false;
249 for (int i
= 0; i
< ARRAY_SIZE(qualifiers
); i
++) {
250 for (int j
= 0; j
< ARRAY_SIZE(types
); j
++) {
251 for (int k
= 0; types
[j
][k
]; k
++) {
252 bool is_valid
= true;
253 bool floatImageType
= strncmp(types
[j
][k
], "image", 5) == 0;
254 bool signedImageType
= strncmp(types
[j
][k
], "iimage", 6) == 0;
256 /* The EXT_shader_image_load_store spec says:
257 * These functions [atomic functions] support 32-bit unsigned integer
258 * operands and 32-bit signed integer operands.
260 if (atomicOp
&& floatImageType
) {
262 /* The GL_ARB_ES3_1_compatibility says:
263 * a new GLSL built-in function, imageAtomicExchange, which performs atomic
264 * exchanges on r32f floating point images.
266 if (atomicOpExchange
&& piglit_is_extension_supported("GL_ARB_ES3_1_compatibility")) {
267 enable_arb_es31_compat
= true;
272 /* The EXT_shader_image_load_store spec says:
273 * These functions [imageAtomicIncWrap and imageAtomicDecWrap] support
274 * only 32-bit unsigned integer operands.
276 if (atomicOpWrap
&& signedImageType
)
279 /* The EXT_shader_image_load_store spec says:
280 * A layout of "size1x8" is illegal for image variables associated
281 * with floating-point data types.
283 if (floatImageType
&& i
== 0)
287 * The format of the image unit must be in the "1x32" equivalence
288 * class [...] otherwise the atomic operation is invalid.
290 if (atomicOp
&& i
!= 2)
293 /* Build the fragment template */
294 sprintf(fs
, fs_template
,
295 enable_arb_es31_compat
? "enable" : "disable",
303 /* And verify we can build the fragment shader */
304 GLint program
= piglit_compile_shader_text_nothrow(GL_FRAGMENT_SHADER
, fs
, is_valid
);
305 if (is_valid
!= (bool) program
) {
309 glDeleteShader(program
);
317 static unsigned compute_imageAtomicIncWrap(int num_exec
, unsigned wrap
)
320 /* The EXT_shader_image_load_store spec says:
322 * imageAtomicIncWrap() computes a new value by adding one to the contents of
323 * the selected texel, and then forcing the result to zero if and only if the
324 * incremented value is greater than or equal to <wrap>. These functions
325 * support only 32-bit unsigned integer operands.
327 for (int i
= 0; i
< num_exec
; i
++) {
329 /* nvidia and amdgpu-pro drivers interprets the spec as > instead of >= */
337 static unsigned compute_imageAtomicDecWrap(int num_exec
, unsigned wrap
)
340 /* The EXT_shader_image_load_store spec says:
342 * imageAtomicDecWrap() computes a new value by subtracting one from the
343 * contents of the selected texel, and then forcing the result to <wrap>-1 if
344 * the original value read from the selected texel was either zero or greater
345 * than <wrap>. These functions support only 32-bit unsigned integer
348 for (int i
= 0; i
< num_exec
; i
++) {
349 if (value
== 0 || value
> wrap
) {
350 /* nvidia and amdgpu-pro drivers wraps to "wrap" and not "wrap - 1" */
360 piglit_init(int argc
, char **argv
)
362 struct test_data test_data
[] = {
364 "imageAtomicIncWrap",
365 compute_imageAtomicIncWrap
,
371 "imageAtomicIncWrap",
372 compute_imageAtomicIncWrap
,
374 create_buffer_texture
,
378 "imageAtomicDecWrap",
379 compute_imageAtomicDecWrap
,
385 "imageAtomicDecWrap",
386 compute_imageAtomicDecWrap
,
388 create_buffer_texture
,
392 const struct piglit_subtest tests
[] =
395 "imageAtomicIncWrap uimage1D",
401 "imageAtomicIncWrap uimageBuffer",
407 "imageAtomicDecWrap uimage1D",
413 "imageAtomicDecWrap uimageBuffer",
418 /* Compile only tests */
423 "imageLoad(image, coord)",
429 "imageStore(image, coord, v_value)",
435 "imageAtomicAdd(image, coord, i_value)",
441 "imageAtomicMin(image, coord, i_value)",
447 "imageAtomicMax(image, coord, i_value)",
453 "imageAtomicAnd(image, coord, i_value)",
459 "imageAtomicOr(image, coord, i_value)",
465 "imageAtomicXor(image, coord, i_value)",
468 "imageAtomicExchange",
471 "imageAtomicExchange(image, coord, i_value)",
474 "imageAtomicCompSwap",
477 "imageAtomicCompSwap(image, coord, i_value, i_value)",
480 "imageAtomicIncWrap",
483 "imageAtomicIncWrap(image, coord, wrap_value)",
486 "imageAtomicDecWrap",
489 "imageAtomicDecWrap(image, coord, wrap_value)",
494 piglit_require_extension("GL_EXT_shader_image_load_store");
496 enum piglit_result result
= PIGLIT_PASS
;
498 result
= piglit_run_selected_subtests(
504 piglit_report_result(result
);