1 /*
2 * Copyright © 2013 Chris Forbes
3 * Copyright 2014 Intel Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 /** @file texture-storage-multisample.c
26 *
27 * Based on arb_texture_storage_multisample/tex-storage.c by Chris Forbes and
28 * piglit_multisample_texture in piglit_util_gl.c by Jason Ekstrand.
29 * Adapted to test glTextureStorage2DMultisample and
30 * glTextureStorage3DMultisample by Laura Ekstrand (laura@jlekstrand.net).
31 */
32
33 #include "piglit-util-gl.h"
34
35 PIGLIT_GL_TEST_CONFIG_BEGIN
36
37 config.supports_gl_core_version = 31;
38
39 config.window_visual = PIGLIT_GL_VISUAL_RGBA |
40 PIGLIT_GL_VISUAL_DOUBLE;
41 config.khr_no_error_support = PIGLIT_NO_ERRORS;
42
43 PIGLIT_GL_TEST_CONFIG_END
44
45 /* This has the modelview matrix built in. */
46 static const char multisample_texture_vs_source[] =
47 "#version 140\n"
48 "in vec2 piglit_vertex;\n"
49 "out vec2 tex_coords;\n"
50 "void main()\n"
51 "{\n"
52 " tex_coords = piglit_vertex;\n"
53 " vec2 pos = (piglit_vertex.xy * 2) - vec2(1, 1);\n"
54 " gl_Position = vec4(pos, 0, 1);\n"
55 "}\n"
56 ;
57
58 static const char multisample_texture_fs_source[] =
59 "#version 140\n"
60 "#extension GL_ARB_sample_shading : enable\n"
61 "in vec2 tex_coords;\n"
62 "uniform sampler2DArray tex;\n"
63 "uniform int tex_depth;\n"
64 "uniform int z;\n"
65 "void main()\n"
66 "{\n"
67 " int layer = (gl_SampleID * tex_depth) + z;\n"
68 " gl_FragColor = texture(tex, vec3(tex_coords, layer));\n"
69 "}\n"
70 ;
71
72 /**
73 * Uploads an arbitrary multisample texture.
74 * TODO: Make this part of Mesa meta?
75 *
76 * This function acts like glTexSub*Image for multisample textures.
77 * For the texture given, it assumes that glTexImage[23]DMultisample or
78 * glTex*Storage[23]DMultisample has already been called to establish the
79 * storage.
80 *
81 * When this function returns, multisample texture will be bound to the
82 * currently active texture.
83 *
84 * \param tex Texture name for a previously initialized texture.
85 * \param target either GL_TEXTURE_2D_MULTISAMPLE or
86 * GL_TEXTURE2D_MULTISAMPLE_ARRAY
87 * \param internalformat a renderable color format accepted by
88 * glTexImage2DMultisample
89 * \param width texture width
90 * \param height texture height
91 * \param depth texture depth. If target is
92 * GL_TEXTURE_2D_MULTISAMPLE, this must be 1.
93 * \param samples the number of samples
94 * \param format format of the pixel data
95 * \param type type of the pixel data
96 * \param data pixel data with which to fill the texture
97 * You need data for each sample. The samples should be
98 * specified in depth.
99 *
100 */
101 void
texture_sub_image_multisample(GLenum tex,GLenum target,GLenum internalFormat,unsigned width,unsigned height,unsigned depth,unsigned samples,GLenum format,GLenum type,void * data)102 texture_sub_image_multisample(GLenum tex, GLenum target,
103 GLenum internalFormat, unsigned width,
104 unsigned height, unsigned depth,
105 unsigned samples, GLenum format, GLenum type,
106 void *data)
107 {
108 static GLuint prog = 0;
109 static GLint tex_loc, tex_depth_loc, z_loc;
110 static GLuint fbo, array_tex;
111 unsigned z;
112
113 struct {
114 GLint active_tex;
115 GLint draw_fbo;
116 GLint prog;
117 GLint viewport[4];
118 GLboolean arb_sample_shading;
119 GLfloat min_sample_shading;
120 GLint clamp_fragment_color;
121 } backup;
122
123 piglit_require_extension("GL_ARB_texture_multisample");
124 piglit_require_extension("GL_ARB_sample_shading");
125
126 if (target == GL_TEXTURE_2D_MULTISAMPLE) {
127 assert(depth == 1);
128 } else if (target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
129 /* empty */
130 } else {
131 assert(!"Invalid texture target");
132 return;
133 }
134
135 if (prog == 0) {
136 /* First-run setup */
137 prog = piglit_build_simple_program(
138 multisample_texture_vs_source,
139 multisample_texture_fs_source);
140
141 tex_loc = glGetUniformLocation(prog, "tex");
142 tex_depth_loc = glGetUniformLocation(prog, "tex_depth");
143 z_loc = glGetUniformLocation(prog, "z");
144
145 glGenFramebuffers(1, &fbo);
146 glGenTextures(1, &array_tex);
147 }
148
149 /* Backup client values so we can restore them later */
150 glGetIntegerv(GL_ACTIVE_TEXTURE, &backup.active_tex);
151 glGetIntegerv(GL_CURRENT_PROGRAM, &backup.prog);
152 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &backup.draw_fbo);
153 glGetIntegerv(GL_VIEWPORT, backup.viewport);
154 glGetBooleanv(GL_SAMPLE_SHADING_ARB, &backup.arb_sample_shading);
155 glGetFloatv(GL_MIN_SAMPLE_SHADING_VALUE_ARB, &backup.min_sample_shading);
156
157 /* This ensures that copying is done on a per-sample basis rather than
158 * the default per-pixel basis.
159 */
160 glEnable(GL_SAMPLE_SHADING_ARB);
161 glMinSampleShadingARB(1.0f);
162
163 /* Load the data into a texture for drawing. */
164 glBindTexture(GL_TEXTURE_2D_ARRAY, array_tex);
165 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
166 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
167 glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, internalFormat, width, height,
168 depth * samples, 0, format, type, data);
169
170 /* Bind the special FBO and attach our texture to it. */
171 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
172 glViewport(0, 0, width, height);
173
174 glUseProgram(prog);
175 glUniform1i(tex_loc, backup.active_tex - GL_TEXTURE0);
176 glUniform1i(tex_depth_loc, depth);
177
178
179 /* When we call draw arrays, the data (in array_tex) will get drawn
180 * into our texture (in tex) because it's attached to
181 * the framebuffer.
182 */
183 if (target == GL_TEXTURE_2D_MULTISAMPLE) {
184 glUniform1i(z_loc, 0);
185 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
186 GL_COLOR_ATTACHMENT0,
187 target, tex, 0);
188 if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) !=
189 GL_FRAMEBUFFER_COMPLETE)
190 return;
191
192 piglit_draw_rect(0.0, 0.0, 2.0, 2.0);
193 } else {
194 for (z = 0; z < depth; ++z) {
195 glUniform1i(z_loc, z);
196 glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER,
197 GL_COLOR_ATTACHMENT0,
198 tex, 0, z);
199 if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) !=
200 GL_FRAMEBUFFER_COMPLETE)
201 return;
202
203 piglit_draw_rect(0.0, 0.0, 2.0, 2.0);
204 }
205 }
206
207 /* Restore values for the client */
208 if (!backup.arb_sample_shading)
209 glDisable(GL_SAMPLE_SHADING_ARB);
210 glMinSampleShadingARB(backup.min_sample_shading);
211
212 glUseProgram(backup.prog);
213 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, backup.draw_fbo);
214 glViewport(backup.viewport[0], backup.viewport[1],
215 backup.viewport[2], backup.viewport[3]);
216 glBindTexture(target, tex);
217 }
218
219 static bool
check_non_generated_texture(void)220 check_non_generated_texture(void)
221 {
222 bool pass = true;
223
224 /* Section 8.19 of the OpenGL 4.5 Core Profile spec says:
225 *
226 * "An INVALID_OPERATION error is generated by TextureStorage* if
227 * texture is not the name of an existing texture object."
228 */
229 glTextureStorage2DMultisample(250, 4, GL_RGBA8, 64, 64, GL_TRUE);
230 pass = piglit_check_gl_error(GL_INVALID_OPERATION) && pass;
231 glTextureStorage3DMultisample(250, 4, GL_RGBA8, 64, 64, 3, GL_TRUE);
232 pass = piglit_check_gl_error(GL_INVALID_OPERATION) && pass;
233
234 piglit_report_subtest_result(pass ? PIGLIT_PASS : PIGLIT_FAIL,
235 "non-generated texture name");
236 return pass;
237 }
238
239 static bool
check_unsized_format(void)240 check_unsized_format(void)
241 {
242 bool pass = true;
243
244 /* Section 8.19 of the OpenGL 4.5 Core Profile spec says:
245 *
246 * "An INVALID_ENUM error is generated if internalformat is one of
247 * the unsized base internal formats listed in table 8.11."
248 */
249 GLuint tex;
250 glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &tex);
251 glTextureStorage2DMultisample(tex, 4, GL_RGBA, 64, 64, GL_TRUE);
252
253 /* unsized formats may not be used with TexStorage* */
254 pass = piglit_check_gl_error(GL_INVALID_ENUM) && pass;
255 piglit_report_subtest_result(pass ? PIGLIT_PASS : PIGLIT_FAIL,
256 "unsized-format");
257 return pass;
258 }
259
260 static bool
check_immutable(void)261 check_immutable(void)
262 {
263 bool pass = true;
264 GLuint tex;
265 GLint param;
266 GLint samples;
267
268 glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &samples);
269
270 glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &tex);
271 /* specify storage for the texture, and mark it immutable-format */
272 glTextureStorage2DMultisample(tex, samples, GL_RGBA8, 64, 64, GL_TRUE);
273 pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
274
275 /* should now have TEXTURE_IMMUTABLE_FORMAT */
276 glGetTextureParameteriv(tex, GL_TEXTURE_IMMUTABLE_FORMAT, ¶m);
277
278 if (!piglit_check_gl_error(GL_NO_ERROR)) {
279 pass = false;
280 printf("failed to fetch texture parameter"
281 " TEXTURE_IMMUTABLE_FORMAT\n");
282 }
283
284 if (param != GL_TRUE) {
285 pass = false;
286 printf("expected TEXTURE_IMMUTABLE_FORMAT to be true,"
287 " got %d\n", param);
288 }
289
290 /* calling Tex*Storage* again on the same texture should fail */
291 glTextureStorage2DMultisample(tex, samples, GL_RGBA8, 32, 32, GL_TRUE);
292 if (!piglit_check_gl_error(GL_INVALID_OPERATION)) {
293 pass = false;
294 printf("expected respecifying an immutable-format texture"
295 " (with TexStorage*Multisample) to fail\n");
296 }
297
298 /* calling TexImage2DMultisample should fail too */
299 glBindTextureUnit(0, tex);
300 glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE,
301 samples, GL_RGBA8, 32, 32, GL_TRUE);
302
303 if (!piglit_check_gl_error(GL_INVALID_OPERATION)) {
304 pass = false;
305 printf("expected respecifying an immutable-format texture"
306 " (with TexImage*Multisample) to fail\n");
307 }
308
309 piglit_report_subtest_result(pass ? PIGLIT_PASS : PIGLIT_FAIL,
310 "immutable");
311 return pass;
312 }
313
314 static bool
draw_multisampled(void)315 draw_multisampled(void)
316 {
317 bool pass = true;
318 GLuint texture, fbo;
319 int x, y, z, idx;
320 int samples = 2;
321 float sample_mult;
322
323 /* Make a texture of size piglit_width x piglit_height that is divided
324 * into two triangles by a diagonal (\) line. (Use \ rather than /
325 * because texture_sub_image_multisample uses /.)
326 */
327 /* TODO: Do spatial anti-aliasing rather than blending. */
328 GLubyte* data = malloc(4 * samples * piglit_width * piglit_height *
329 sizeof(GLubyte));
330 float m = ((float) piglit_height / piglit_width);
331 for (z = 0; z < samples; ++z) {
332 for (y = 0; y < piglit_height; ++y) {
333 for (x = 0; x < piglit_width; ++x) {
334 idx = 4 * ((z * piglit_height + y) *
335 piglit_width + x);
336 sample_mult = ((float) z)/samples;
337 if (y <= ((int) piglit_height - (m * x))) {
338 /* Green below or on the line. */
339 data[idx + 0] = 0 * sample_mult;
340 data[idx + 1] = 255 * sample_mult;
341 data[idx + 2] = 0 * sample_mult;
342 }
343 else {
344 /* White above the line. */
345 data[idx + 0] = 255 * sample_mult;
346 data[idx + 1] = 255 * sample_mult;
347 data[idx + 2] = 255 * sample_mult;
348 }
349 data[idx + 3] = 255;
350 }
351 }
352 }
353
354 /* Set up the image. */
355 glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &texture);
356 glTextureStorage2DMultisample(texture, samples, GL_RGBA8,
357 piglit_width, piglit_height, GL_FALSE);
358 texture_sub_image_multisample(texture, GL_TEXTURE_2D_MULTISAMPLE,
359 GL_RGBA8, piglit_width, piglit_height,
360 1, samples, GL_RGBA, GL_UNSIGNED_BYTE,
361 data);
362
363 /* Draw the image. Can't use piglit_draw_rect_tex because the OpenGL
364 * 1.0 pipeline doesn't handle multisample textures.
365 */
366 glGenFramebuffers(1, &fbo);
367 glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
368 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
369 GL_TEXTURE_2D_MULTISAMPLE, texture, 0);
370 glBlitFramebuffer(0, 0, piglit_width, piglit_height,
371 0, 0, piglit_width, piglit_height,
372 GL_COLOR_BUFFER_BIT, GL_LINEAR);
373 pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
374
375 /* TODO: Add a Piglit probe call to check the output */
376 if (!piglit_automatic) {
377 piglit_present_results();
378 }
379
380 piglit_report_subtest_result(pass ? PIGLIT_PASS : PIGLIT_FAIL,
381 "multisampled drawing");
382
383
384 free(data);
385 return pass;
386 }
387
388 static bool
trivial_but_should_work(void)389 trivial_but_should_work(void)
390 {
391 bool pass = true;
392 GLuint texture;
393 GLint samples;
394
395 glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &samples);
396
397 /* 2D case */
398 glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &texture);
399 glTextureStorage2DMultisample(texture, samples, GL_RGBA8, 64, 64, GL_TRUE);
400 pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
401
402 /* 3D case */
403 glDeleteTextures(1, &texture);
404 glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 1, &texture);
405 glTextureStorage3DMultisample(texture, samples, GL_RGBA8, 64, 64, 3,
406 GL_TRUE);
407 pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
408
409 piglit_report_subtest_result(pass ? PIGLIT_PASS : PIGLIT_FAIL,
410 "trivial, but should work");
411 return pass;
412 }
413
414 static bool
check_improper_effective_target(void)415 check_improper_effective_target(void)
416 {
417 bool pass = true;
418 GLuint texture;
419
420 /* 3D case with 2D target */
421 glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &texture);
422 glTextureStorage3DMultisample(texture, 4, GL_RGBA8, 64, 64, 3,
423 GL_TRUE);
424 pass = piglit_check_gl_error(GL_INVALID_OPERATION) && pass;
425
426 /* 2D case with 3D target */
427 glDeleteTextures(1, &texture);
428 glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 1, &texture);
429 glTextureStorage2DMultisample(texture, 4, GL_RGBA8, 64, 64, GL_TRUE);
430 pass = piglit_check_gl_error(GL_INVALID_OPERATION) && pass;
431
432 /* 2D case with non-multisampled target */
433 glDeleteTextures(1, &texture);
434 glCreateTextures(GL_TEXTURE_2D, 1, &texture);
435 glTextureStorage2DMultisample(texture, 4, GL_RGBA8, 64, 64, GL_TRUE);
436 pass = piglit_check_gl_error(GL_INVALID_OPERATION) && pass;
437
438 piglit_report_subtest_result(pass ? PIGLIT_PASS : PIGLIT_FAIL,
439 "improper effective target");
440 return pass;
441 }
442
443 void
piglit_init(int argc,char ** argv)444 piglit_init(int argc, char **argv)
445 {
446 int max_samples;
447 piglit_require_extension("GL_ARB_direct_state_access");
448 piglit_require_extension("GL_ARB_texture_storage_multisample");
449 glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
450 printf("Max samples = %d\n", max_samples);
451 }
452
453 enum piglit_result
piglit_display(void)454 piglit_display(void)
455 {
456 bool pass = true;
457
458 if (!piglit_khr_no_error) {
459 pass = check_non_generated_texture() && pass;
460 pass = check_immutable() && pass;
461 pass = check_unsized_format() && pass;
462 pass = check_improper_effective_target() && pass;
463 }
464
465 pass = trivial_but_should_work() && pass;
466 pass = draw_multisampled() && pass;
467
468 return pass ? PIGLIT_PASS : PIGLIT_FAIL;
469 }
470