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, &param);
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