1 /*
2  * Copyright © 2012 Marek Olšák <maraeo@gmail.com>
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 DEALINGS
22  * IN THE SOFTWARE.
23  */
24 
25 /**
26  * @file getcompressedtextureimage.c
27  */
28 #include "piglit-util-gl.h"
29 #include "dsa-utils.h"
30 
31 PIGLIT_GL_TEST_CONFIG_BEGIN
32 
33 	config.window_width = 216;
34 	config.supports_gl_compat_version = 20;
35 	config.supports_gl_core_version = 31;
36 
37 	config.window_visual = PIGLIT_GL_VISUAL_RGBA |
38 			       PIGLIT_GL_VISUAL_DOUBLE;
39 	config.khr_no_error_support = PIGLIT_NO_ERRORS;
40 
41 PIGLIT_GL_TEST_CONFIG_END
42 
43 #define IMAGE_WIDTH 32
44 #define IMAGE_HEIGHT 32
45 #define IMAGE_SIZE (IMAGE_WIDTH * IMAGE_HEIGHT * 4)
46 #define DISPLAY_GAP 4
47 
48 static void
show_image(GLubyte * data,int num_layers,const char * title)49 show_image(GLubyte *data, int num_layers, const char *title)
50 {
51 	GLuint name;
52 	int i;
53 	char junk[50];
54 	GLuint prog = 0;
55 
56 	if (!piglit_automatic) {
57 		prog = dsa_create_program(GL_TEXTURE_2D);
58 		glUseProgram(prog);
59 		dsa_set_xform(prog, piglit_width, piglit_height);
60 
61 		/* Create the texture handle. */
62 		glCreateTextures(GL_TEXTURE_2D, 1, &name);
63 		glTextureStorage2D(name, 1, GL_RGBA8, IMAGE_WIDTH,
64 			IMAGE_HEIGHT);
65 		glTextureParameteri(name, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
66 		glTextureParameteri(name, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
67 		glBindTextureUnit(0, name);
68 
69 		/* Draw the layers, separated by some space */
70 		glClear(GL_COLOR_BUFFER_BIT);
71 		for (i = 0; i < num_layers; ++i) {
72 			int x = (IMAGE_WIDTH + DISPLAY_GAP) * (i % 6);
73 			int y = (IMAGE_HEIGHT + DISPLAY_GAP) * (i / 6);
74 			glTextureSubImage2D(name, 0, 0, 0,
75 					    IMAGE_WIDTH, IMAGE_HEIGHT,
76 					    GL_RGBA, GL_UNSIGNED_BYTE,
77 					    data + i * IMAGE_SIZE);
78 			piglit_draw_rect_tex(x, y, IMAGE_WIDTH, IMAGE_HEIGHT,
79 					     0, 0, 1, 1);
80 		}
81 
82 		/* Make the title. */
83 		printf("****** %s ******\n", title);
84 
85 		piglit_present_results();
86 
87 		/* Pause. */
88 		printf("Enter any char to continue.\n>>>>>>");
89 		(void) scanf("%s", junk);
90 		printf("\n");
91 
92 		glDeleteTextures(1, &name);
93 		glUseProgram(0);
94 		glDeleteProgram(prog);
95 	}
96 }
97 
98 static GLubyte *
make_layer_data(int num_layers)99 make_layer_data(int num_layers)
100 {
101 	int z;
102 	GLubyte *layer_data =
103 		malloc(num_layers * IMAGE_SIZE * sizeof(GLubyte));
104 	GLubyte *data = piglit_rgbw_image_ubyte(IMAGE_WIDTH,
105 						IMAGE_HEIGHT, true);
106 
107 	for (z = 0; z < num_layers; z++) {
108 		memcpy(layer_data + IMAGE_SIZE * z, data, IMAGE_SIZE);
109 	}
110 
111 	free(data);
112 
113 	/* Show the first layer of the completed layer data. */
114 	show_image(layer_data, num_layers, "Test Data");
115 
116 	return layer_data;
117 }
118 
119 static bool
compare_layer(int layer,int num_elements,int tolerance,GLubyte * data,GLubyte * expected)120 compare_layer(int layer, int num_elements, int tolerance,
121 			  GLubyte *data, GLubyte *expected)
122 {
123 	int i;
124 
125 	for (i = 0; i < num_elements; ++i) {
126 		if (abs((int)data[i] - (int)expected[i]) > tolerance) {
127 			printf("GetCompressedTextureImage() returns incorrect"
128 			       " data in byte %i for layer %i\n",
129 			       i, layer);
130 			printf("    corresponding to (%i,%i), channel %i\n",
131 			       (i / 4) / IMAGE_WIDTH, (i / 4) % IMAGE_HEIGHT,
132 				i % 4);
133 			printf("    expected: %i\n", expected[i]);
134 			printf("    got: %i\n", data[i]);
135 			return false;
136 		}
137 	}
138 	return true;
139 }
140 
141 static enum piglit_result
getTexImage(bool doPBO,GLenum target,GLubyte * data,GLenum internalformat,int tolerance)142 getTexImage(bool doPBO, GLenum target, GLubyte *data,
143 	    GLenum internalformat, int tolerance)
144 {
145 	int i;
146 	int num_layers=1, num_faces=1, layer_size;
147 	GLubyte *data2 = NULL;
148 	GLubyte *dataGet;
149 	GLuint packPBO;
150 	bool pass = true;
151 	GLuint name;
152 	GLint compressed;
153 	GLint comp_size;
154 
155 	/* Upload the data.  These are all targets that can be compressed
156 	 * according to _mesa_target_can_be_compressed.
157 	 */
158 	switch (target) {
159 	case GL_TEXTURE_2D:
160 		glCreateTextures(target, 1, &name);
161 		glTextureStorage2D(name, 1, internalformat, IMAGE_WIDTH,
162 				   IMAGE_HEIGHT);
163 		glTextureSubImage2D(name, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT,
164 				    GL_RGBA, GL_UNSIGNED_BYTE, data);
165 		layer_size = IMAGE_SIZE;
166 		break;
167 
168 	case GL_TEXTURE_CUBE_MAP:
169 		num_faces = 6;
170 		glCreateTextures(target, 1, &name);
171 		if (!piglit_khr_no_error) {
172 			/* This is invalid. You must use 2D storage call for
173 			 * cube.
174 			 */
175 			glTextureStorage3D(name, 1, internalformat,
176 					   IMAGE_WIDTH, IMAGE_HEIGHT,
177 					   num_faces);
178 			pass &= piglit_check_gl_error(GL_INVALID_OPERATION);
179 		}
180 		glTextureStorage2D(name, 1, internalformat,
181 				   IMAGE_WIDTH, IMAGE_HEIGHT);
182 		/* This is legal. */
183 		glTextureSubImage3D(name, 0, 0, 0, 0, IMAGE_WIDTH,
184 				    IMAGE_HEIGHT, num_faces, GL_RGBA,
185 				    GL_UNSIGNED_BYTE, data);
186 		layer_size = IMAGE_SIZE;
187 		break;
188 
189 	case GL_TEXTURE_2D_ARRAY:
190 		num_layers = 7; /* Fall through. */
191 	case GL_TEXTURE_CUBE_MAP_ARRAY:
192 		num_layers = 6 * 3;
193 		glCreateTextures(target, 1, &name);
194 		glTextureStorage3D(name, 1, internalformat, IMAGE_WIDTH,
195 				   IMAGE_HEIGHT, num_layers);
196 		glTextureSubImage3D(name, 0, 0, 0, 0,
197 				    IMAGE_WIDTH, IMAGE_HEIGHT, num_layers,
198 				    GL_RGBA, GL_UNSIGNED_BYTE, data);
199 		layer_size = IMAGE_SIZE;
200 		break;
201 
202 	default:
203 		puts("Invalid texture target.");
204 		return PIGLIT_FAIL;
205 	}
206 
207 	/* Make sure the driver has compressed the image. */
208 	glGetTextureLevelParameteriv(name, 0, GL_TEXTURE_COMPRESSED,
209 		&compressed);
210 	printf("\tIs the texture compressed? %s.\n",
211 		compressed ? "yes" : "no");
212 
213 	glGetTextureLevelParameteriv(name, 0,
214 				     GL_TEXTURE_COMPRESSED_IMAGE_SIZE,
215 				     &comp_size);
216 	/* Section 8.11 (Texture Queries) of the OpenGL 4.5 Core Profile spec
217 	 * says:
218 	 *
219 	 *     "For GetTextureLevelParameter* only, texture may also be a cube
220 	 *     map texture object.  In this case the query is always performed
221 	 *     for face zero (the TEXTURE_CUBE_MAP_POSITIVE_X face), since
222 	 *     there is no way to specify another face."
223 	 */
224 	if (target == GL_TEXTURE_CUBE_MAP)
225 		comp_size *= num_faces;
226 	printf("\tThe size of the texture in bytes is %d.\n", comp_size);
227 
228 	/* Show the uncompressed data. */
229 	show_image(data, num_layers * num_faces, "Data Before Compression");
230 
231 
232 	/* Setup the PBO or data array to read into from
233 	 * glGetCompressedTextureImage
234 	 */
235 	if (doPBO) {
236 		glGenBuffers(1, &packPBO);
237 		glBindBuffer(GL_PIXEL_PACK_BUFFER, packPBO);
238 		/* Make the buffer big enough to hold uncompressed data. */
239 		glBufferData(GL_PIXEL_PACK_BUFFER, layer_size * num_faces *
240 			     num_layers * sizeof(GLubyte),
241 			     NULL, GL_STREAM_READ);
242 	} else {
243 		glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
244 		data2 = malloc(layer_size * num_faces * num_layers *
245 			       sizeof(GLubyte));
246 		memset(data2, 123, layer_size * num_faces * num_layers *
247 			       sizeof(GLubyte));
248 	}
249 	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
250 	assert(num_layers * num_faces * layer_size <= 18 * IMAGE_SIZE);
251 
252 
253 	/* Download the compressed texture image. */
254 	if (doPBO)
255 		glGetCompressedTextureImage(name, 0, comp_size, NULL);
256 	else
257 		glGetCompressedTextureImage(name, 0, comp_size, data2);
258 	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
259 
260 	if (doPBO)
261 		dataGet = (GLubyte *) glMapBufferRange(
262 					       GL_PIXEL_PACK_BUFFER, 0,
263 					       comp_size,
264 					       GL_MAP_READ_BIT);
265 	else
266 		dataGet = data2;
267 
268 	/* Re-upload the texture in compressed form. */
269 	switch (target) {
270 	case GL_TEXTURE_2D:
271 		glCompressedTextureSubImage2D(name, 0, 0, 0,
272 					      IMAGE_WIDTH, IMAGE_HEIGHT,
273 					      internalformat, comp_size,
274 					      dataGet);
275 		break;
276 
277 	case GL_TEXTURE_CUBE_MAP:
278 		glCompressedTextureSubImage3D(name, 0, 0, 0, 0,
279 					      IMAGE_WIDTH, IMAGE_HEIGHT,
280 					      num_faces,
281 					      internalformat, comp_size,
282 					      dataGet);
283 		break;
284 
285 	case GL_TEXTURE_2D_ARRAY:
286 	case GL_TEXTURE_CUBE_MAP_ARRAY:
287 		glCompressedTextureSubImage3D(name, 0, 0, 0, 0,
288 					      IMAGE_WIDTH, IMAGE_HEIGHT,
289 					      num_layers,
290 					      internalformat, comp_size,
291 					      dataGet);
292 		break;
293 	}
294 
295 
296 	/* Get the uncompressed version for comparison. */
297 	if (doPBO) {
298 		glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
299 		glGetTextureImage(name, 0, GL_RGBA, GL_UNSIGNED_BYTE,
300 			layer_size * num_layers * num_faces * sizeof(GLubyte),
301 			NULL);
302 	}
303 	else {
304 		glGetTextureImage(name, 0, GL_RGBA, GL_UNSIGNED_BYTE,
305 			layer_size * num_layers * num_faces * sizeof(GLubyte),
306 			data2);
307 	}
308 	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
309 	if (doPBO)
310 		dataGet = (GLubyte *) glMapBufferRange(
311 					       GL_PIXEL_PACK_BUFFER, 0,
312 					       layer_size * num_layers *
313 					       num_faces * sizeof(GLubyte),
314 					       GL_MAP_READ_BIT);
315 	else
316 		dataGet = data2;
317 
318 	/* Examine the image after pulling it off the graphics card. */
319 	show_image(dataGet, num_layers * num_faces, "Data After Compression");
320 
321 	/* Do the comparison */
322 	for (i = 0; i < num_faces * num_layers; i++) {
323 		pass = compare_layer(i, layer_size, tolerance, dataGet,
324 				     data + (i * layer_size)) && pass;
325 		dataGet += layer_size;
326 	}
327 
328 	if (doPBO) {
329 		glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
330 		glDeleteBuffers(1, &packPBO);
331 	}
332 
333 	glDeleteTextures(1, &name);
334 	free(data2);
335 
336 	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
337 }
338 
339 struct target_and_mask {
340 	GLenum target;
341 	bool mask;
342 };
343 
344 static struct target_and_mask targets[] = {
345 	{GL_TEXTURE_2D, 1},
346 	{GL_TEXTURE_CUBE_MAP, 1},
347 	{GL_TEXTURE_2D_ARRAY, 1},
348 	{GL_TEXTURE_CUBE_MAP_ARRAY, 1},
349 };
350 
351 static void
clear_target_mask(GLenum target)352 clear_target_mask(GLenum target)
353 {
354 	int i;
355 	for (i = 0; i < ARRAY_SIZE(targets); ++i) {
356 		if (targets[i].target == target) {
357 			targets[i].mask = 0;
358 		}
359 	}
360 }
361 
362 void
piglit_init(int argc,char ** argv)363 piglit_init(int argc, char **argv)
364 {
365 	piglit_require_extension("GL_ARB_direct_state_access");
366 	piglit_require_extension("GL_ARB_texture_storage");
367 
368 	if (!piglit_is_extension_supported("GL_EXT_texture_array"))
369 		clear_target_mask(GL_TEXTURE_2D_ARRAY);
370 	if (!piglit_is_extension_supported("GL_ARB_texture_cube_map_array"))
371 		clear_target_mask(GL_TEXTURE_CUBE_MAP_ARRAY);
372 
373 	glClearColor(0.5, 0.5, 0.5, 1);
374 }
375 
376 enum piglit_result
piglit_display(void)377 piglit_display(void)
378 {
379 	int i;
380 	GLenum internalformat = GL_COMPRESSED_RGBA_FXT1_3DFX;
381 	int tolerance = 8;
382 	GLubyte *data;
383 	enum piglit_result subtest;
384 	enum piglit_result result = PIGLIT_PASS;
385 
386 	piglit_require_extension("GL_3DFX_texture_compression_FXT1");
387 
388 	data = make_layer_data(18);
389 
390 	for (i = 0; i < ARRAY_SIZE(targets); ++i) {
391 		if (!targets[i].mask)
392 			continue;
393 
394 		printf("Testing %s into PBO\n",
395 			piglit_get_gl_enum_name(targets[i].target));
396 		subtest = getTexImage(true, targets[i].target, data,
397 				      internalformat, tolerance);
398 		piglit_report_subtest_result(subtest, "getTexImage %s PBO",
399 					     piglit_get_gl_enum_name(
400 						targets[i].target));
401 		if (subtest == PIGLIT_FAIL)
402 			result = PIGLIT_FAIL;
403 
404 		printf("\n"); /* Separate tests with some white space. */
405 
406 		printf("Testing %s into client array\n",
407 			piglit_get_gl_enum_name(targets[i].target));
408 		subtest = getTexImage(false, targets[i].target, data,
409 				      internalformat, tolerance);
410 		piglit_report_subtest_result(subtest, "getTexImage %s",
411 					     piglit_get_gl_enum_name(
412 						targets[i].target));
413 		if (subtest == PIGLIT_FAIL)
414 			result = PIGLIT_FAIL;
415 
416 		printf("\n\n"); /* Separate targets with some white space. */
417 
418 		if (!piglit_check_gl_error(GL_NO_ERROR))
419 			result = PIGLIT_FAIL;
420 	}
421 
422 	/* 1D targets can't be compressed in Mesa right now, but here is a
423 	 * trivial test for the entry point.
424 	 */
425 	glCompressedTextureSubImage1D(250, 0, 0, 60,
426 				      internalformat, 60*4*8,
427 				      NULL);
428 
429 	if (!piglit_khr_no_error) {
430 		 /* Bad texture */
431 		if (!piglit_check_gl_error(GL_INVALID_OPERATION))
432 			subtest = PIGLIT_FAIL;
433 		else
434 			subtest = PIGLIT_PASS;
435 		piglit_report_subtest_result(subtest, "Compressed Texture"
436 					     " Sub Image 1D");
437 		if (subtest == PIGLIT_FAIL)
438 			result = PIGLIT_FAIL;
439 	}
440 
441 	free(data);
442 
443 	return result;
444 }
445