1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Implementation-defined limit tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "es3fImplementationLimitTests.hpp"
25 #include "tcuTestLog.hpp"
26 #include "gluDefs.hpp"
27 #include "gluStrUtil.hpp"
28 #include "gluRenderContext.hpp"
29
30 #include <vector>
31 #include <set>
32 #include <algorithm>
33 #include <iterator>
34 #include <limits>
35
36 #include "glwEnums.hpp"
37 #include "glwFunctions.hpp"
38
39 namespace deqp
40 {
41 namespace gles3
42 {
43 namespace Functional
44 {
45
46 using std::vector;
47 using std::string;
48 using std::set;
49 using namespace glw; // GL types
50
51 namespace LimitQuery
52 {
53
54 // Query function template.
55 template<typename T>
56 T query (const glw::Functions& gl, deUint32 param);
57
58 // Compare template.
59 template<typename T>
compare(const T & min,const T & reported)60 inline bool compare (const T& min, const T& reported) { return min <= reported; }
61
62 // Types for queries
63
64 struct NegInt
65 {
66 GLint value;
NegIntdeqp::gles3::Functional::LimitQuery::NegInt67 NegInt (GLint value_) : value(value_) {}
68 };
69
operator <<(std::ostream & str,const NegInt & v)70 std::ostream& operator<< (std::ostream& str, const NegInt& v) { return str << v.value; }
71
72 struct FloatRange
73 {
74 float min;
75 float max;
FloatRangedeqp::gles3::Functional::LimitQuery::FloatRange76 FloatRange (float min_, float max_) : min(min_), max(max_) {}
77 };
78
operator <<(std::ostream & str,const FloatRange & range)79 std::ostream& operator<< (std::ostream& str, const FloatRange& range) { return str << range.min << ", " << range.max; }
80
81 struct AlignmentInt
82 {
83 GLint value;
AlignmentIntdeqp::gles3::Functional::LimitQuery::AlignmentInt84 AlignmentInt (GLint value_) : value(value_) {}
85 };
86
operator <<(std::ostream & str,const AlignmentInt & v)87 std::ostream& operator<< (std::ostream& str, const AlignmentInt& v) { return str << v.value; }
88
89 // For custom formatting
90 struct Boolean
91 {
92 GLboolean value;
Booleandeqp::gles3::Functional::LimitQuery::Boolean93 Boolean (GLboolean value_) : value(value_) {}
94 };
95
operator <<(std::ostream & str,const Boolean & boolean)96 std::ostream& operator<< (std::ostream& str, const Boolean& boolean) { return str << (boolean.value ? "GL_TRUE" : "GL_FALSE"); }
97
98 // Query function implementations.
99 template<>
query(const glw::Functions & gl,deUint32 param)100 GLint query<GLint> (const glw::Functions& gl, deUint32 param)
101 {
102 GLint val = -1;
103 gl.getIntegerv(param, &val);
104 return val;
105 }
106
107 template<>
query(const glw::Functions & gl,deUint32 param)108 GLint64 query<GLint64> (const glw::Functions& gl, deUint32 param)
109 {
110 GLint64 val = -1;
111 gl.getInteger64v(param, &val);
112 return val;
113 }
114
115 template<>
query(const glw::Functions & gl,deUint32 param)116 GLuint64 query<GLuint64> (const glw::Functions& gl, deUint32 param)
117 {
118 GLint64 val = 0;
119 gl.getInteger64v(param, &val);
120 return (GLuint64)val;
121 }
122
123 template<>
query(const glw::Functions & gl,deUint32 param)124 GLfloat query<GLfloat> (const glw::Functions& gl,deUint32 param)
125 {
126 GLfloat val = -1000.f;
127 gl.getFloatv(param, &val);
128 return val;
129 }
130
131 template<>
query(const glw::Functions & gl,deUint32 param)132 NegInt query<NegInt> (const glw::Functions& gl, deUint32 param)
133 {
134 return NegInt(query<GLint>(gl, param));
135 }
136
137 template<>
query(const glw::Functions & gl,deUint32 param)138 Boolean query<Boolean> (const glw::Functions& gl, deUint32 param)
139 {
140 GLboolean val = GL_FALSE;
141 gl.getBooleanv(param, &val);
142 return Boolean(val);
143 }
144
145 template<>
query(const glw::Functions & gl,deUint32 param)146 FloatRange query<FloatRange> (const glw::Functions& gl, deUint32 param)
147 {
148 float v[2] = { -1.0f, -1.0f };
149 gl.getFloatv(param, &v[0]);
150 return FloatRange(v[0], v[1]);
151 }
152
153 template<>
query(const glw::Functions & gl,deUint32 param)154 AlignmentInt query<AlignmentInt> (const glw::Functions& gl, deUint32 param)
155 {
156 return AlignmentInt(query<GLint>(gl, param));
157 }
158
159 // Special comparison operators
160 template<>
compare(const Boolean & min,const Boolean & reported)161 bool compare<Boolean> (const Boolean& min, const Boolean& reported)
162 {
163 return !min.value || (min.value && reported.value);
164 }
165
166 template<>
compare(const NegInt & min,const NegInt & reported)167 bool compare<NegInt> (const NegInt& min, const NegInt& reported)
168 {
169 // Reverse comparison.
170 return reported.value <= min.value;
171 }
172
173 template<>
compare(const FloatRange & min,const FloatRange & reported)174 bool compare<FloatRange> (const FloatRange& min, const FloatRange& reported)
175 {
176 return reported.min <= min.min && min.max <= reported.max;
177 }
178
179 template<>
compare(const AlignmentInt & min,const AlignmentInt & reported)180 bool compare<AlignmentInt> (const AlignmentInt& min, const AlignmentInt& reported)
181 {
182 // Reverse comparison.
183 return reported.value <= min.value;
184 }
185
186 // Special error descriptions
187
188 enum QueryClass
189 {
190 CLASS_VALUE = 0,
191 CLASS_RANGE,
192 CLASS_ALIGNMENT,
193 };
194
195 template <QueryClass Class>
196 struct QueryClassTraits
197 {
198 static const char* const s_errorDescription;
199 };
200
201 template <>
202 const char* const QueryClassTraits<CLASS_VALUE>::s_errorDescription = "reported value is less than minimum required value!";
203
204 template <>
205 const char* const QueryClassTraits<CLASS_RANGE>::s_errorDescription = "reported range does not contain the minimum required range!";
206
207 template <>
208 const char* const QueryClassTraits<CLASS_ALIGNMENT>::s_errorDescription = "reported alignment is larger than minimum required aligmnent!";
209
210 template <typename T>
211 struct QueryTypeTraits
212 {
213 };
214
215 template <> struct QueryTypeTraits<GLint> { enum { CLASS = CLASS_VALUE }; };
216 template <> struct QueryTypeTraits<GLint64> { enum { CLASS = CLASS_VALUE }; };
217 template <> struct QueryTypeTraits<GLuint64> { enum { CLASS = CLASS_VALUE }; };
218 template <> struct QueryTypeTraits<GLfloat> { enum { CLASS = CLASS_VALUE }; };
219 template <> struct QueryTypeTraits<Boolean> { enum { CLASS = CLASS_VALUE }; };
220 template <> struct QueryTypeTraits<NegInt> { enum { CLASS = CLASS_VALUE }; };
221 template <> struct QueryTypeTraits<FloatRange> { enum { CLASS = CLASS_RANGE }; };
222 template <> struct QueryTypeTraits<AlignmentInt> { enum { CLASS = CLASS_ALIGNMENT }; };
223
224 } // LimitQuery
225
226 using namespace LimitQuery;
227 using tcu::TestLog;
228
229 template<typename T>
230 class LimitQueryCase : public TestCase
231 {
232 public:
LimitQueryCase(Context & context,const char * name,const char * description,deUint32 limit,const T & minRequiredValue)233 LimitQueryCase (Context& context, const char* name, const char* description, deUint32 limit, const T& minRequiredValue)
234 : TestCase (context, name, description)
235 , m_limit (limit)
236 , m_minRequiredValue (minRequiredValue)
237 {
238 }
239
iterate(void)240 IterateResult iterate (void)
241 {
242 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
243 const T value = query<T>(m_context.getRenderContext().getFunctions(), m_limit);
244 GLU_EXPECT_NO_ERROR(gl.getError(), "Query failed");
245
246 const bool isOk = compare<T>(m_minRequiredValue, value);
247
248 m_testCtx.getLog() << TestLog::Message << "Reported: " << value << TestLog::EndMessage;
249 m_testCtx.getLog() << TestLog::Message << "Minimum required: " << m_minRequiredValue << TestLog::EndMessage;
250
251 if (!isOk)
252 m_testCtx.getLog() << TestLog::Message << "FAIL: " << QueryClassTraits<(QueryClass)QueryTypeTraits<T>::CLASS>::s_errorDescription << TestLog::EndMessage;
253
254 m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
255 isOk ? "Pass" : "Requirement not satisfied");
256 return STOP;
257 }
258
259 private:
260 deUint32 m_limit;
261 T m_minRequiredValue;
262 };
263
264 static const deUint32 s_requiredCompressedTexFormats[] =
265 {
266 GL_COMPRESSED_R11_EAC,
267 GL_COMPRESSED_SIGNED_R11_EAC,
268 GL_COMPRESSED_RG11_EAC,
269 GL_COMPRESSED_SIGNED_RG11_EAC,
270 GL_COMPRESSED_RGB8_ETC2,
271 GL_COMPRESSED_SRGB8_ETC2,
272 GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
273 GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
274 GL_COMPRESSED_RGBA8_ETC2_EAC,
275 GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC
276 };
277
278 class CompressedTextureFormatsQueryCase : public TestCase
279 {
280 public:
CompressedTextureFormatsQueryCase(Context & context)281 CompressedTextureFormatsQueryCase (Context& context)
282 : TestCase(context, "compressed_texture_formats", "GL_COMPRESSED_TEXTURE_FORMATS")
283 {
284 }
285
iterate(void)286 IterateResult iterate (void)
287 {
288 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
289 const GLint numFormats = query<GLint>(gl, GL_NUM_COMPRESSED_TEXTURE_FORMATS);
290 vector<GLint> formats (numFormats);
291 bool allFormatsOk = true;
292
293 if (numFormats > 0)
294 gl.getIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, &formats[0]);
295
296 GLU_EXPECT_NO_ERROR(gl.getError(), "Query failed");
297
298 // Log formats.
299 m_testCtx.getLog() << TestLog::Message << "Reported:" << TestLog::EndMessage;
300 for (vector<GLint>::const_iterator fmt = formats.begin(); fmt != formats.end(); fmt++)
301 m_testCtx.getLog() << TestLog::Message << glu::getCompressedTextureFormatStr(*fmt) << TestLog::EndMessage;
302
303 // Check that all required formats are in list.
304 {
305 set<GLint> formatSet(formats.begin(), formats.end());
306
307 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_requiredCompressedTexFormats); ndx++)
308 {
309 const deUint32 fmt = s_requiredCompressedTexFormats[ndx];
310 const bool found = formatSet.find(fmt) != formatSet.end();
311
312 if (!found)
313 {
314 m_testCtx.getLog() << TestLog::Message << "ERROR: " << glu::getCompressedTextureFormatStr(fmt) << " is missing!" << TestLog::EndMessage;
315 allFormatsOk = false;
316 }
317 }
318 }
319
320 m_testCtx.setTestResult(allFormatsOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
321 allFormatsOk ? "Pass" : "Requirement not satisfied");
322 return STOP;
323 }
324 };
325
queryExtensionsNonIndexed(const glw::Functions & gl)326 static vector<string> queryExtensionsNonIndexed (const glw::Functions& gl)
327 {
328 const string extensionStr = (const char*)gl.getString(GL_EXTENSIONS);
329 vector<string> extensionList;
330 size_t pos = 0;
331
332 for (;;)
333 {
334 const size_t nextPos = extensionStr.find(' ', pos);
335 const size_t len = nextPos == string::npos ? extensionStr.length()-pos : nextPos-pos;
336
337 if (len > 0)
338 extensionList.push_back(extensionStr.substr(pos, len));
339
340 if (nextPos == string::npos)
341 break;
342 else
343 pos = nextPos+1;
344 }
345
346 return extensionList;
347 }
348
queryExtensionsIndexed(const glw::Functions & gl)349 static vector<string> queryExtensionsIndexed (const glw::Functions& gl)
350 {
351 const int numExtensions = query<GLint>(gl, GL_NUM_EXTENSIONS);
352 vector<string> extensions (numExtensions);
353
354 GLU_EXPECT_NO_ERROR(gl.getError(), "GL_NUM_EXTENSIONS query failed");
355
356 for (int ndx = 0; ndx < numExtensions; ndx++)
357 extensions[ndx] = (const char*)gl.getStringi(GL_EXTENSIONS, (GLuint)ndx);
358
359 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetStringi(GL_EXTENSIONS) failed");
360
361 return extensions;
362 }
363
compareExtensionLists(const vector<string> & a,const vector<string> & b)364 static bool compareExtensionLists (const vector<string>& a, const vector<string>& b)
365 {
366 if (a.size() != b.size())
367 return false;
368
369 set<string> extsInB(b.begin(), b.end());
370
371 for (vector<string>::const_iterator i = a.begin(); i != a.end(); ++i)
372 {
373 if (extsInB.find(*i) == extsInB.end())
374 return false;
375 }
376
377 return true;
378 }
379
380 class ExtensionQueryCase : public TestCase
381 {
382 public:
ExtensionQueryCase(Context & context)383 ExtensionQueryCase (Context& context)
384 : TestCase(context, "extensions", "GL_EXTENSIONS")
385 {
386 }
387
iterate(void)388 IterateResult iterate (void)
389 {
390 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
391 const vector<string> nonIndexedExts = queryExtensionsNonIndexed(gl);
392 const vector<string> indexedExts = queryExtensionsIndexed(gl);
393 const bool isOk = compareExtensionLists(nonIndexedExts, indexedExts);
394
395 m_testCtx.getLog() << TestLog::Message << "Extensions as reported by glGetStringi(GL_EXTENSIONS):" << TestLog::EndMessage;
396 for (vector<string>::const_iterator ext = indexedExts.begin(); ext != indexedExts.end(); ++ext)
397 m_testCtx.getLog() << TestLog::Message << *ext << TestLog::EndMessage;
398
399 if (!isOk)
400 {
401 m_testCtx.getLog() << TestLog::Message << "Extensions as reported by glGetString(GL_EXTENSIONS):" << TestLog::EndMessage;
402 for (vector<string>::const_iterator ext = nonIndexedExts.begin(); ext != nonIndexedExts.end(); ++ext)
403 m_testCtx.getLog() << TestLog::Message << *ext << TestLog::EndMessage;
404
405 m_testCtx.getLog() << TestLog::Message << "ERROR: Extension lists do not match!" << TestLog::EndMessage;
406 }
407
408 m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
409 isOk ? "Pass" : "Invalid extension list");
410 return STOP;
411 }
412 };
413
ImplementationLimitTests(Context & context)414 ImplementationLimitTests::ImplementationLimitTests (Context& context)
415 : TestCaseGroup(context, "implementation_limits", "Implementation-defined limits")
416 {
417 }
418
~ImplementationLimitTests(void)419 ImplementationLimitTests::~ImplementationLimitTests (void)
420 {
421 }
422
init(void)423 void ImplementationLimitTests::init (void)
424 {
425 const int minVertexUniformBlocks = 12;
426 const int minVertexUniformComponents = 1024;
427
428 const int minFragmentUniformBlocks = 12;
429 const int minFragmentUniformComponents = 896;
430
431 const int minUniformBlockSize = 16384;
432 const int minCombinedVertexUniformComponents = (minVertexUniformBlocks*minUniformBlockSize)/4 + minVertexUniformComponents;
433 const int minCombinedFragmentUniformComponents = (minFragmentUniformBlocks*minUniformBlockSize)/4 + minFragmentUniformComponents;
434
435 #define LIMIT_CASE(NAME, PARAM, TYPE, MIN_VAL) \
436 addChild(new LimitQueryCase<TYPE>(m_context, #NAME, #PARAM, PARAM, MIN_VAL))
437
438 LIMIT_CASE(max_element_index, GL_MAX_ELEMENT_INDEX, GLint64, (1<<24)-1);
439 LIMIT_CASE(subpixel_bits, GL_SUBPIXEL_BITS, GLint, 4);
440 LIMIT_CASE(max_3d_texture_size, GL_MAX_3D_TEXTURE_SIZE, GLint, 256);
441 LIMIT_CASE(max_texture_size, GL_MAX_TEXTURE_SIZE, GLint, 2048);
442 LIMIT_CASE(max_array_texture_layers, GL_MAX_ARRAY_TEXTURE_LAYERS, GLint, 256);
443 LIMIT_CASE(max_texture_lod_bias, GL_MAX_TEXTURE_LOD_BIAS, GLfloat, 2.0f);
444 LIMIT_CASE(max_cube_map_texture_size, GL_MAX_CUBE_MAP_TEXTURE_SIZE, GLint, 2048);
445 LIMIT_CASE(max_renderbuffer_size, GL_MAX_RENDERBUFFER_SIZE, GLint, 2048);
446 LIMIT_CASE(max_draw_buffers, GL_MAX_DRAW_BUFFERS, GLint, 4);
447 LIMIT_CASE(max_color_attachments, GL_MAX_COLOR_ATTACHMENTS, GLint, 4);
448 // GL_MAX_VIEWPORT_DIMS
449 LIMIT_CASE(aliased_point_size_range, GL_ALIASED_POINT_SIZE_RANGE, FloatRange, FloatRange(1,1));
450 LIMIT_CASE(aliased_line_width_range, GL_ALIASED_LINE_WIDTH_RANGE, FloatRange, FloatRange(1,1));
451 LIMIT_CASE(max_elements_indices, GL_MAX_ELEMENTS_INDICES, GLint, 0);
452 LIMIT_CASE(max_elements_vertices, GL_MAX_ELEMENTS_VERTICES, GLint, 0);
453 LIMIT_CASE(num_compressed_texture_formats, GL_NUM_COMPRESSED_TEXTURE_FORMATS, GLint, DE_LENGTH_OF_ARRAY(s_requiredCompressedTexFormats));
454 addChild(new CompressedTextureFormatsQueryCase(m_context)); // GL_COMPRESSED_TEXTURE_FORMATS
455 // GL_PROGRAM_BINARY_FORMATS
456 LIMIT_CASE(num_program_binary_formats, GL_NUM_PROGRAM_BINARY_FORMATS, GLint, 0);
457 // GL_SHADER_BINARY_FORMATS
458 LIMIT_CASE(num_shader_binary_formats, GL_NUM_SHADER_BINARY_FORMATS, GLint, 0);
459 LIMIT_CASE(shader_compiler, GL_SHADER_COMPILER, Boolean, GL_TRUE);
460 // Shader data type ranges & precisions
461 LIMIT_CASE(max_server_wait_timeout, GL_MAX_SERVER_WAIT_TIMEOUT, GLuint64, 0);
462
463 // Version and extension support
464 addChild(new ExtensionQueryCase(m_context)); // GL_EXTENSIONS + consistency validation
465 LIMIT_CASE(num_extensions, GL_NUM_EXTENSIONS, GLint, 0);
466 LIMIT_CASE(major_version, GL_MAJOR_VERSION, GLint, 3);
467 LIMIT_CASE(minor_version, GL_MINOR_VERSION, GLint, 0);
468 // GL_RENDERER
469 // GL_SHADING_LANGUAGE_VERSION
470 // GL_VENDOR
471 // GL_VERSION
472
473 // Vertex shader limits
474 LIMIT_CASE(max_vertex_attribs, GL_MAX_VERTEX_ATTRIBS, GLint, 16);
475 LIMIT_CASE(max_vertex_uniform_components, GL_MAX_VERTEX_UNIFORM_COMPONENTS, GLint, minVertexUniformComponents);
476 LIMIT_CASE(max_vertex_uniform_vectors, GL_MAX_VERTEX_UNIFORM_VECTORS, GLint, minVertexUniformComponents/4);
477 LIMIT_CASE(max_vertex_uniform_blocks, GL_MAX_VERTEX_UNIFORM_BLOCKS, GLint, minVertexUniformBlocks);
478 LIMIT_CASE(max_vertex_output_components, GL_MAX_VERTEX_OUTPUT_COMPONENTS, GLint, 64);
479 LIMIT_CASE(max_vertex_texture_image_units, GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, GLint, 16);
480
481 // Fragment shader limits
482 LIMIT_CASE(max_fragment_uniform_components, GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, GLint, minFragmentUniformComponents);
483 LIMIT_CASE(max_fragment_uniform_vectors, GL_MAX_FRAGMENT_UNIFORM_VECTORS, GLint, minFragmentUniformComponents/4);
484 LIMIT_CASE(max_fragment_uniform_blocks, GL_MAX_FRAGMENT_UNIFORM_BLOCKS, GLint, minFragmentUniformBlocks);
485 LIMIT_CASE(max_fragment_input_components, GL_MAX_FRAGMENT_INPUT_COMPONENTS, GLint, 60);
486 LIMIT_CASE(max_texture_image_units, GL_MAX_TEXTURE_IMAGE_UNITS, GLint, 16);
487 LIMIT_CASE(min_program_texel_offset, GL_MIN_PROGRAM_TEXEL_OFFSET, NegInt, -8);
488 LIMIT_CASE(max_program_texel_offset, GL_MAX_PROGRAM_TEXEL_OFFSET, GLint, 7);
489
490 // Aggregate shader limits
491 LIMIT_CASE(max_uniform_buffer_bindings, GL_MAX_UNIFORM_BUFFER_BINDINGS, GLint, 24);
492 LIMIT_CASE(max_uniform_block_size, GL_MAX_UNIFORM_BLOCK_SIZE, GLint64, minUniformBlockSize);
493 LIMIT_CASE(uniform_buffer_offset_alignment, GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, AlignmentInt, 256);
494 LIMIT_CASE(max_combined_uniform_blocks, GL_MAX_COMBINED_UNIFORM_BLOCKS, GLint, 24);
495 LIMIT_CASE(max_combined_vertex_uniform_components, GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS, GLint64, minCombinedVertexUniformComponents);
496 LIMIT_CASE(max_combined_fragment_uniform_components, GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS, GLint64, minCombinedFragmentUniformComponents);
497 LIMIT_CASE(max_varying_components, GL_MAX_VARYING_COMPONENTS, GLint, 60);
498 LIMIT_CASE(max_varying_vectors, GL_MAX_VARYING_VECTORS, GLint, 15);
499 LIMIT_CASE(max_combined_texture_image_units, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, GLint, 32);
500
501 // Transform feedback limits
502 LIMIT_CASE(max_transform_feedback_interleaved_components, GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, GLint, 64);
503 LIMIT_CASE(max_transform_feedback_separate_attribs, GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, GLint, 4);
504 LIMIT_CASE(max_transform_feedback_separate_components, GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, GLint, 4);
505 }
506
507 } // Functional
508 } // gles3
509 } // deqp
510