2 * Copyright © 2011 VMware, 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
21 * DEALINGS IN THE SOFTWARE.
27 * This should expose any errors in texel addressing within a texture image
28 * when calling glTexSubImage1D/2D/3D().
35 #include "piglit-util-gl.h"
36 #include "../fbo/fbo-formats.h"
39 #define STRINGIFY(x) STR(x)
41 PIGLIT_GL_TEST_CONFIG_BEGIN
43 config
.supports_gl_compat_version
= 10;
45 config
.window_visual
= PIGLIT_GL_VISUAL_RGBA
| PIGLIT_GL_VISUAL_DOUBLE
;
46 config
.khr_no_error_support
= PIGLIT_NO_ERRORS
;
48 config
.window_width
= 512;
49 config
.window_height
= 512;
51 PIGLIT_GL_TEST_CONFIG_END
54 * This is a subset of the formats in fbo-formats.h
55 * We don't test non-color, float, or int/uint textures at this time.
57 static const struct test_desc texsubimage_test_sets
[] = {
62 GL_UNSIGNED_NORMALIZED
,
65 tdfx_texture_compression_fxt1
,
66 ARRAY_SIZE(tdfx_texture_compression_fxt1
),
67 "GL_3DFX_texture_compression_FXT1",
68 GL_UNSIGNED_NORMALIZED
,
69 {"GL_ARB_texture_compression",
70 "GL_3DFX_texture_compression_FXT1"},
73 ext_texture_compression_s3tc
,
74 ARRAY_SIZE(ext_texture_compression_s3tc
),
75 "GL_EXT_texture_compression_s3tc",
76 GL_UNSIGNED_NORMALIZED
,
77 {"GL_ARB_texture_compression",
78 "GL_EXT_texture_compression_s3tc"},
81 ext_texture_compression_rgtc
,
82 ARRAY_SIZE(ext_texture_compression_rgtc
),
83 "GL_EXT_texture_compression_rgtc",
84 GL_UNSIGNED_NORMALIZED
,
85 {"GL_EXT_texture_compression_rgtc"}
88 ext_texture_compression_latc
,
89 ARRAY_SIZE(ext_texture_compression_latc
),
90 "GL_EXT_texture_compression_latc",
91 GL_UNSIGNED_NORMALIZED
,
92 {"GL_EXT_texture_compression_latc"}
94 arb_texture_compression_bptc_unorm
,
95 ARRAY_SIZE(arb_texture_compression_bptc_unorm
),
96 "GL_ARB_texture_compression_bptc-unorm",
97 GL_UNSIGNED_NORMALIZED
,
98 {"GL_ARB_texture_compression_bptc"}
100 arb_texture_compression_bptc_float
,
101 ARRAY_SIZE(arb_texture_compression_bptc_float
),
102 "GL_ARB_texture_compression_bptc-float",
104 {"GL_ARB_texture_compression_bptc"}
108 /* Default texture size. Other values might be used if the texture has
109 * less dimensions or other restrictions */
110 #define DEFAULT_TEX_WIDTH 128
111 #define DEFAULT_TEX_HEIGHT 64
112 #define DEFAULT_TEX_DEPTH 8
114 static const GLenum srcFormat
= GL_RGBA
;
116 /* List of texture targets to test, terminated by GL_NONE */
117 static const GLenum
*test_targets
;
119 /* If set to GL_TRUE then the texture sub image upload will be read
121 static GLboolean use_pbo
= GL_FALSE
;
124 int tx
, ty
, tz
, tw
, th
, td
;
127 /* If set, format and sub-region are given from command line. */
132 struct sub_region region
;
135 static struct single_test manual_dispatch
= {
136 .enabled
= false, .targets
= { GL_NONE
, GL_NONE
}
139 static const char fragment_1d_array
[] =
140 "#extension GL_EXT_texture_array : require\n"
141 "uniform sampler1DArray tex;\n"
142 "const float TEX_HEIGHT = " STRINGIFY(DEFAULT_TEX_HEIGHT
) ".0;\n"
146 " float layer = gl_TexCoord[0].t * TEX_HEIGHT - 0.5;\n"
147 " gl_FragColor = texture1DArray(tex, vec2(gl_TexCoord[0].s,\n"
151 static const char fragment_2d_array
[] =
152 "#extension GL_EXT_texture_array : require\n"
153 "uniform sampler2DArray tex;\n"
154 "const float TEX_DEPTH = " STRINGIFY(DEFAULT_TEX_DEPTH
) ".0;\n"
158 " float layer = gl_TexCoord[0].p * TEX_DEPTH - 0.5;\n"
159 " gl_FragColor = texture2DArray(tex, vec3(gl_TexCoord[0].st,\n"
163 static const char vertex_cube_map_array
[] =
164 "const float N_SIDES = 6.0;\n"
165 "const float TEX_DEPTH = " STRINGIFY(DEFAULT_TEX_DEPTH
) ".0 *\n"
170 " vec2 face_coord;\n"
172 " float slice = gl_MultiTexCoord0.p * TEX_DEPTH - 0.5;\n"
173 " float layer = floor(slice / N_SIDES);\n"
174 " int face = int(floor(mod(slice, N_SIDES)));\n"
176 " face_coord = gl_MultiTexCoord0.st * 2.0 - 1.0;\n"
178 " res = vec3(1.0, -face_coord.ts);\n"
179 " else if (face == 1)\n"
180 " res = vec3(-1.0, face_coord.ts * vec2(-1.0, 1.0));\n"
181 " else if (face == 2)\n"
182 " res = vec3(face_coord.s, 1.0, face_coord.t);\n"
183 " else if (face == 3)\n"
184 " res = vec3(face_coord.s, -1.0, -face_coord.t);\n"
185 " else if (face == 4)\n"
186 " res = vec3(face_coord.st * vec2(1.0, -1.0), 1.0);\n"
188 " res = vec3(-face_coord.st, -1.0);\n"
189 " gl_TexCoord[0] = vec4(res, layer);\n"
190 " gl_Position = ftransform();\n"
193 static const char fragment_cube_map_array
[] =
194 "#extension GL_ARB_texture_cube_map_array : require\n"
195 "uniform samplerCubeArray tex;\n"
199 " gl_FragColor = texture(tex, gl_TexCoord[0]);\n"
203 * XXX add this to piglit-util if useful elsewhere.
206 piglit_draw_rect_tex3d(float x
, float y
, float w
, float h
,
207 float tx
, float ty
, float tw
, float th
,
208 float tz0
, float tz1
)
242 glVertexPointer(4, GL_FLOAT
, 0, verts
);
243 glTexCoordPointer(3, GL_FLOAT
, 0, tex
);
244 glEnableClientState(GL_VERTEX_ARRAY
);
245 glEnableClientState(GL_TEXTURE_COORD_ARRAY
);
247 glDrawArrays(GL_QUADS
, 0, 4);
249 glDisableClientState(GL_VERTEX_ARRAY
);
250 glDisableClientState(GL_TEXTURE_COORD_ARRAY
);
254 equal_images(GLenum target
,
255 const GLubyte
*original_ref
,
256 const GLubyte
*updated_ref
,
257 const GLubyte
*testImg
,
258 GLuint w
, GLuint h
, GLuint d
,
259 GLuint tx
, GLuint ty
, GLuint tz
,
260 GLuint tw
, GLuint th
, GLuint td
)
268 case GL_TEXTURE_1D_ARRAY
:
274 return piglit_equal_images_update_rgba8(original_ref
, updated_ref
, testImg
,
276 tx
, ty
, tz
, tw
, th
, td
,
281 * Draw each image of the texture to the framebuffer and then save the
282 * entire thing to a buffer with glReadPixels().
285 draw_and_read_texture(GLuint w
, GLuint h
, GLuint d
, GLubyte
*ref
)
289 for (i
= 0; i
< d
; i
++) {
290 float tz
= (i
+ 0.5f
) / d
;
291 piglit_draw_rect_tex3d(i
/ 8 * w
, i
% 8 * h
, /* x/y */
293 0.0, 0.0, /* tx/ty */
294 1.0, 1.0, /* tw/th */
295 tz
, tz
/* tz0/tz1 */);
298 for (i
= 0; i
< d
; i
+= 8) {
299 glReadPixels(i
/ 8 * w
, i
% 8 * h
,
300 w
, h
* MIN2(8, d
- i
),
301 GL_RGBA
, GL_UNSIGNED_BYTE
,
303 ref
+= 8 * w
* h
* 4;
308 create_texture(GLenum target
,
310 GLsizei w
, GLsizei h
, GLsizei d
,
316 glPixelStorei(GL_UNPACK_ROW_LENGTH
, w
);
317 glPixelStorei(GL_UNPACK_IMAGE_HEIGHT
, h
);
319 glGenTextures(1, &tex
);
320 glBindTexture(target
, tex
);
321 glTexParameteri(target
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
322 glTexParameteri(target
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
323 glPixelStorei(GL_UNPACK_SKIP_PIXELS
, 0);
324 glPixelStorei(GL_UNPACK_SKIP_ROWS
, 0);
325 glPixelStorei(GL_UNPACK_SKIP_IMAGES
, 0);
327 glTexImage3D(target
, 0, intFormat
, w
, h
, d
, 0,
328 srcFormat
, GL_UNSIGNED_BYTE
, img
);
331 glTexImage2D(target
, 0, intFormat
, w
, h
, 0,
332 srcFormat
, GL_UNSIGNED_BYTE
, img
);
335 glTexImage1D(target
, 0, intFormat
, w
, 0,
336 srcFormat
, GL_UNSIGNED_BYTE
, img
);
338 assert(!"Unknown texture dimensions");
345 test_region(GLuint pbo
, GLenum target
, GLenum internal_format
,
346 const unsigned char *original_img
,
347 const unsigned char *original_ref
,
348 const unsigned char *updated_img
,
349 const unsigned char *updated_ref
,
350 unsigned w
, unsigned h
, unsigned d
,
351 const struct sub_region
*region
)
355 unsigned char *test_img
= (unsigned char *)malloc(w
* h
* d
* 4);
357 /* Recreate the original texture */
358 tex
= create_texture(target
, internal_format
, w
, h
, d
,
359 srcFormat
, original_img
);
362 glBindBuffer(GL_PIXEL_UNPACK_BUFFER
, pbo
);
364 /* replace texture region with data from updated image */
365 glPixelStorei(GL_UNPACK_SKIP_PIXELS
, region
->tx
);
366 glPixelStorei(GL_UNPACK_SKIP_ROWS
, region
->ty
);
367 glPixelStorei(GL_UNPACK_SKIP_IMAGES
, region
->tz
);
369 glTexSubImage3D(target
, 0,
370 region
->tx
, region
->ty
, region
->tz
,
371 region
->tw
, region
->th
, region
->td
,
372 srcFormat
, GL_UNSIGNED_BYTE
,
373 use_pbo
? NULL
: updated_img
);
375 glTexSubImage2D(target
, 0,
376 region
->tx
, region
->ty
,
377 region
->tw
, region
->th
,
378 srcFormat
, GL_UNSIGNED_BYTE
,
379 use_pbo
? NULL
: updated_img
);
381 glTexSubImage1D(target
, 0, region
->tx
, region
->tw
,
382 srcFormat
, GL_UNSIGNED_BYTE
,
383 use_pbo
? NULL
: updated_img
);
385 assert(!"Unknown image dimensions");
389 glBindBuffer(GL_PIXEL_UNPACK_BUFFER
, 0);
391 /* draw test image */
392 draw_and_read_texture(w
, h
, d
, test_img
);
394 glDeleteTextures(1, &tex
);
396 piglit_present_results();
398 if (!equal_images(target
,
399 original_ref
, updated_ref
, test_img
,
401 region
->tx
, region
->ty
, region
->tz
,
402 region
->tw
, region
->th
, region
->td
)) {
403 printf("texsubimage failed\n");
404 printf(" target: %s\n", piglit_get_gl_enum_name(target
));
405 printf(" internal format: %s\n",
406 get_format_name(internal_format
));
407 printf(" region: %d, %d %d x %d\n",
408 region
->tx
, region
->ty
, region
->tw
, region
->th
);
418 * Create two textures with different reference values. Draw both of
419 * the textures to the framebuffer and save the reference images with
423 * - Create another texture with the same initial values as the first
425 * - replace a random sub-region of the texture image with values from
427 * - draw the texture to the framebuffer and read back with glReadPixels
428 * - compare reference images to test image choosing either the first
429 * or second reference image for each pixel depending on whether it
430 * is within the updated region
431 * \param target GL_TEXTURE_1D/2D/3D
432 * \param intFormat the internal texture format
435 test_format(GLenum target
, GLenum intFormat
,
436 unsigned w
, unsigned h
, unsigned d
,
437 const struct sub_region
*regions
, unsigned num_regions
)
439 GLuint tex
, i
, j
, k
, n
, t
;
440 GLubyte
*original_img
, *original_ref
;
441 GLubyte
*updated_img
, *updated_ref
;
442 GLboolean pass
= GL_TRUE
;
445 original_img
= (GLubyte
*) malloc(w
* h
* d
* 4);
446 original_ref
= (GLubyte
*) malloc(w
* h
* d
* 4);
447 updated_img
= (GLubyte
*) malloc(w
* h
* d
* 4);
448 updated_ref
= (GLubyte
*) malloc(w
* h
* d
* 4);
450 /* fill source tex images */
452 for (i
= 0; i
< d
; i
++) {
453 for (j
= 0; j
< h
; j
++) {
454 for (k
= 0; k
< w
; k
++) {
455 original_img
[n
+ 0] = j
* 4;
456 original_img
[n
+ 1] = k
* 2;
457 original_img
[n
+ 2] = i
* 128 / d
;
458 original_img
[n
+ 3] = 255;
460 /* Swizzle the components in the
463 updated_img
[n
+ 0] = original_img
[n
+ 1];
464 updated_img
[n
+ 1] = original_img
[n
+ 2];
465 updated_img
[n
+ 2] = original_img
[n
+ 0];
466 updated_img
[n
+ 3] = original_img
[n
+ 3];
474 glGenBuffers(1, &pbo
);
475 glBindBuffer(GL_PIXEL_UNPACK_BUFFER
, pbo
);
476 glBufferData(GL_PIXEL_UNPACK_BUFFER
,
480 glBindBuffer(GL_PIXEL_UNPACK_BUFFER
, 0);
483 glTexEnvi(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_REPLACE
);
486 /* draw original reference image */
487 tex
= create_texture(target
, intFormat
, w
, h
, d
,
488 srcFormat
, original_img
);
489 glClear(GL_COLOR_BUFFER_BIT
);
490 draw_and_read_texture(w
, h
, d
, original_ref
);
491 glDeleteTextures(1, &tex
);
493 /* draw updated reference image */
494 tex
= create_texture(target
, intFormat
, w
, h
, d
,
495 srcFormat
, updated_img
);
496 glClear(GL_COLOR_BUFFER_BIT
);
497 draw_and_read_texture(w
, h
, d
, updated_ref
);
498 glDeleteTextures(1, &tex
);
500 for (t
= 0; t
< num_regions
; t
++) {
501 if (!test_region(pbo
, target
, intFormat
,
502 original_img
, original_ref
,
503 updated_img
, updated_ref
,
504 w
, h
, d
, ®ions
[t
])) {
515 glDeleteBuffers(1, &pbo
);
521 select_regions(unsigned w
, unsigned h
, unsigned d
, GLenum internal_format
,
522 struct sub_region
*regions
, unsigned num_regions
)
525 GLuint bw
, bh
, bb
, wMask
, hMask
, dMask
;
527 piglit_get_compressed_block_size(internal_format
, &bw
, &bh
, &bb
);
532 for (i
= 0; i
< num_regions
; i
++) {
533 /* Choose random region of texture to update.
534 * Use sizes and positions that are multiples of
535 * the compressed block size.
537 regions
[i
].tw
= (rand() % w
) & wMask
;
538 regions
[i
].th
= (rand() % h
) & hMask
;
539 regions
[i
].td
= (rand() % d
) & dMask
;
540 regions
[i
].tx
= (rand() % (w
- regions
[i
].tw
)) & wMask
;
541 regions
[i
].ty
= (rand() % (h
- regions
[i
].th
)) & hMask
;
542 regions
[i
].tz
= (rand() % (d
- regions
[i
].td
)) & dMask
;
544 assert(regions
[i
].tx
+ regions
[i
].tw
<= w
);
545 assert(regions
[i
].ty
+ regions
[i
].th
<= h
);
546 assert(regions
[i
].tz
+ regions
[i
].td
<= d
);
551 * Test all formats in texsubimage_test_sets[] for the given
555 test_formats(GLenum target
, unsigned w
, unsigned h
, unsigned d
)
557 GLboolean pass
= GL_TRUE
;
560 /* loop over the format groups */
561 for (i
= 0; i
< ARRAY_SIZE(texsubimage_test_sets
); i
++) {
562 const struct test_desc
*set
= &texsubimage_test_sets
[i
];
563 GLboolean skip
= GL_FALSE
;
565 /* only test compressed formats with 2D textures */
566 if (i
> 0 && target
!= GL_TEXTURE_2D
)
569 /* skip formats for unsupported extensions */
570 for (j
= 0; j
< ARRAY_SIZE(set
->ext
); j
++) {
572 !piglit_is_extension_supported(set
->ext
[j
])) {
573 /* req'd extension not supported */
581 /* loop over formats in the set */
582 for (j
= 0; j
< set
->num_formats
; j
++) {
583 struct sub_region regions
[10];
585 select_regions(w
, h
, d
, set
->format
[j
].internalformat
,
586 regions
, ARRAY_SIZE(regions
));
588 if (!test_format(target
,
589 set
->format
[j
].internalformat
,
591 regions
, ARRAY_SIZE(regions
))) {
601 prepare_tex_to_fbo_blit_program(GLenum target
)
606 case GL_TEXTURE_1D_ARRAY
:
607 program
= piglit_build_simple_program(NULL
, fragment_1d_array
);
609 case GL_TEXTURE_2D_ARRAY
:
610 program
= piglit_build_simple_program(NULL
, fragment_2d_array
);
612 case GL_TEXTURE_CUBE_MAP_ARRAY
:
613 program
= piglit_build_simple_program(vertex_cube_map_array
,
614 fragment_cube_map_array
);
624 glUseProgram(program
);
625 tex_location
= glGetUniformLocation(program
, "tex");
626 glUniform1i(tex_location
, 0);
633 print_usage_and_exit(const char *s
)
635 printf("Invalid argument: %s\n", s
);
636 printf("Usage: texsubimage <pbo> manual <target> <format> "
637 "<tx> <ty> <tz> <tw> <th> <tz>");
641 read_integer(const char *s
)
644 unsigned res
= strtol(s
, &endptr
, 0);
646 if (endptr
!= s
+ strlen(s
))
647 print_usage_and_exit(s
);
653 init_manual_dispatch(char **argv
, unsigned argc
)
655 static const unsigned manual_arg_num
= 8;
656 if (argc
< manual_arg_num
)
657 print_usage_and_exit(argv
[0]);
659 manual_dispatch
.targets
[0] = piglit_get_gl_enum_from_name(argv
[0]);
660 manual_dispatch
.format
= piglit_get_gl_enum_from_name(argv
[1]);
662 manual_dispatch
.region
.tx
= read_integer(argv
[2]);
663 manual_dispatch
.region
.ty
= read_integer(argv
[3]);
664 manual_dispatch
.region
.tz
= read_integer(argv
[4]);
665 manual_dispatch
.region
.tw
= read_integer(argv
[5]);
666 manual_dispatch
.region
.th
= read_integer(argv
[6]);
667 manual_dispatch
.region
.td
= read_integer(argv
[7]);
669 manual_dispatch
.enabled
= true;
673 adjust_tex_dimensions(GLenum target
, unsigned *w
, unsigned *h
, unsigned *d
)
675 if (target
== GL_TEXTURE_CUBE_MAP_ARRAY_ARB
) {
678 } else if (target
!= GL_TEXTURE_3D
&&
679 target
!= GL_TEXTURE_2D_ARRAY
) {
683 if (target
== GL_TEXTURE_1D
)
690 GLboolean pass
= GL_TRUE
;
693 if (manual_dispatch
.enabled
)
694 test_targets
= manual_dispatch
.targets
;
696 /* Loop over 1/2/3D texture targets */
697 for (i
= 0; test_targets
[i
] != GL_NONE
; i
++) {
698 unsigned w
= DEFAULT_TEX_WIDTH
;
699 unsigned h
= DEFAULT_TEX_HEIGHT
;
700 unsigned d
= DEFAULT_TEX_DEPTH
;
701 const GLuint program
=
702 prepare_tex_to_fbo_blit_program(test_targets
[i
]);
704 adjust_tex_dimensions(test_targets
[i
], &w
, &h
, &d
);
706 if (manual_dispatch
.enabled
) {
707 pass
= test_format(test_targets
[i
],
708 manual_dispatch
.format
,
710 &manual_dispatch
.region
, 1);
712 pass
= test_formats(test_targets
[i
], w
, h
, d
) && pass
;
716 glDisable(test_targets
[i
]);
719 glDeleteProgram(program
);
723 return pass
? PIGLIT_PASS
: PIGLIT_FAIL
;
728 piglit_init(int argc
, char **argv
)
730 static const GLenum core_targets
[] = {
736 static const GLenum array_targets
[] = {
737 GL_TEXTURE_1D_ARRAY_EXT
,
738 GL_TEXTURE_2D_ARRAY_EXT
,
741 static const GLenum cube_map_array_targets
[] = {
742 GL_TEXTURE_CUBE_MAP_ARRAY_ARB
,
745 int remaining_argc
= 1;
748 test_targets
= core_targets
;
750 for (i
= 1; i
< argc
; i
++) {
751 if (!strcmp(argv
[i
], "array")) {
752 piglit_require_extension("GL_EXT_texture_array");
753 piglit_require_fragment_shader();
754 test_targets
= array_targets
;
755 } else if (!strcmp(argv
[i
], "cube_map_array")) {
756 piglit_require_extension
757 ("GL_ARB_texture_cube_map_array");
758 piglit_require_vertex_shader();
759 piglit_require_fragment_shader();
760 test_targets
= cube_map_array_targets
;
761 } else if (!strcmp(argv
[i
], "pbo")) {
762 piglit_require_extension("GL_ARB_pixel_buffer_object");
764 } else if (!strcmp(argv
[i
], "manual")) {
765 init_manual_dispatch(argv
+ i
+ 1, argc
- (i
+ 1));
768 argv
[remaining_argc
++] = argv
[i
];
772 if (!manual_dispatch
.enabled
)
773 fbo_formats_init(remaining_argc
, argv
, 0);
774 (void) fbo_formats_display
;
776 piglit_ortho_projection(piglit_width
, piglit_height
, GL_FALSE
);