2 * Copyright © 2011, 2016 Intel Corporation
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.
25 * \file piglit-vbo.cpp
27 * This file adds the facility for specifying vertex data to piglit
28 * tests using a columnar text format, for example:
31 * vertex/double/vec3 foo/uint/uint bar[0]/int/int bar[1]/int/int
32 * 0.0 0.0 0.0 10 0 0 # comment
37 * The format consists of a row of column headers followed by any
38 * number of rows of data. Each column header has the form
39 * "ATTRNAME[ARRAY_INDEX]/GL_TYPE/GLSL_TYPE/MATRIX_COLUMN", where
40 * ATTRNAME is the name of the vertex attribute to be bound to this
41 * column, ARRAY_INDEX is the index, GL_TYPE is the GL type of data
42 * that follows ("half", "float", "double", "byte", "ubyte", "short",
43 * "ushort", "int" or "uint"), GLSL_TYPE is the GLSL type of the data
44 * ("int", "uint", "float", "double", "ivec"*, "uvec"*, "vec"*,
45 * "dvec"*, "mat"*, "dmat"*) and MATRIX_COLUMN is the column number of
46 * the data in case of being a matrix. [ARRAY_INDEX] is optional and
47 * needs to be specified only in case of array
48 * attributes. MATRIX_COLUMN doesn't need to be specified if it is not
49 * a matrix column as in the example before. So another example, if
50 * you want to specify the data of a mat2x3:
53 * foomatrix/float/mat2x3/0 foomatrix/float/mat2x3/1
54 * 0.0 1.0 2.0 3.0 4.0 5.0
55 * 6.0 7.0 8.0 9.0 10.0 11.0
58 * The same example, using the deprecated but compatible syntax would
62 * foomatrix/float/3/0 foomatrix/float/3/1
63 * 0.0 1.0 2.0 3.0 4.0 5.0
64 * 6.0 7.0 8.0 9.0 10.0 11.0
67 * The data follows the column headers in space-separated form. "#"
68 * can be used for comments, as in shell scripts.
70 * To process textual vertex data, call the function
71 * setup_vbo_from_text(), passing the int identifying the linked
72 * program, and the string containing the vertex data. The return
73 * value is the number of rows of vertex data found.
75 * If an error occurs, setup_vbo_from_text() will print out a
76 * description of the error and exit with PIGLIT_FAIL.
78 * For the first example above, the call to setup_vbo_from_text() is
79 * roughly equivalent to the following GL operations:
82 * struct vertex_attributes {
87 * { { 0.0, 0.0, 0.0 }, 10, { 0, 0 } },
88 * { { 0.0, 1.0, 0.0 }, 5, { 1, 1 } },
89 * { { 1.0, 1.0, 0.0 }, 0, { 0, 1 } }
91 * size_t stride = sizeof(vertex_data[0]);
92 * GLint vertex_index = glGetAttribLocation(prog, "vertex");
93 * GLint foo_index = glGetAttribLocation(prog, "foo");
94 * GLint bar_index = glGetAttribLocation(prog, "bar");
95 * GLuint buffer_handle;
96 * glGenBuffers(1, &buffer_handle);
97 * glBindBuffer(GL_ARRAY_BUFFER, buffer_handle);
98 * glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), &vertex_data,
100 * glVertexAttribPointer(vertex_index, 3, GL_FLOAT, GL_FALSE, stride,
101 * (void *) offsetof(vertex_attributes, vertex));
102 * glVertexAttribIPointer(foo_index, 3, GL_UNSIGNED_INT, stride,
103 * (void *) offsetof(vertex_attributes, foo));
104 * glVertexAttribIPointer(bar_index, 3, GL_INT, stride,
105 * (void *) offsetof(vertex_attributes, bar));
106 * glEnableVertexAttribArray(vertex_index);
107 * glEnableVertexAttribArray(foo_index);
108 * glEnableVertexAttribArray(bar_index);
111 * Which could correspond to a vertex shader of this type:
120 * if (bar[0] + bar[1] == foo) {
121 * gl_Position = vec4(vertex, 1.0);
123 * gl_Position = vec4(1.0, vertex);
136 #include "piglit-util.h"
137 #include "piglit-util-gl.h"
138 #include "piglit-vbo.h"
141 * Convert a type name string to a GLenum.
144 decode_type(const char *type
,
145 GLenum
*gl_type
, size_t *gl_type_size
, GLenum
*glsl_type
)
149 assert(gl_type_size
);
151 static struct type_table_entry
{
152 const char *type
; /* NULL means end of table */
156 } const type_table
[] = {
157 { "byte", GL_BYTE
, 1, GL_INT
},
158 { "ubyte", GL_UNSIGNED_BYTE
, 1, GL_UNSIGNED_INT
},
159 { "short", GL_SHORT
, 2, GL_INT
},
160 { "ushort", GL_UNSIGNED_SHORT
, 2, GL_UNSIGNED_INT
},
161 { "int", GL_INT
, 4, GL_INT
},
162 { "uint", GL_UNSIGNED_INT
, 4, GL_UNSIGNED_INT
},
163 { "half", GL_HALF_FLOAT
, 2, GL_FLOAT
},
164 { "float", GL_FLOAT
, 4, GL_FLOAT
},
165 { "double", GL_DOUBLE
, 8, GL_DOUBLE
},
170 for (int i
= 0; type_table
[i
].type
; ++i
) {
171 if (0 == strcmp(type
, type_table
[i
].type
)) {
172 *gl_type
= type_table
[i
].gl_type
;
173 *gl_type_size
= type_table
[i
].gl_type_size
;
175 *glsl_type
= type_table
[i
].glsl_type
;
185 * Convert a GLSL type name string to its basic GLenum type.
188 decode_glsl_type(const char *type
,
189 GLenum
*glsl_type
, size_t *rows
, size_t *cols
, char **endptr
)
196 if (isdigit(type
[0])) {
197 *rows
= strtoul(type
, endptr
, 10);
203 static struct type_table_entry
{
204 const char *type
; /* NULL means end of table */
206 } const type_table
[] = {
208 { "uint", GL_UNSIGNED_INT
},
209 { "float", GL_FLOAT
},
210 { "double", GL_DOUBLE
},
212 { "uvec", GL_UNSIGNED_INT
},
214 { "dvec", GL_DOUBLE
},
216 { "dmat", GL_DOUBLE
},
221 for (int i
= 0; type_table
[i
].type
; ++i
) {
222 const size_t type_len
= strlen(type_table
[i
].type
);
223 if (0 == strncmp(type
, type_table
[i
].type
, type_len
)) {
224 *endptr
= const_cast<char *>(&type
[type_len
]);
226 /* In case of vectors or matrices, let's
227 * calculate rows and columns.
230 if (!isdigit(**endptr
))
232 *rows
= **endptr
- '0';
235 /* In case of matrices, let's
236 * calculate the rows.
240 if (**endptr
== 'x') {
241 if (!isdigit(*(++*endptr
)))
243 *rows
= **endptr
- '0';
253 *glsl_type
= type_table
[i
].glsl_type
;
260 *endptr
= const_cast<char *>(type
);
266 * Description of a vertex attribute, built from its column header
268 class vertex_attrib_description
271 vertex_attrib_description(GLuint prog
, const char *text
);
272 bool parse_datum(const char **text
, void *data
) const;
273 void setup(size_t *offset
, size_t stride
) const;
276 * GL data type of this attribute.
281 * Size of the GL data type of this attribute.
283 size_t data_type_size
;
286 * GLSL data type of this attribute.
288 GLenum glsl_data_type
;
291 * Index of the array for this attribute.
296 * Number of columns of the GLSL data type of this attribute.
301 * Number of rows of the GLSL data type of this attribute.
306 * Index of the matrix column for this attribute.
311 * Index of this vertex attribute in the linked program.
317 get_attrib_location(GLuint prog
,
318 const std::string
&name
,
324 unsigned long ul
= strtoul(name
.c_str(), &end
, 10);
326 if (errno
== 0 && *end
== '\0' && ul
<= UINT_MAX
) {
331 GLint attrib_location
= glGetAttribLocation(prog
, name
.c_str());
332 if (attrib_location
== -1)
335 *index
= attrib_location
;
341 * Build a vertex_attrib_description from a column header, by looking
342 * up the vertex attribute in the linked program and interpreting the
343 * type, dimensions and mattrix_column parts of the header.
345 * If there is a parse failure, print a description of the problem and
346 * then exit with PIGLIT_FAIL.
348 vertex_attrib_description::vertex_attrib_description(GLuint prog
,
351 /* Split the column header into
352 * name[array_index]/type/dimensions/matrix_column fields.
354 const char *first_slash
= strchr(text
, '/');
355 if (first_slash
== NULL
) {
356 printf("Column headers must be in the form"
357 " name[array_index]/type/dimensions/matrix_column.\n"
358 "Note: [array_index] and matrix_column are optional.\n"
361 piglit_report_result(PIGLIT_FAIL
);
364 std::string
name(text
, first_slash
);
366 /* If the attrib is an array, strip the index */
367 if (name
[name
.size() - 1] == ']') {
368 std::string::size_type n
= name
.find('[');
369 if (n
== std::string::npos
) {
370 printf("Column header looked like an array"
371 " but couldn't parse it.\n"
374 piglit_report_result(PIGLIT_FAIL
);
376 this->array_index
= strtoul(name
.substr(n
+ 1).c_str(), NULL
, 0);
380 this->array_index
= 0;
383 const char *second_slash
= strchr(first_slash
+ 1, '/');
384 if (second_slash
== NULL
) {
385 printf("Column headers must be in the form"
386 " name[array_index]/type/dimensions/matrix_column.\n"
387 "Note: [array_index] and matrix_column are optional.\n"
390 piglit_report_result(PIGLIT_FAIL
);
394 if (!decode_glsl_type(second_slash
+ 1,
395 &this->glsl_data_type
,
396 &this->rows
, &this->cols
,
398 printf("Unrecognized GLSL type: %s\n", second_slash
+ 1);
399 piglit_report_result(PIGLIT_FAIL
);
402 std::string
type_str(first_slash
+ 1, second_slash
);
403 GLenum
*glsl_data_type
= this->glsl_data_type
? 0
404 : &this->glsl_data_type
;
406 if (!decode_type(type_str
.c_str(),
407 &this->data_type
, &this->data_type_size
,
409 printf("Unrecognized GL type: %s\n", type_str
.c_str());
410 piglit_report_result(PIGLIT_FAIL
);
413 if (*endptr
!= '\0') {
414 const char *third_slash
= strchr(second_slash
+ 1, '/');
415 this->matrix_index
= strtoul(third_slash
+ 1, &endptr
, 10);
417 if (this->matrix_index
> 3) {
418 printf("Matrix column index must be between 0 and 3. Got: %lu\n",
419 (unsigned long) this->matrix_index
);
420 piglit_report_result(PIGLIT_FAIL
);
423 if (*endptr
!= '\0') {
424 printf("Column headers must be in the form"
425 " name[array_index]/type/dimensions/matrix_column.\n"
426 "Note: [array_index] and matrix_column are optional.\n"
429 piglit_report_result(PIGLIT_FAIL
);
432 this->matrix_index
= 0;
435 if (!get_attrib_location(prog
, name
, &this->index
)) {
436 printf("Unexpected vbo column name. Got: %s\n", name
.c_str());
437 piglit_report_result(PIGLIT_FAIL
);
439 /* If the type is integral, verify that integer vertex
440 * attribute support is present. Note: we treat it as a FAIL
441 * if support is not present, because it's up to the test to
442 * either (a) not require integer vertex attribute support, or
443 * (b) skip itself if integer vertex attribute support is not
446 if (this->glsl_data_type
!= GL_FLOAT
&&
448 (piglit_get_gl_version() < 30 &&
449 !piglit_is_extension_supported("GL_EXT_gpu_shader4")))) {
450 printf("Test uses glVertexAttribIPointer(),"
451 " which is unsupported.\n");
452 piglit_report_result(PIGLIT_FAIL
);
455 if (this->rows
< 1 || this->rows
> 4) {
456 printf("Rows must be between 1 and 4. Got: %lu\n",
457 (unsigned long) this->rows
);
458 piglit_report_result(PIGLIT_FAIL
);
461 if (this->cols
< 1 || this->cols
> 4) {
462 printf("Columns must be between 1 and 4. Got: %lu\n",
463 (unsigned long) this->cols
);
464 piglit_report_result(PIGLIT_FAIL
);
470 * Parse a single number (floating point or integral) from one of the
471 * data rows, and store it in the location pointed to by \c data.
472 * Update \c text to point to the next character of input.
474 * If there is a parse failure, print a description of the problem and
475 * then return false. Otherwise return true.
478 vertex_attrib_description::parse_datum(const char **text
, void *data
) const
482 switch (this->data_type
) {
483 case GL_HALF_FLOAT
: {
484 unsigned short value
= strtohf_hex(*text
, &endptr
);
485 if (errno
== ERANGE
) {
486 printf("Could not parse as half float\n");
489 *((GLhalf
*) data
) = value
;
493 float value
= strtof_hex(*text
, &endptr
);
494 if (errno
== ERANGE
) {
495 printf("Could not parse as float\n");
498 *((GLfloat
*) data
) = value
;
502 double value
= strtod_hex(*text
, &endptr
);
503 if (errno
== ERANGE
) {
504 printf("Could not parse as double\n");
507 *((GLdouble
*) data
) = value
;
511 long value
= strtol_hex(*text
, &endptr
);
512 if (errno
== ERANGE
|| value
< SCHAR_MIN
|| value
> SCHAR_MAX
) {
513 printf("Could not parse as signed byte\n");
516 *((GLbyte
*) data
) = (GLbyte
) value
;
519 case GL_UNSIGNED_BYTE
: {
520 unsigned long value
= strtoul(*text
, &endptr
, 0);
521 if (errno
== ERANGE
|| value
> UCHAR_MAX
) {
522 printf("Could not parse as unsigned byte\n");
525 *((GLubyte
*) data
) = (GLubyte
) value
;
529 long value
= strtol_hex(*text
, &endptr
);
530 if (errno
== ERANGE
|| value
< SHRT_MIN
|| value
> SHRT_MAX
) {
531 printf("Could not parse as signed short\n");
534 *((GLshort
*) data
) = (GLshort
) value
;
537 case GL_UNSIGNED_SHORT
: {
538 unsigned long value
= strtoul(*text
, &endptr
, 0);
539 if (errno
== ERANGE
|| value
> USHRT_MAX
) {
540 printf("Could not parse as unsigned short\n");
543 *((GLushort
*) data
) = (GLushort
) value
;
547 long value
= strtol_hex(*text
, &endptr
);
548 if (errno
== ERANGE
) {
549 printf("Could not parse as signed integer\n");
552 *((GLint
*) data
) = (GLint
) value
;
555 case GL_UNSIGNED_INT
: {
556 unsigned long value
= strtoul(*text
, &endptr
, 0);
557 if (errno
== ERANGE
) {
558 printf("Could not parse as unsigned integer\n");
561 *((GLuint
*) data
) = (GLuint
) value
;
565 assert(!"Unexpected data type");
575 * Execute the necessary GL calls to bind this attribute to its data.
578 vertex_attrib_description::setup(size_t *offset
, size_t stride
) const
580 GLuint actual_index
= this->index
+ this->matrix_index
581 + this->array_index
* this->cols
;
582 switch (this->glsl_data_type
) {
584 glVertexAttribPointer(actual_index
, this->rows
,
585 this->data_type
, GL_FALSE
, stride
,
590 || !piglit_is_extension_supported("GL_ARB_vertex_attrib_64bit")) {
591 fprintf(stderr
,"vertex_attrib_description fail. no 64-bit float support\n");
594 if (this->data_type
!= GL_DOUBLE
) {
595 fprintf(stderr
,"vertex_attrib_description fail. the GL"
596 " type must be 'GL_DOUBLE' and it is '%s'\n",
597 piglit_get_prim_name(this->data_type
));
600 glVertexAttribLPointer(actual_index
, this->rows
,
601 this->data_type
, stride
,
605 if (piglit_is_gles() && piglit_get_gl_version() < 30) {
606 fprintf(stderr
,"vertex_attrib_description fail. no int support\n");
609 switch (this->data_type
) {
611 case GL_UNSIGNED_BYTE
:
613 case GL_UNSIGNED_SHORT
:
615 case GL_UNSIGNED_INT
:
618 fprintf(stderr
,"vertex_attrib_description fail. the GL"
619 " type '%s' is incompatible\n",
620 piglit_get_prim_name(this->data_type
));
623 glVertexAttribIPointer(actual_index
, this->rows
,
624 this->data_type
, stride
,
628 glEnableVertexAttribArray(actual_index
);
629 *offset
+= this->rows
* this->data_type_size
;
634 * Data structure containing all of the data parsed from the text
635 * input, as well as the methods that parse it and convert it to GL
641 vbo_data(std::string
const &text
, GLuint prog
);
642 size_t setup() const;
645 void parse_header_line(const std::string
&line
, GLuint prog
);
646 void parse_data_line(const std::string
&line
, unsigned int line_num
);
647 void parse_line(std::string line
, unsigned int line_num
, GLuint prog
);
650 * True if the header line has already been parsed.
655 * Description of each attribute.
657 std::vector
<vertex_attrib_description
> attribs
;
660 * Raw data buffer containing parsed numbers.
662 std::vector
<char> raw_data
;
665 * Number of bytes in each row of raw_data.
670 * Number of rows in raw_data.
678 is_blank_line(const std::string
&line
)
680 for (size_t i
= 0; i
< line
.size(); ++i
) {
681 if (!isspace(line
[i
]))
689 * Populate this->attribs and compute this->stride based on column
692 * If there is a parse failure, print a description of the problem and
693 * then exit with PIGLIT_FAIL.
696 vbo_data::parse_header_line(const std::string
&line
, GLuint prog
)
700 while (pos
< line
.size()) {
701 if (isspace(line
[pos
])) {
704 size_t column_header_end
= pos
;
705 while (column_header_end
< line
.size() &&
706 !isspace(line
[column_header_end
]))
708 std::string column_header
= line
.substr(
709 pos
, column_header_end
- pos
);
710 vertex_attrib_description
desc(
711 prog
, column_header
.c_str());
712 attribs
.push_back(desc
);
713 this->stride
+= desc
.rows
* desc
.data_type_size
;
714 pos
= column_header_end
+ 1;
721 * Convert a data row into binary form and append it to this->raw_data.
723 * If there is a parse failure, print a description of the problem and
724 * then exit with PIGLIT_FAIL.
727 vbo_data::parse_data_line(const std::string
&line
, unsigned int line_num
)
729 /* Allocate space in raw_data for this line */
730 size_t old_size
= this->raw_data
.size();
731 this->raw_data
.resize(old_size
+ this->stride
);
732 char *data_ptr
= &this->raw_data
[old_size
];
734 const char *line_ptr
= line
.c_str();
735 for (size_t i
= 0; i
< this->attribs
.size(); ++i
) {
736 for (size_t j
= 0; j
< this->attribs
[i
].rows
; ++j
) {
737 if (!this->attribs
[i
].parse_datum(&line_ptr
,
739 printf("At line %u of [vertex data] section\n",
741 printf("Offending text: %s\n", line_ptr
);
742 piglit_report_result(PIGLIT_FAIL
);
744 data_ptr
+= this->attribs
[i
].data_type_size
;
753 * Parse a line of input text.
755 * If there is a parse failure, print a description of the problem and
756 * then exit with PIGLIT_FAIL.
759 vbo_data::parse_line(std::string line
, unsigned int line_num
, GLuint prog
)
761 /* Ignore end-of-line comments */
762 line
= line
.substr(0, line
.find('#'));
764 /* Ignore blank or comment-only lines */
765 if (is_blank_line(line
))
768 if (!this->header_seen
) {
769 this->header_seen
= true;
770 parse_header_line(line
, prog
);
772 parse_data_line(line
, line_num
);
778 * Parse the input but don't execute any GL commands.
780 * If there is a parse failure, print a description of the problem and
781 * then exit with PIGLIT_FAIL.
783 vbo_data::vbo_data(const std::string
&text
, GLuint prog
)
784 : header_seen(false), stride(0), num_rows(0)
786 unsigned int line_num
= 1;
789 while (pos
< text
.size()) {
790 size_t end_of_line
= text
.find('\n', pos
);
791 if (end_of_line
== std::string::npos
)
792 end_of_line
= text
.size();
793 parse_line(text
.substr(pos
, end_of_line
), line_num
++, prog
);
794 pos
= end_of_line
+ 1;
800 * Execute the necessary GL commands to set up the vertex data passed
801 * to the constructor.
804 vbo_data::setup() const
806 GLuint buffer_handle
;
807 glGenBuffers(1, &buffer_handle
);
808 glBindBuffer(GL_ARRAY_BUFFER
, buffer_handle
);
809 glBufferData(GL_ARRAY_BUFFER
, this->stride
* this->num_rows
,
810 &this->raw_data
[0], GL_STATIC_DRAW
);
813 for (size_t i
= 0; i
< attribs
.size(); ++i
)
814 attribs
[i
].setup(&offset
, this->stride
);
816 /* Leave buffer bound for later draw calls */
818 return this->num_rows
;
823 * Set up a vertex buffer object for the program prog based on the
824 * data encoded in text_start. text_end indicates the end of the text
825 * string; if it is NULL, the string is assumed to be null-terminated.
827 * Return value is the number of rows of vertex data found.
829 * For details about the format of the text string, see the comment at
830 * the top of this file.
833 setup_vbo_from_text(GLuint prog
, const char *text_start
, const char *text_end
)
835 if (text_end
== NULL
)
836 text_end
= text_start
+ strlen(text_start
);
837 std::string
text(text_start
, text_end
);
838 return vbo_data(text
, prog
).setup();