1 /*-------------------------------------------------------------------------
2 * OpenGL Conformance Test Suite
3 * -----------------------------
4 *
5 * Copyright (c) 2016 The Khronos Group Inc.
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 glcRobustBufferAccessBehaviorTests.cpp
21 * \brief Implements conformance tests for "Robust Buffer Access Behavior" functionality.
22 */ /*-------------------------------------------------------------------*/
23
24 #include "glcRobustBufferAccessBehaviorTests.hpp"
25
26 #include "deSharedPtr.hpp"
27 #include "gluContextInfo.hpp"
28 #include "gluDefs.hpp"
29 #include "gluShaderUtil.hpp"
30 #include "glwEnums.hpp"
31 #include "glwFunctions.hpp"
32 #include "tcuCommandLine.hpp"
33 #include "tcuStringTemplate.hpp"
34 #include "tcuTestLog.hpp"
35
36 #include <cstring>
37 #include <string>
38
39 using namespace glw;
40
41 namespace glcts
42 {
43 namespace RobustBufferAccessBehavior
44 {
45 /* Buffer constants */
46 const GLuint Buffer::m_invalid_id = -1;
47
48 const GLenum Buffer::m_targets[Buffer::m_n_targets] = {
49 GL_ARRAY_BUFFER, /* 0 */
50 GL_ATOMIC_COUNTER_BUFFER, /* 1 */
51 GL_COPY_READ_BUFFER, /* 2 */
52 GL_COPY_WRITE_BUFFER, /* 3 */
53 GL_DISPATCH_INDIRECT_BUFFER, /* 4 */
54 GL_DRAW_INDIRECT_BUFFER, /* 5 */
55 GL_ELEMENT_ARRAY_BUFFER, /* 6 */
56 GL_PIXEL_PACK_BUFFER, /* 7 */
57 GL_PIXEL_UNPACK_BUFFER, /* 8 */
58 GL_QUERY_BUFFER, /* 9 */
59 GL_SHADER_STORAGE_BUFFER, /* 10 */
60 GL_TRANSFORM_FEEDBACK_BUFFER, /* 11 */
61 GL_UNIFORM_BUFFER, /* 12 */
62 };
63
64 /** Constructor.
65 *
66 * @param context CTS context.
67 **/
Buffer(const glw::Functions & gl)68 Buffer::Buffer(const glw::Functions &gl) : m_id(m_invalid_id), m_gl(gl), m_target(GL_ARRAY_BUFFER)
69 {
70 }
71
72 /** Destructor
73 *
74 **/
~Buffer()75 Buffer::~Buffer()
76 {
77 Release();
78 }
79
80 /** Initialize buffer instance
81 *
82 * @param target Buffer target
83 * @param usage Buffer usage enum
84 * @param size <size> parameter
85 * @param data <data> parameter
86 **/
InitData(glw::GLenum target,glw::GLenum usage,glw::GLsizeiptr size,const glw::GLvoid * data)87 void Buffer::InitData(glw::GLenum target, glw::GLenum usage, glw::GLsizeiptr size, const glw::GLvoid *data)
88 {
89 /* Delete previous buffer instance */
90 Release();
91
92 m_target = target;
93
94 Generate(m_gl, m_id);
95 Bind(m_gl, m_id, m_target);
96 Data(m_gl, m_target, usage, size, data);
97 }
98
99 /** Release buffer instance
100 *
101 **/
Release()102 void Buffer::Release()
103 {
104 if (m_invalid_id != m_id)
105 {
106 m_gl.deleteBuffers(1, &m_id);
107 m_id = m_invalid_id;
108 }
109 }
110
111 /** Binds buffer to its target
112 *
113 **/
Bind() const114 void Buffer::Bind() const
115 {
116 Bind(m_gl, m_id, m_target);
117 }
118
119 /** Binds indexed buffer
120 *
121 * @param index <index> parameter
122 **/
BindBase(glw::GLuint index) const123 void Buffer::BindBase(glw::GLuint index) const
124 {
125 BindBase(m_gl, m_id, m_target, index);
126 }
127
128 /** Bind buffer to given target
129 *
130 * @param gl GL functions
131 * @param id Id of buffer
132 * @param target Buffer target
133 **/
Bind(const glw::Functions & gl,glw::GLuint id,glw::GLenum target)134 void Buffer::Bind(const glw::Functions &gl, glw::GLuint id, glw::GLenum target)
135 {
136 gl.bindBuffer(target, id);
137 GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer");
138 }
139
140 /** Binds indexed buffer
141 *
142 * @param gl GL functions
143 * @param id Id of buffer
144 * @param target Buffer target
145 * @param index <index> parameter
146 **/
BindBase(const glw::Functions & gl,glw::GLuint id,glw::GLenum target,glw::GLuint index)147 void Buffer::BindBase(const glw::Functions &gl, glw::GLuint id, glw::GLenum target, glw::GLuint index)
148 {
149 gl.bindBufferBase(target, index, id);
150 GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferBase");
151 }
152
153 /** Allocate memory for buffer and sends initial content
154 *
155 * @param gl GL functions
156 * @param target Buffer target
157 * @param usage Buffer usage enum
158 * @param size <size> parameter
159 * @param data <data> parameter
160 **/
Data(const glw::Functions & gl,glw::GLenum target,glw::GLenum usage,glw::GLsizeiptr size,const glw::GLvoid * data)161 void Buffer::Data(const glw::Functions &gl, glw::GLenum target, glw::GLenum usage, glw::GLsizeiptr size,
162 const glw::GLvoid *data)
163 {
164 gl.bufferData(target, size, data, usage);
165 GLU_EXPECT_NO_ERROR(gl.getError(), "BufferData");
166 }
167
168 /** Generate buffer
169 *
170 * @param gl GL functions
171 * @param out_id Id of buffer
172 **/
Generate(const glw::Functions & gl,glw::GLuint & out_id)173 void Buffer::Generate(const glw::Functions &gl, glw::GLuint &out_id)
174 {
175 GLuint id = m_invalid_id;
176
177 gl.genBuffers(1, &id);
178 GLU_EXPECT_NO_ERROR(gl.getError(), "GenBuffers");
179
180 if (m_invalid_id == id)
181 {
182 TCU_FAIL("Got invalid id");
183 }
184
185 out_id = id;
186 }
187
188 /** Update range of buffer
189 *
190 * @param gl GL functions
191 * @param target Buffer target
192 * @param offset Offset in buffer
193 * @param size <size> parameter
194 * @param data <data> parameter
195 **/
SubData(const glw::Functions & gl,glw::GLenum target,glw::GLintptr offset,glw::GLsizeiptr size,glw::GLvoid * data)196 void Buffer::SubData(const glw::Functions &gl, glw::GLenum target, glw::GLintptr offset, glw::GLsizeiptr size,
197 glw::GLvoid *data)
198 {
199 gl.bufferSubData(target, offset, size, data);
200 GLU_EXPECT_NO_ERROR(gl.getError(), "BufferSubData");
201 }
202
203 /* Framebuffer constants */
204 const GLuint Framebuffer::m_invalid_id = -1;
205
206 /** Constructor.
207 *
208 * @param context CTS context.
209 **/
Framebuffer(const glw::Functions & gl)210 Framebuffer::Framebuffer(const glw::Functions &gl) : m_id(m_invalid_id), m_gl(gl)
211 {
212 /* Nothing to done here */
213 }
214
215 /** Destructor
216 *
217 **/
~Framebuffer()218 Framebuffer::~Framebuffer()
219 {
220 Release();
221 }
222
223 /** Release texture instance
224 *
225 **/
Release()226 void Framebuffer::Release()
227 {
228 if (m_invalid_id != m_id)
229 {
230 m_gl.deleteFramebuffers(1, &m_id);
231 m_id = m_invalid_id;
232 }
233 }
234
235 /** Attach texture to specified attachment
236 *
237 * @param gl GL functions
238 * @param target Framebuffer target
239 * @param attachment Attachment
240 * @param texture_id Texture id
241 * @param level Level of mipmap
242 * @param width Texture width
243 * @param height Texture height
244 **/
AttachTexture(const glw::Functions & gl,glw::GLenum target,glw::GLenum attachment,glw::GLuint texture_id,glw::GLint level,glw::GLuint width,glw::GLuint height)245 void Framebuffer::AttachTexture(const glw::Functions &gl, glw::GLenum target, glw::GLenum attachment,
246 glw::GLuint texture_id, glw::GLint level, glw::GLuint width, glw::GLuint height)
247 {
248 gl.framebufferTexture(target, attachment, texture_id, level);
249 GLU_EXPECT_NO_ERROR(gl.getError(), "FramebufferTexture");
250
251 gl.viewport(0 /* x */, 0 /* y */, width, height);
252 GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport");
253 }
254
255 /** Binds framebuffer to DRAW_FRAMEBUFFER
256 *
257 * @param gl GL functions
258 * @param target Framebuffer target
259 * @param id ID of framebuffer
260 **/
Bind(const glw::Functions & gl,glw::GLenum target,glw::GLuint id)261 void Framebuffer::Bind(const glw::Functions &gl, glw::GLenum target, glw::GLuint id)
262 {
263 gl.bindFramebuffer(target, id);
264 GLU_EXPECT_NO_ERROR(gl.getError(), "BindFramebuffer");
265 }
266
267 /** Generate framebuffer
268 *
269 **/
Generate(const glw::Functions & gl,glw::GLuint & out_id)270 void Framebuffer::Generate(const glw::Functions &gl, glw::GLuint &out_id)
271 {
272 GLuint id = m_invalid_id;
273
274 gl.genFramebuffers(1, &id);
275 GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers");
276
277 if (m_invalid_id == id)
278 {
279 TCU_FAIL("Invalid id");
280 }
281
282 out_id = id;
283 }
284
285 /* Program constants */
286 const GLuint Program::m_invalid_id = 0;
287
288 /** Constructor.
289 *
290 * @param context CTS context.
291 **/
Program(const glw::Functions & gl)292 Program::Program(const glw::Functions &gl)
293 : m_id(m_invalid_id)
294 , m_compute(gl)
295 , m_fragment(gl)
296 , m_geometry(gl)
297 , m_tess_ctrl(gl)
298 , m_tess_eval(gl)
299 , m_vertex(gl)
300 , m_gl(gl)
301 {
302 /* Nothing to be done here */
303 }
304
305 /** Destructor
306 *
307 **/
~Program()308 Program::~Program()
309 {
310 Release();
311 }
312
313 /** Initialize program instance
314 *
315 * @param compute_shader Compute shader source code
316 * @param fragment_shader Fragment shader source code
317 * @param geometry_shader Geometry shader source code
318 * @param tesselation_control_shader Tesselation control shader source code
319 * @param tesselation_evaluation_shader Tesselation evaluation shader source code
320 * @param vertex_shader Vertex shader source code
321 **/
Init(const std::string & compute_shader,const std::string & fragment_shader,const std::string & geometry_shader,const std::string & tesselation_control_shader,const std::string & tesselation_evaluation_shader,const std::string & vertex_shader)322 void Program::Init(const std::string &compute_shader, const std::string &fragment_shader,
323 const std::string &geometry_shader, const std::string &tesselation_control_shader,
324 const std::string &tesselation_evaluation_shader, const std::string &vertex_shader)
325 {
326 /* Delete previous program */
327 Release();
328
329 /* Initialize shaders */
330 m_compute.Init(GL_COMPUTE_SHADER, compute_shader);
331 m_fragment.Init(GL_FRAGMENT_SHADER, fragment_shader);
332 m_geometry.Init(GL_GEOMETRY_SHADER, geometry_shader);
333 m_tess_ctrl.Init(GL_TESS_CONTROL_SHADER, tesselation_control_shader);
334 m_tess_eval.Init(GL_TESS_EVALUATION_SHADER, tesselation_evaluation_shader);
335 m_vertex.Init(GL_VERTEX_SHADER, vertex_shader);
336
337 /* Create program, set up transform feedback and attach shaders */
338 Create(m_gl, m_id);
339 Attach(m_gl, m_id, m_compute.m_id);
340 Attach(m_gl, m_id, m_fragment.m_id);
341 Attach(m_gl, m_id, m_geometry.m_id);
342 Attach(m_gl, m_id, m_tess_ctrl.m_id);
343 Attach(m_gl, m_id, m_tess_eval.m_id);
344 Attach(m_gl, m_id, m_vertex.m_id);
345
346 /* Link program */
347 Link(m_gl, m_id);
348 }
349
350 /** Release program instance
351 *
352 **/
Release()353 void Program::Release()
354 {
355 if (m_invalid_id != m_id)
356 {
357 Use(m_gl, m_invalid_id);
358
359 m_gl.deleteProgram(m_id);
360 m_id = m_invalid_id;
361 }
362
363 m_compute.Release();
364 m_fragment.Release();
365 m_geometry.Release();
366 m_tess_ctrl.Release();
367 m_tess_eval.Release();
368 m_vertex.Release();
369 }
370
371 /** Set program as active
372 *
373 **/
Use() const374 void Program::Use() const
375 {
376 Use(m_gl, m_id);
377 }
378
379 /** Attach shader to program
380 *
381 * @param gl GL functions
382 * @param program_id Id of program
383 * @param shader_id Id of shader
384 **/
Attach(const glw::Functions & gl,glw::GLuint program_id,glw::GLuint shader_id)385 void Program::Attach(const glw::Functions &gl, glw::GLuint program_id, glw::GLuint shader_id)
386 {
387 /* Quick checks */
388 if ((m_invalid_id == program_id) || (Shader::m_invalid_id == shader_id))
389 {
390 return;
391 }
392
393 gl.attachShader(program_id, shader_id);
394 GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader");
395 }
396
397 /** Create program instance
398 *
399 * @param gl GL functions
400 * @param out_id Id of program
401 **/
Create(const glw::Functions & gl,glw::GLuint & out_id)402 void Program::Create(const glw::Functions &gl, glw::GLuint &out_id)
403 {
404 const GLuint id = gl.createProgram();
405 GLU_EXPECT_NO_ERROR(gl.getError(), "CreateProgram");
406
407 if (m_invalid_id == id)
408 {
409 TCU_FAIL("Failed to create program");
410 }
411
412 out_id = id;
413 }
414
415 /** Link program
416 *
417 * @param gl GL functions
418 * @param id Id of program
419 **/
Link(const glw::Functions & gl,glw::GLuint id)420 void Program::Link(const glw::Functions &gl, glw::GLuint id)
421 {
422 GLint status = GL_FALSE;
423
424 gl.linkProgram(id);
425 GLU_EXPECT_NO_ERROR(gl.getError(), "LinkProgram");
426
427 /* Get link status */
428 gl.getProgramiv(id, GL_LINK_STATUS, &status);
429 GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv");
430
431 /* Log link error */
432 if (GL_TRUE != status)
433 {
434 glw::GLint length = 0;
435 std::string message;
436
437 /* Get error log length */
438 gl.getProgramiv(id, GL_INFO_LOG_LENGTH, &length);
439 GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv");
440
441 message.resize(length, 0);
442
443 /* Get error log */
444 gl.getProgramInfoLog(id, length, 0, &message[0]);
445 GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramInfoLog");
446
447 TCU_FAIL(message.c_str());
448 }
449 }
450
451 /** Use program
452 *
453 * @param gl GL functions
454 * @param id Id of program
455 **/
Use(const glw::Functions & gl,glw::GLuint id)456 void Program::Use(const glw::Functions &gl, glw::GLuint id)
457 {
458 gl.useProgram(id);
459 GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram");
460 }
461
462 /* Shader's constants */
463 const GLuint Shader::m_invalid_id = 0;
464
465 /** Constructor.
466 *
467 * @param context CTS context.
468 **/
Shader(const glw::Functions & gl)469 Shader::Shader(const glw::Functions &gl) : m_id(m_invalid_id), m_gl(gl)
470 {
471 /* Nothing to be done here */
472 }
473
474 /** Destructor
475 *
476 **/
~Shader()477 Shader::~Shader()
478 {
479 Release();
480 }
481
482 /** Initialize shader instance
483 *
484 * @param stage Shader stage
485 * @param source Source code
486 **/
Init(glw::GLenum stage,const std::string & source)487 void Shader::Init(glw::GLenum stage, const std::string &source)
488 {
489 if (true == source.empty())
490 {
491 /* No source == no shader */
492 return;
493 }
494
495 /* Delete any previous shader */
496 Release();
497
498 Create(m_gl, stage, m_id);
499 Source(m_gl, m_id, source);
500
501 Compile(m_gl, m_id);
502 }
503
504 /** Release shader instance
505 *
506 **/
Release()507 void Shader::Release()
508 {
509 if (m_invalid_id != m_id)
510 {
511 m_gl.deleteShader(m_id);
512 m_id = m_invalid_id;
513 }
514 }
515
516 /** Compile shader
517 *
518 * @param gl GL functions
519 * @param id Shader id
520 **/
Compile(const glw::Functions & gl,glw::GLuint id)521 void Shader::Compile(const glw::Functions &gl, glw::GLuint id)
522 {
523 GLint status = GL_FALSE;
524
525 /* Compile */
526 gl.compileShader(id);
527 GLU_EXPECT_NO_ERROR(gl.getError(), "CompileShader");
528
529 /* Get compilation status */
530 gl.getShaderiv(id, GL_COMPILE_STATUS, &status);
531 GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv");
532
533 /* Log compilation error */
534 if (GL_TRUE != status)
535 {
536 glw::GLint length = 0;
537 std::string message;
538
539 /* Error log length */
540 gl.getShaderiv(id, GL_INFO_LOG_LENGTH, &length);
541 GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv");
542
543 /* Prepare storage */
544 message.resize(length, 0);
545
546 /* Get error log */
547 gl.getShaderInfoLog(id, length, 0, &message[0]);
548 GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderInfoLog");
549
550 TCU_FAIL(message.c_str());
551 }
552 }
553
554 /** Create shader
555 *
556 * @param gl GL functions
557 * @param stage Shader stage
558 * @param out_id Shader id
559 **/
Create(const glw::Functions & gl,glw::GLenum stage,glw::GLuint & out_id)560 void Shader::Create(const glw::Functions &gl, glw::GLenum stage, glw::GLuint &out_id)
561 {
562 const GLuint id = gl.createShader(stage);
563 GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader");
564
565 if (m_invalid_id == id)
566 {
567 TCU_FAIL("Failed to create shader");
568 }
569
570 out_id = id;
571 }
572
573 /** Set shader's source code
574 *
575 * @param gl GL functions
576 * @param id Shader id
577 * @param source Shader source code
578 **/
Source(const glw::Functions & gl,glw::GLuint id,const std::string & source)579 void Shader::Source(const glw::Functions &gl, glw::GLuint id, const std::string &source)
580 {
581 const GLchar *code = source.c_str();
582
583 gl.shaderSource(id, 1 /* count */, &code, 0 /* lengths */);
584 GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderSource");
585 }
586
587 /* Texture static fields */
588 const GLuint Texture::m_invalid_id = -1;
589
590 /** Constructor.
591 *
592 * @param context CTS context.
593 **/
Texture(const glw::Functions & gl)594 Texture::Texture(const glw::Functions &gl) : m_id(m_invalid_id), m_gl(gl)
595 {
596 /* Nothing to done here */
597 }
598
599 /** Destructor
600 *
601 **/
~Texture()602 Texture::~Texture()
603 {
604 Release();
605 }
606
607 /** Release texture instance
608 *
609 **/
Release()610 void Texture::Release()
611 {
612 if (m_invalid_id != m_id)
613 {
614 m_gl.deleteTextures(1, &m_id);
615 m_id = m_invalid_id;
616 }
617 }
618
619 /** Bind texture to target
620 *
621 * @param gl GL functions
622 * @param id Id of texture
623 * @param tex_type Type of texture
624 **/
Bind(const glw::Functions & gl,glw::GLuint id,glw::GLenum target)625 void Texture::Bind(const glw::Functions &gl, glw::GLuint id, glw::GLenum target)
626 {
627 gl.bindTexture(target, id);
628 GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture");
629 }
630
631 /** Set contents of compressed texture
632 *
633 * @param gl GL functions
634 * @param target Texture target
635 * @param level Mipmap level
636 * @param internal_format Format of data
637 * @param width Width of texture
638 * @param height Height of texture
639 * @param depth Depth of texture
640 * @param image_size Size of data
641 * @param data Buffer with image data
642 **/
CompressedImage(const glw::Functions & gl,glw::GLenum target,glw::GLint level,glw::GLenum internal_format,glw::GLuint width,glw::GLuint height,glw::GLuint depth,glw::GLsizei image_size,const glw::GLvoid * data)643 void Texture::CompressedImage(const glw::Functions &gl, glw::GLenum target, glw::GLint level,
644 glw::GLenum internal_format, glw::GLuint width, glw::GLuint height, glw::GLuint depth,
645 glw::GLsizei image_size, const glw::GLvoid *data)
646 {
647 switch (target)
648 {
649 case GL_TEXTURE_1D:
650 gl.compressedTexImage1D(target, level, internal_format, width, 0 /* border */, image_size, data);
651 GLU_EXPECT_NO_ERROR(gl.getError(), "CompressedTexImage1D");
652 break;
653 case GL_TEXTURE_1D_ARRAY:
654 case GL_TEXTURE_2D:
655 case GL_TEXTURE_RECTANGLE:
656 gl.compressedTexImage2D(target, level, internal_format, width, height, 0 /* border */, image_size, data);
657 GLU_EXPECT_NO_ERROR(gl.getError(), "CompressedTexImage2D");
658 break;
659 case GL_TEXTURE_CUBE_MAP:
660 gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, internal_format, width, height, 0 /* border */,
661 image_size, data);
662 gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, internal_format, width, height, 0 /* border */,
663 image_size, data);
664 gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, internal_format, width, height, 0 /* border */,
665 image_size, data);
666 gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, internal_format, width, height, 0 /* border */,
667 image_size, data);
668 gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, internal_format, width, height, 0 /* border */,
669 image_size, data);
670 gl.compressedTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, internal_format, width, height, 0 /* border */,
671 image_size, data);
672 GLU_EXPECT_NO_ERROR(gl.getError(), "CompressedTexImage2D");
673 break;
674 case GL_TEXTURE_3D:
675 case GL_TEXTURE_2D_ARRAY:
676 gl.compressedTexImage3D(target, level, internal_format, width, height, depth, 0 /* border */, image_size, data);
677 GLU_EXPECT_NO_ERROR(gl.getError(), "CompressedTexImage3D");
678 break;
679 default:
680 TCU_FAIL("Invliad enum");
681 }
682 }
683
684 /** Generate texture instance
685 *
686 * @param gl GL functions
687 * @param out_id Id of texture
688 **/
Generate(const glw::Functions & gl,glw::GLuint & out_id)689 void Texture::Generate(const glw::Functions &gl, glw::GLuint &out_id)
690 {
691 GLuint id = m_invalid_id;
692
693 gl.genTextures(1, &id);
694 GLU_EXPECT_NO_ERROR(gl.getError(), "GenTextures");
695
696 if (m_invalid_id == id)
697 {
698 TCU_FAIL("Invalid id");
699 }
700
701 out_id = id;
702 }
703
704 /** Get texture data
705 *
706 * @param gl GL functions
707 * @param target Texture target
708 * @param format Format of data
709 * @param type Type of data
710 * @param out_data Buffer for data
711 **/
GetData(const glw::Functions & gl,glw::GLint level,glw::GLenum target,glw::GLenum format,glw::GLenum type,glw::GLvoid * out_data)712 void Texture::GetData(const glw::Functions &gl, glw::GLint level, glw::GLenum target, glw::GLenum format,
713 glw::GLenum type, glw::GLvoid *out_data)
714 {
715 gl.getTexImage(target, level, format, type, out_data);
716 GLU_EXPECT_NO_ERROR(gl.getError(), "GetTexImage");
717 }
718
719 /** Get texture data
720 *
721 * @param gl GL functions
722 * @param id Texture id
723 * @param level Mipmap level
724 * @param width Texture width
725 * @param height Texture height
726 * @param format Format of data
727 * @param type Type of data
728 * @param out_data Buffer for data
729 **/
GetData(const glw::Functions & gl,glw::GLuint id,glw::GLint level,glw::GLuint width,glw::GLuint height,glw::GLenum format,glw::GLenum type,glw::GLvoid * out_data)730 void Texture::GetData(const glw::Functions &gl, glw::GLuint id, glw::GLint level, glw::GLuint width, glw::GLuint height,
731 glw::GLenum format, glw::GLenum type, glw::GLvoid *out_data)
732 {
733 GLuint fbo;
734 gl.genFramebuffers(1, &fbo);
735 GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers");
736 gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
737 GLU_EXPECT_NO_ERROR(gl.getError(), "BindFramebuffer");
738 gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, level);
739 GLU_EXPECT_NO_ERROR(gl.getError(), "FramebufferTexture2D");
740
741 gl.readPixels(0, 0, width, height, format, type, out_data);
742 GLU_EXPECT_NO_ERROR(gl.getError(), "ReadPixels");
743
744 gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
745 }
746
747 /** Generate texture instance
748 *
749 * @param gl GL functions
750 * @param target Texture target
751 * @param level Mipmap level
752 * @param pname Parameter to query
753 * @param param Result of query
754 **/
GetLevelParameter(const glw::Functions & gl,glw::GLenum target,glw::GLint level,glw::GLenum pname,glw::GLint * param)755 void Texture::GetLevelParameter(const glw::Functions &gl, glw::GLenum target, glw::GLint level, glw::GLenum pname,
756 glw::GLint *param)
757 {
758 gl.getTexLevelParameteriv(target, level, pname, param);
759 GLU_EXPECT_NO_ERROR(gl.getError(), "GetTexLevelParameteriv");
760 }
761
762 /** Set contents of texture
763 *
764 * @param gl GL functions
765 * @param target Texture target
766 * @param level Mipmap level
767 * @param internal_format Format of data
768 * @param width Width of texture
769 * @param height Height of texture
770 * @param depth Depth of texture
771 * @param format Format of data
772 * @param type Type of data
773 * @param data Buffer with image data
774 **/
Image(const glw::Functions & gl,glw::GLenum target,glw::GLint level,glw::GLenum internal_format,glw::GLuint width,glw::GLuint height,glw::GLuint depth,glw::GLenum format,glw::GLenum type,const glw::GLvoid * data)775 void Texture::Image(const glw::Functions &gl, glw::GLenum target, glw::GLint level, glw::GLenum internal_format,
776 glw::GLuint width, glw::GLuint height, glw::GLuint depth, glw::GLenum format, glw::GLenum type,
777 const glw::GLvoid *data)
778 {
779 switch (target)
780 {
781 case GL_TEXTURE_1D:
782 gl.texImage1D(target, level, internal_format, width, 0 /* border */, format, type, data);
783 GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage1D");
784 break;
785 case GL_TEXTURE_1D_ARRAY:
786 case GL_TEXTURE_2D:
787 case GL_TEXTURE_RECTANGLE:
788 gl.texImage2D(target, level, internal_format, width, height, 0 /* border */, format, type, data);
789 GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage2D");
790 break;
791 case GL_TEXTURE_CUBE_MAP:
792 gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, internal_format, width, height, 0 /* border */, format,
793 type, data);
794 gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, internal_format, width, height, 0 /* border */, format,
795 type, data);
796 gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, internal_format, width, height, 0 /* border */, format,
797 type, data);
798 gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, internal_format, width, height, 0 /* border */, format,
799 type, data);
800 gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, internal_format, width, height, 0 /* border */, format,
801 type, data);
802 gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, internal_format, width, height, 0 /* border */, format,
803 type, data);
804 GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage2D");
805 break;
806 case GL_TEXTURE_3D:
807 case GL_TEXTURE_2D_ARRAY:
808 gl.texImage3D(target, level, internal_format, width, height, depth, 0 /* border */, format, type, data);
809 GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage3D");
810 break;
811 default:
812 TCU_FAIL("Invliad enum");
813 }
814 }
815
816 /** Allocate storage for texture
817 *
818 * @param gl GL functions
819 * @param target Texture target
820 * @param levels Number of levels
821 * @param internal_format Internal format of texture
822 * @param width Width of texture
823 * @param height Height of texture
824 * @param depth Depth of texture
825 **/
Storage(const glw::Functions & gl,glw::GLenum target,glw::GLsizei levels,glw::GLenum internal_format,glw::GLuint width,glw::GLuint height,glw::GLuint depth)826 void Texture::Storage(const glw::Functions &gl, glw::GLenum target, glw::GLsizei levels, glw::GLenum internal_format,
827 glw::GLuint width, glw::GLuint height, glw::GLuint depth)
828 {
829 switch (target)
830 {
831 case GL_TEXTURE_1D:
832 gl.texStorage1D(target, levels, internal_format, width);
833 GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage1D");
834 break;
835 case GL_TEXTURE_1D_ARRAY:
836 case GL_TEXTURE_2D:
837 case GL_TEXTURE_RECTANGLE:
838 case GL_TEXTURE_CUBE_MAP:
839 gl.texStorage2D(target, levels, internal_format, width, height);
840 GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage2D");
841 break;
842 case GL_TEXTURE_2D_MULTISAMPLE:
843 gl.texStorage2DMultisample(target, levels, internal_format, width, height, GL_FALSE);
844 GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage2DMultisample");
845 break;
846 case GL_TEXTURE_3D:
847 case GL_TEXTURE_2D_ARRAY:
848 gl.texStorage3D(target, levels, internal_format, width, height, depth);
849 GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage3D");
850 break;
851 default:
852 TCU_FAIL("Invliad enum");
853 }
854 }
855
856 /** Set contents of texture
857 *
858 * @param gl GL functions
859 * @param target Texture target
860 * @param level Mipmap level
861 * @param x X offset
862 * @param y Y offset
863 * @param z Z offset
864 * @param width Width of texture
865 * @param height Height of texture
866 * @param depth Depth of texture
867 * @param format Format of data
868 * @param type Type of data
869 * @param pixels Buffer with image data
870 **/
SubImage(const glw::Functions & gl,glw::GLenum target,glw::GLint level,glw::GLint x,glw::GLint y,glw::GLint z,glw::GLsizei width,glw::GLsizei height,glw::GLsizei depth,glw::GLenum format,glw::GLenum type,const glw::GLvoid * pixels)871 void Texture::SubImage(const glw::Functions &gl, glw::GLenum target, glw::GLint level, glw::GLint x, glw::GLint y,
872 glw::GLint z, glw::GLsizei width, glw::GLsizei height, glw::GLsizei depth, glw::GLenum format,
873 glw::GLenum type, const glw::GLvoid *pixels)
874 {
875 switch (target)
876 {
877 case GL_TEXTURE_1D:
878 gl.texSubImage1D(target, level, x, width, format, type, pixels);
879 GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage1D");
880 break;
881 case GL_TEXTURE_1D_ARRAY:
882 case GL_TEXTURE_2D:
883 case GL_TEXTURE_RECTANGLE:
884 gl.texSubImage2D(target, level, x, y, width, height, format, type, pixels);
885 GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage2D");
886 break;
887 case GL_TEXTURE_CUBE_MAP:
888 gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, x, y, width, height, format, type, pixels);
889 gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, x, y, width, height, format, type, pixels);
890 gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, x, y, width, height, format, type, pixels);
891 gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, x, y, width, height, format, type, pixels);
892 gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, x, y, width, height, format, type, pixels);
893 gl.texSubImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, x, y, width, height, format, type, pixels);
894 GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage2D");
895 break;
896 case GL_TEXTURE_3D:
897 case GL_TEXTURE_2D_ARRAY:
898 gl.texSubImage3D(target, level, x, y, z, width, height, depth, format, type, pixels);
899 GLU_EXPECT_NO_ERROR(gl.getError(), "TexSubImage3D");
900 break;
901 default:
902 TCU_FAIL("Invliad enum");
903 }
904 }
905
906 /* VertexArray constants */
907 const GLuint VertexArray::m_invalid_id = -1;
908
909 /** Constructor.
910 *
911 * @param context CTS context.
912 **/
VertexArray(const glw::Functions & gl)913 VertexArray::VertexArray(const glw::Functions &gl) : m_id(m_invalid_id), m_gl(gl)
914 {
915 }
916
917 /** Destructor
918 *
919 **/
~VertexArray()920 VertexArray::~VertexArray()
921 {
922 Release();
923 }
924
925 /** Release vertex array object instance
926 *
927 **/
Release()928 void VertexArray::Release()
929 {
930 if (m_invalid_id != m_id)
931 {
932 Bind(m_gl, 0);
933
934 m_gl.deleteVertexArrays(1, &m_id);
935
936 m_id = m_invalid_id;
937 }
938 }
939
940 /** Binds Vertex array object
941 *
942 * @param gl GL functions
943 * @param id ID of vertex array object
944 **/
Bind(const glw::Functions & gl,glw::GLuint id)945 void VertexArray::Bind(const glw::Functions &gl, glw::GLuint id)
946 {
947 gl.bindVertexArray(id);
948 GLU_EXPECT_NO_ERROR(gl.getError(), "BindVertexArray");
949 }
950
951 /** Generates Vertex array object
952 *
953 * @param gl GL functions
954 * @param out_id ID of vertex array object
955 **/
Generate(const glw::Functions & gl,glw::GLuint & out_id)956 void VertexArray::Generate(const glw::Functions &gl, glw::GLuint &out_id)
957 {
958 GLuint id = m_invalid_id;
959
960 gl.genVertexArrays(1, &id);
961 GLU_EXPECT_NO_ERROR(gl.getError(), "GenVertexArrays");
962
963 if (m_invalid_id == id)
964 {
965 TCU_FAIL("Invalid id");
966 }
967
968 out_id = id;
969 }
970
971 template <typename TYPE>
initPixels(std::vector<TYPE> & pixels,GLuint n_pixels,GLuint n_channels)972 void initPixels(std::vector<TYPE> &pixels, GLuint n_pixels, GLuint n_channels)
973 {
974 if (n_channels == 1)
975 {
976 for (GLuint i = 0; i < n_pixels; ++i)
977 pixels[i] = static_cast<TYPE>(i);
978 }
979 else if (n_channels == 2)
980 {
981 for (GLuint i = 0; i < n_pixels; ++i)
982 {
983 GLuint idx = i * 2;
984 pixels[idx] = static_cast<TYPE>(i);
985 pixels[idx + 1] = pixels[idx];
986 }
987 }
988 else if (n_channels == 4)
989 {
990 for (GLuint i = 0; i < n_pixels; ++i)
991 {
992 GLuint idx = i * 4;
993 pixels[idx] = static_cast<TYPE>(i);
994 pixels[idx + 1] = pixels[idx];
995 pixels[idx + 2] = pixels[idx];
996 pixels[idx + 3] = pixels[idx];
997 }
998 }
999 else
1000 TCU_FAIL("Unsuported number of channels");
1001 }
1002
RobustnessBase(tcu::TestContext & testCtx,const char * name,const char * description,glu::ApiType apiType)1003 RobustnessBase::RobustnessBase(tcu::TestContext &testCtx, const char *name, const char *description,
1004 glu::ApiType apiType)
1005 : tcu::TestCase(testCtx, name, description)
1006 , m_api_type(apiType)
1007 , m_context_is_es(false)
1008 , m_has_khr_robust_buffer_access(false)
1009 {
1010 }
1011
createRobustContext(glu::ResetNotificationStrategy reset)1012 glu::RenderContext *RobustnessBase::createRobustContext(glu::ResetNotificationStrategy reset)
1013 {
1014 // Create test context to verify if required extensions are available
1015 {
1016 deqp::Context context(m_testCtx, glu::ContextType(m_api_type));
1017 const glu::ContextInfo &contextInfo = context.getContextInfo();
1018 glu::ContextType context_type = context.getRenderContext().getType();
1019 if (!contextInfo.isExtensionSupported("GL_KHR_robustness") &&
1020 !contextSupports(context_type, glu::ApiType::es(3, 2)))
1021 {
1022 m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "GL_KHR_robustness extension not supported");
1023 return NULL;
1024 }
1025
1026 m_has_khr_robust_buffer_access = contextInfo.isExtensionSupported("GL_KHR_robust_buffer_access_behavior") ||
1027 contextInfo.isExtensionSupported("GL_ARB_robust_buffer_access_behavior") ||
1028 contextSupports(context_type, glu::ApiType::core(4, 5));
1029 if (!m_has_khr_robust_buffer_access && !contextSupports(context_type, glu::ApiType::core(4, 3)))
1030 {
1031 m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED,
1032 "robust_buffer_access_behavior extension not supported");
1033 return NULL;
1034 }
1035
1036 glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(context_type);
1037 m_specializationMap["VERSION"] = glu::getGLSLVersionDeclaration(glslVersion);
1038 m_context_is_es = glu::isContextTypeES(context_type);
1039 }
1040
1041 glu::RenderConfig renderCfg(glu::ContextType(m_api_type, glu::CONTEXT_ROBUST));
1042 const tcu::CommandLine &commandLine = m_testCtx.getCommandLine();
1043 glu::parseRenderConfig(&renderCfg, commandLine);
1044
1045 if (commandLine.getSurfaceType() == tcu::SURFACETYPE_WINDOW)
1046 renderCfg.resetNotificationStrategy = reset;
1047 else
1048 throw tcu::NotSupportedError("Test not supported in non-windowed context");
1049
1050 /* Try to create core/es robusness context */
1051 return createRenderContext(m_testCtx.getPlatform(), commandLine, renderCfg);
1052 }
1053
1054 /** Constructor
1055 *
1056 * @param testCtx Test context
1057 **/
VertexBufferObjectsTest(tcu::TestContext & testCtx,glu::ApiType apiType)1058 VertexBufferObjectsTest::VertexBufferObjectsTest(tcu::TestContext &testCtx, glu::ApiType apiType)
1059 : RobustnessBase(testCtx, "vertex_buffer_objects", "Verifies that out-of-bound reads from VB result in zero",
1060 apiType)
1061 {
1062 /* Nothing to be done */
1063 }
1064
1065 /** Execute test
1066 *
1067 * @return tcu::TestNode::STOP
1068 **/
iterate()1069 tcu::TestNode::IterateResult VertexBufferObjectsTest::iterate()
1070 {
1071 de::SharedPtr<glu::RenderContext> robustContext(createRobustContext());
1072 if (!robustContext.get())
1073 return STOP;
1074
1075 static const GLuint invalid_elements[] = {
1076 9, 1, 12, 10, 2, 3, 11, 3, 4, 12, 4, 5, 13, 5, 6, 14, 6, 7, 15, 7, 8, 16, 8, 1,
1077 };
1078
1079 static const GLuint valid_elements[] = {
1080 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 6, 0, 6, 7, 0, 7, 8, 0, 8, 1,
1081 };
1082
1083 static const GLfloat vertices[] = {
1084 0.0f, 0.0f, 0.0f, /* 0 */
1085 -1.0f, 0.0f, 0.0f, /* 1 */
1086 -1.0f, 1.0f, 0.0f, /* 2 */
1087 0.0f, 1.0f, 0.0f, /* 3 */
1088 1.0f, 1.0f, 0.0f, /* 4 */
1089 1.0f, 0.0f, 0.0f, /* 5 */
1090 1.0f, -1.0f, 0.0f, /* 6 */
1091 0.0f, -1.0f, 0.0f, /* 7 */
1092 -1.0f, -1.0f, 0.0f, /* 8 */
1093 };
1094
1095 static const GLuint height = 8;
1096 static const GLuint n_vertices = 24;
1097 static const GLuint width = 8;
1098
1099 /* GL entry points */
1100 const Functions &gl = robustContext->getFunctions();
1101
1102 /* Test case objects */
1103 Framebuffer framebuffer(gl);
1104 Program program(gl);
1105 Texture texture(gl);
1106 Buffer elements_buffer(gl);
1107 Buffer vertices_buffer(gl);
1108 VertexArray vao(gl);
1109
1110 /* Vertex array */
1111 VertexArray::Generate(gl, vao.m_id);
1112 VertexArray::Bind(gl, vao.m_id);
1113
1114 /* Buffers initialization */
1115 elements_buffer.InitData(GL_ELEMENT_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(valid_elements), valid_elements);
1116 vertices_buffer.InitData(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(vertices), vertices);
1117
1118 /* Texture initialization */
1119 Texture::Generate(gl, texture.m_id);
1120 Texture::Bind(gl, texture.m_id, GL_TEXTURE_2D);
1121 Texture::Storage(gl, GL_TEXTURE_2D, 1, GL_R8UI, width, height, 0);
1122 Texture::Bind(gl, 0, GL_TEXTURE_2D);
1123
1124 /* Framebuffer initialization*/
1125 Framebuffer::Generate(gl, framebuffer.m_id);
1126 Framebuffer::Bind(gl, GL_DRAW_FRAMEBUFFER, framebuffer.m_id);
1127 Framebuffer::AttachTexture(gl, GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.m_id, 0 /* level */, width,
1128 height);
1129
1130 /* Shaders initialization */
1131 program.Init("" /* cs */, getFragmentShader(), "" /* gs */, "" /* tcs */, "" /* tes */, getVertexShader());
1132 Program::Use(gl, program.m_id);
1133
1134 /* Vertex buffer initialization */
1135 vertices_buffer.Bind();
1136 gl.bindVertexBuffer(0 /* bindindex = location */, vertices_buffer.m_id, 0 /* offset */, 12 /* stride */);
1137 gl.vertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, NULL);
1138 gl.enableVertexAttribArray(0 /* location */);
1139
1140 /* Binding elements/indices buffer */
1141 elements_buffer.Bind();
1142
1143 cleanTexture(gl, texture.m_id);
1144
1145 gl.drawElements(GL_TRIANGLES, n_vertices, GL_UNSIGNED_INT, 0 /* indices */);
1146 GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements");
1147
1148 if (false == verifyValidResults(gl, texture.m_id))
1149 {
1150 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid result for valid input" << tcu::TestLog::EndMessage;
1151
1152 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
1153 return tcu::TestNode::STOP;
1154 }
1155
1156 /* Generate invalid data sets */
1157 const GLuint invalid_elements_offsets[] = {
1158 0, // close fetch
1159 4 * 1024, // near fetch (4K of the end of the object)
1160 1024 * 1024, // medium fetch (1MB past the end of the object)
1161 10 * 1024 * 1024 // high fetch (10MB beyond the end of the object)
1162 };
1163 const GLuint invalid_buffers_count = DE_LENGTH_OF_ARRAY(invalid_elements_offsets);
1164 const GLuint item_count = DE_LENGTH_OF_ARRAY(invalid_elements);
1165 GLuint invalid_elements_set[invalid_buffers_count][item_count];
1166 for (GLuint buffer_index = 0; buffer_index < invalid_buffers_count; ++buffer_index)
1167 {
1168 for (GLuint item_index = 0; item_index < item_count; ++item_index)
1169 invalid_elements_set[buffer_index][item_index] =
1170 invalid_elements[item_index] + invalid_elements_offsets[buffer_index];
1171 }
1172
1173 for (GLuint buffer_index = 0; buffer_index < invalid_buffers_count; ++buffer_index)
1174 {
1175 /* Create elements/indices buffer */
1176 elements_buffer.InitData(GL_ELEMENT_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(invalid_elements_set[buffer_index]),
1177 invalid_elements_set[buffer_index]);
1178 elements_buffer.Bind();
1179
1180 cleanTexture(gl, texture.m_id);
1181
1182 gl.drawElements(GL_TRIANGLES, n_vertices, GL_UNSIGNED_INT, 0 /* indices */);
1183 GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements");
1184
1185 if (false == verifyInvalidResults(gl, texture.m_id))
1186 {
1187 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid result for invalid input"
1188 << tcu::TestLog::EndMessage;
1189 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
1190 return tcu::TestNode::STOP;
1191 }
1192 }
1193
1194 /* Done */
1195 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1196 return tcu::TestNode::STOP;
1197 }
1198
1199 /** Prepare shader for current test case
1200 *
1201 * @return Source
1202 **/
getFragmentShader()1203 std::string VertexBufferObjectsTest::getFragmentShader()
1204 {
1205 const char *source = "${VERSION}\n"
1206 "layout (location = 0) out lowp uvec4 out_fs_color;\n"
1207 "void main()\n"
1208 "{\n"
1209 " out_fs_color = uvec4(1, 255, 255, 255);\n"
1210 "}\n";
1211 return tcu::StringTemplate(source).specialize(m_specializationMap);
1212 }
1213
1214 /** Prepare shader for current test case
1215 *
1216 * @return Source
1217 **/
getVertexShader()1218 std::string VertexBufferObjectsTest::getVertexShader()
1219 {
1220 const char *source = "${VERSION}\n"
1221 "layout (location = 0) in vec4 in_vs_position;\n"
1222 "void main()\n"
1223 "{\n"
1224 " gl_Position = in_vs_position;\n"
1225 "}\n";
1226
1227 return tcu::StringTemplate(source).specialize(m_specializationMap);
1228 }
1229
1230 /** Fill texture with value 128
1231 *
1232 * @param texture_id Id of texture
1233 **/
cleanTexture(const Functions & gl,glw::GLuint texture_id)1234 void VertexBufferObjectsTest::cleanTexture(const Functions &gl, glw::GLuint texture_id)
1235 {
1236 static const GLuint height = 8;
1237 static const GLuint width = 8;
1238
1239 GLubyte pixels[width * height];
1240 for (GLuint i = 0; i < width * height; ++i)
1241 {
1242 pixels[i] = 128;
1243 }
1244
1245 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
1246
1247 Texture::SubImage(gl, GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, width, height, 0 /* depth */,
1248 GL_RED_INTEGER, GL_UNSIGNED_BYTE, pixels);
1249
1250 /* Unbind */
1251 Texture::Bind(gl, 0, GL_TEXTURE_2D);
1252 }
1253
1254 /** Verifies that texutre is not filled with 1
1255 *
1256 * @param texture_id Id of texture
1257 *
1258 * @return false when image is filled with 1, true otherwise
1259 **/
verifyInvalidResults(const Functions & gl,glw::GLuint texture_id)1260 bool VertexBufferObjectsTest::verifyInvalidResults(const Functions &gl, glw::GLuint texture_id)
1261 {
1262 // In OpenGL ES there is undefined out-of-bound behavior - no verification
1263 if (m_context_is_es)
1264 return true;
1265 return !verifyResults(gl, texture_id);
1266 }
1267
1268 /** Verifies that texutre is filled with 1
1269 *
1270 * @param texture_id Id of texture
1271 *
1272 * @return true when image is filled with 1, false otherwise
1273 **/
verifyValidResults(const Functions & gl,glw::GLuint texture_id)1274 bool VertexBufferObjectsTest::verifyValidResults(const Functions &gl, glw::GLuint texture_id)
1275 {
1276 return verifyResults(gl, texture_id);
1277 }
1278
1279 /** Verifies that texutre is filled with 1
1280 *
1281 * @param texture_id Id of texture
1282 *
1283 * @return true when image is filled with 1, false otherwise
1284 **/
verifyResults(const Functions & gl,glw::GLuint texture_id)1285 bool VertexBufferObjectsTest::verifyResults(const Functions &gl, glw::GLuint texture_id)
1286 {
1287 static const GLuint height = 8;
1288 static const GLuint width = 8;
1289 GLuint pixel_size = 4 * sizeof(GLuint);
1290 GLuint expected_value = 1;
1291
1292 std::vector<GLubyte> pixels(width * height * pixel_size, 0);
1293 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
1294 Texture::GetData(gl, texture_id, 0 /* level */, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
1295 Texture::Bind(gl, 0, GL_TEXTURE_2D);
1296
1297 /* Verify */
1298 for (GLuint i = 0; i < pixels.size(); i += pixel_size)
1299 {
1300 if (expected_value != pixels[i])
1301 return false;
1302 }
1303
1304 return true;
1305 }
1306
1307 /** Constructor
1308 *
1309 * @param testCtx Test context
1310 **/
TexelFetchTest(tcu::TestContext & testCtx,glu::ApiType apiType)1311 TexelFetchTest::TexelFetchTest(tcu::TestContext &testCtx, glu::ApiType apiType)
1312 : RobustnessBase(testCtx, "texel_fetch", "Verifies that out-of-bound fetches from texture result in zero", apiType)
1313 , m_test_case(R8)
1314 {
1315 /* Nothing to be done */
1316 }
1317
1318 /** Constructor
1319 *
1320 * @param testCtx Test context
1321 * @param name Test name
1322 * @param description Test description
1323 * @param apiType Api type
1324 **/
TexelFetchTest(tcu::TestContext & testCtx,const char * name,const char * description,glu::ApiType apiType)1325 TexelFetchTest::TexelFetchTest(tcu::TestContext &testCtx, const char *name, const char *description,
1326 glu::ApiType apiType)
1327 : RobustnessBase(testCtx, name, description, apiType)
1328 , m_test_case(R8)
1329 {
1330 /* Nothing to be done */
1331 }
1332
1333 /** Execute test
1334 *
1335 * @return tcu::TestNode::STOP
1336 **/
iterate()1337 tcu::TestNode::IterateResult TexelFetchTest::iterate()
1338 {
1339 de::SharedPtr<glu::RenderContext> robustContext(createRobustContext());
1340 if (!robustContext.get())
1341 return STOP;
1342
1343 /* Constants */
1344 static const GLuint height = 16;
1345 static const GLuint width = 16;
1346
1347 /* GL entry points */
1348 const Functions &gl = robustContext->getFunctions();
1349
1350 /* Test result indicator */
1351 bool test_result = true;
1352
1353 GLuint invalid_fetch_offsets[] = {
1354 16, // near fetch
1355 512, // medium fetch
1356 1008, // high fetch
1357 };
1358 GLuint fetch_offsets_count = sizeof(invalid_fetch_offsets) / sizeof(GLuint);
1359 glu::ContextType contextType = robustContext->getType();
1360
1361 /* Iterate over all cases */
1362 for (; m_test_case < LAST; m_test_case = (TEST_CASES)((GLuint)m_test_case + 1))
1363 {
1364 GLint level = 0;
1365 GLenum texture_target = GL_TEXTURE_2D;
1366
1367 if (R32UI_MULTISAMPLE == m_test_case || RG8_SNORM == m_test_case)
1368 {
1369 // 1. RG8_SNORM case:
1370 // Skip RG8_SNORM format case.
1371 // RG8_SNORM is not required to be used as a render target
1372 // OpenGL 4.5 Core Spec, Page 197
1373 //
1374 // 2. R32UI_MULTISAMPLE case
1375 // Skip test in multi sample case
1376 // texelFetch with invalid lod plane results undefined value
1377 // OpenGL 4.5 Core Spec, around page 377
1378 m_test_case = (TEST_CASES)((GLuint)m_test_case + 1);
1379 continue;
1380 }
1381
1382 /* */
1383 Texture destination_texture(gl);
1384 Framebuffer framebuffer(gl);
1385 Texture source_texture(gl);
1386 Program program(gl);
1387 VertexArray vao(gl);
1388
1389 /* Prepare VAO */
1390 VertexArray::Generate(gl, vao.m_id);
1391 VertexArray::Bind(gl, vao.m_id);
1392
1393 /* Prepare textures */
1394 Texture::Generate(gl, destination_texture.m_id);
1395 Texture::Generate(gl, source_texture.m_id);
1396
1397 if (R32UI_MULTISAMPLE == m_test_case)
1398 {
1399 GLint max_integer_samples;
1400 gl.getIntegerv(GL_MAX_INTEGER_SAMPLES, &max_integer_samples);
1401 GLint max_image_samples;
1402 gl.getIntegerv(GL_MAX_IMAGE_SAMPLES, &max_image_samples);
1403 if (max_integer_samples < 4 || max_image_samples < 4)
1404 {
1405 /* prepareTexture() hard-codes 4 samples (n_levels) for
1406 * R32UI_MULTISAMPLE case. This value exceeds the required
1407 * min-max value (1 in OpenGL ES 3.2) and is not supported
1408 * by all implementations.
1409 *
1410 * Also, the test uses a compute shader with images
1411 * to upload the texture so max_image_samples >= 4
1412 * is also required.
1413 */
1414 m_testCtx.getLog() << tcu::TestLog::Message << "Test case: " << getTestCaseName() << " not supported"
1415 << tcu::TestLog::EndMessage;
1416
1417 continue;
1418 }
1419 }
1420
1421 prepareTexture(gl, false, destination_texture.m_id);
1422 prepareTexture(gl, true, source_texture.m_id);
1423
1424 /* Select FBO settings */
1425 if (R32UI_MIPMAP == m_test_case)
1426 {
1427 level = 1;
1428 }
1429 else if (R32UI_MULTISAMPLE == m_test_case)
1430 {
1431 texture_target = GL_TEXTURE_2D_MULTISAMPLE;
1432 }
1433
1434 /* Prepare FBO */
1435 Framebuffer::Generate(gl, framebuffer.m_id);
1436 Framebuffer::Bind(gl, GL_DRAW_FRAMEBUFFER, framebuffer.m_id);
1437 Framebuffer::AttachTexture(gl, GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, destination_texture.m_id, level,
1438 width, height);
1439
1440 /* Prepare valid program */
1441 program.Init("" /* cs */, getFragmentShader(contextType, true), getGeometryShader(), "" /* tcs */, "" /* tes */,
1442 getVertexShader());
1443
1444 /* Test valid case */
1445 /* Set program */
1446 Program::Use(gl, program.m_id);
1447
1448 /* Set texture */
1449 gl.activeTexture(GL_TEXTURE0); /* location = 0 */
1450 GLU_EXPECT_NO_ERROR(gl.getError(), "ActiveTexture");
1451 Texture::Bind(gl, source_texture.m_id, texture_target);
1452 gl.uniform1i(0 /* location */, 0 /* texture unit */);
1453 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
1454
1455 /* Check if setup is supported */
1456 GLenum fbo_status = gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER);
1457 GLU_EXPECT_NO_ERROR(gl.getError(), "CheckFramebufferStatus");
1458 if (GL_FRAMEBUFFER_COMPLETE != fbo_status)
1459 {
1460 m_testCtx.getLog() << tcu::TestLog::Message << "Test case: " << getTestCaseName() << " not supported"
1461 << tcu::TestLog::EndMessage;
1462
1463 continue;
1464 }
1465
1466 /* Enable multisampling */
1467 if (R32UI_MULTISAMPLE == m_test_case)
1468 {
1469 gl.enable(GL_MULTISAMPLE);
1470 GLU_EXPECT_NO_ERROR(gl.getError(), "Enable");
1471 }
1472
1473 /* Draw */
1474 gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
1475 {
1476 /* Get error from draw */
1477 GLenum error = gl.getError();
1478
1479 /* Disable multisampling */
1480 if (R32UI_MULTISAMPLE == m_test_case)
1481 {
1482 gl.disable(GL_MULTISAMPLE);
1483 GLU_EXPECT_NO_ERROR(gl.getError(), "Disable");
1484 }
1485
1486 /* Handle error from draw */
1487 GLU_EXPECT_NO_ERROR(error, "DrawArrays");
1488 }
1489
1490 /* Verification */
1491 if (false == verifyValidResults(gl, destination_texture.m_id))
1492 {
1493 test_result = false;
1494 }
1495
1496 /* Test invalid cases */
1497 for (GLuint index = 0; index < fetch_offsets_count; ++index)
1498 {
1499 /* Prepare invalid program */
1500 program.Init("" /* cs */, getFragmentShader(contextType, false, invalid_fetch_offsets[index]),
1501 getGeometryShader(), "" /* tcs */, "" /* tes */, getVertexShader());
1502 Program::Use(gl, program.m_id);
1503 Framebuffer::Bind(gl, GL_DRAW_FRAMEBUFFER, framebuffer.m_id);
1504
1505 /* Set texture */
1506 gl.activeTexture(GL_TEXTURE0); /* location = 0 */
1507 GLU_EXPECT_NO_ERROR(gl.getError(), "ActiveTexture");
1508 Texture::Bind(gl, source_texture.m_id, texture_target);
1509 gl.uniform1i(0 /* location */, 0 /* texture unit */);
1510 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
1511
1512 /* Draw */
1513 gl.clear(GL_COLOR_BUFFER_BIT);
1514 gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
1515 GLU_EXPECT_NO_ERROR(gl.getError(), "DrawArrays");
1516
1517 /* Verification */
1518 if (false == verifyInvalidResults(gl, destination_texture.m_id))
1519 {
1520 test_result = false;
1521 m_testCtx.getLog() << tcu::TestLog::Message << "Test case: " << getTestCaseName() << " failed for "
1522 << invalid_fetch_offsets[index] << " offset" << tcu::TestLog::EndMessage;
1523 }
1524 }
1525 }
1526
1527 /* Set result */
1528 if (true == test_result)
1529 {
1530 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1531 }
1532 else
1533 {
1534 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
1535 }
1536
1537 /* Done */
1538 return tcu::TestNode::STOP;
1539 }
1540
1541 /** Prepares source code for fragment shader
1542 *
1543 * @param is_case_valid Selects if valid or invalid case is tested
1544 *
1545 * @return string with prepared code
1546 **/
getFragmentShader(const glu::ContextType &,bool is_case_valid,GLuint fetch_offset)1547 std::string TexelFetchTest::getFragmentShader(const glu::ContextType &, bool is_case_valid, GLuint fetch_offset)
1548 {
1549 const GLchar *source = "${VERSION}\n"
1550 "in lowp vec2 gs_fs_tex_coord;\n"
1551 "layout (location = 0) out lowp ${TYPE} out_fs_color;\n"
1552 "layout (location = 0) uniform lowp ${SAMPLER} uni_texture;\n"
1553 "\n"
1554 "void main()\n"
1555 "{\n"
1556 " ivec2 point = ivec2(gs_fs_tex_coord * 16.0 + float(${OFFSET}));\n"
1557 " out_fs_color = texelFetch(uni_texture, point, ${PLANE});\n"
1558 "}\n";
1559
1560 m_specializationMap["PLANE"] = "0";
1561 m_specializationMap["SAMPLER"] = "sampler2D";
1562 m_specializationMap["TYPE"] = "vec4";
1563
1564 if (R32UI_MIPMAP == m_test_case)
1565 {
1566 m_specializationMap["PLANE"] = "1";
1567 m_specializationMap["SAMPLER"] = "usampler2D";
1568 m_specializationMap["TYPE"] = "uvec4";
1569
1570 if (false == is_case_valid)
1571 {
1572 fetch_offset = 0;
1573 m_specializationMap["PLANE"] = "2";
1574 }
1575 }
1576 else if (R32UI_MULTISAMPLE == m_test_case)
1577 {
1578 m_specializationMap["PLANE"] = "9";
1579 m_specializationMap["SAMPLER"] = "usampler2DMS";
1580 m_specializationMap["TYPE"] = "uvec4";
1581
1582 if (false == is_case_valid)
1583 {
1584 fetch_offset = 0;
1585 m_specializationMap["PLANE"] = "gl_SampleID";
1586 }
1587 }
1588
1589 std::stringstream offset;
1590 offset << fetch_offset;
1591 m_specializationMap["OFFSET"] = offset.str();
1592
1593 return tcu::StringTemplate(source).specialize(m_specializationMap);
1594 }
1595
1596 /** Prepare shader for current test case
1597 *
1598 * @return Source
1599 **/
getGeometryShader()1600 std::string TexelFetchTest::getGeometryShader()
1601 {
1602 static const GLchar *source = "${VERSION}\n"
1603 "layout(points) in;\n"
1604 "layout(triangle_strip, max_vertices = 4) out;\n"
1605 "\n"
1606 "out vec2 gs_fs_tex_coord;\n"
1607 "\n"
1608 "void main()\n"
1609 "{\n"
1610 " gs_fs_tex_coord = vec2(0, 0);\n"
1611 " gl_Position = vec4(-1, -1, 0, 1);\n"
1612 " EmitVertex();\n"
1613 "\n"
1614 " gs_fs_tex_coord = vec2(0, 1);\n"
1615 " gl_Position = vec4(-1, 1, 0, 1);\n"
1616 " EmitVertex();\n"
1617 "\n"
1618 " gs_fs_tex_coord = vec2(1, 0);\n"
1619 " gl_Position = vec4(1, -1, 0, 1);\n"
1620 " EmitVertex();\n"
1621 "\n"
1622 " gs_fs_tex_coord = vec2(1, 1);\n"
1623 " gl_Position = vec4(1, 1, 0, 1);\n"
1624 " EmitVertex();\n"
1625 "}\n";
1626
1627 return tcu::StringTemplate(source).specialize(m_specializationMap);
1628 }
1629
1630 /** Prepare shader for current test case
1631 *
1632 * @return Source
1633 **/
getVertexShader()1634 std::string TexelFetchTest::getVertexShader()
1635 {
1636 static const GLchar *source = "${VERSION}\n"
1637 "\n"
1638 "void main()\n"
1639 "{\n"
1640 " gl_Position = vec4(0, 0, 0, 1);\n"
1641 "}\n";
1642 return tcu::StringTemplate(source).specialize(m_specializationMap);
1643 }
1644
1645 /** Returns name of current test case
1646 *
1647 * @return Name of test case
1648 **/
getTestCaseName() const1649 const glw::GLchar *TexelFetchTest::getTestCaseName() const
1650 {
1651 const GLchar *name = "";
1652
1653 switch (m_test_case)
1654 {
1655 case R8:
1656 name = "Sampling GL_R8 texture";
1657 break;
1658 case RG8_SNORM:
1659 name = "Sampling GL_RG8_SNORM texture";
1660 break;
1661 case RGBA32F:
1662 name = "Sampling GL_RGBA32F texture";
1663 break;
1664 case R32UI_MIPMAP:
1665 name = "Sampling mipmap of GL_32UI texture";
1666 break;
1667 case R32UI_MULTISAMPLE:
1668 name = "Sampling GL_32UI multisampled texture";
1669 break;
1670 default:
1671 TCU_FAIL("Invalid enum");
1672 }
1673
1674 return name;
1675 }
1676
1677 /** Prepare a texture
1678 *
1679 * @param is_source Selects if texutre will be used as source or destination
1680 * @param texture_id Id of texutre
1681 **/
prepareTexture(const Functions & gl,bool is_source,glw::GLuint texture_id)1682 void TexelFetchTest::prepareTexture(const Functions &gl, bool is_source, glw::GLuint texture_id)
1683 {
1684 /* Image size */
1685 static const GLuint image_height = 16;
1686 static const GLuint image_width = 16;
1687
1688 /* Texture storage parameters */
1689 GLuint height = image_height;
1690 GLenum internal_format = 0;
1691 GLsizei n_levels = 1;
1692 GLenum target = GL_TEXTURE_2D;
1693 GLuint width = image_width;
1694
1695 /* Prepare texture storage parameters */
1696 switch (m_test_case)
1697 {
1698 case R8:
1699 internal_format = GL_R8;
1700 break;
1701 case RG8_SNORM:
1702 internal_format = GL_RG8_SNORM;
1703 break;
1704 case RGBA32F:
1705 internal_format = GL_RGBA32F;
1706 break;
1707 case R32UI_MIPMAP:
1708 height = 2 * image_height;
1709 internal_format = GL_R32UI;
1710 n_levels = 2;
1711 width = 2 * image_width;
1712 break;
1713 case R32UI_MULTISAMPLE:
1714 internal_format = GL_R32UI;
1715 n_levels = 4;
1716 target = GL_TEXTURE_2D_MULTISAMPLE;
1717 break;
1718 default:
1719 TCU_FAIL("Invalid enum");
1720 }
1721
1722 /* Prepare storage */
1723 Texture::Bind(gl, texture_id, target);
1724 Texture::Storage(gl, target, n_levels, internal_format, width, height, 0);
1725
1726 /* Set samplers to NEAREST/NEAREST if required. The results of texelFetch builtins
1727 are undefined if the computed level of detail is not the texture's base level and
1728 the texture's minification filter is NEAREST or LINEAR. */
1729 if (R32UI_MIPMAP == m_test_case)
1730 {
1731 gl.texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
1732 gl.texParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1733 }
1734 else if (R32UI_MULTISAMPLE != m_test_case)
1735 {
1736 gl.texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1737 gl.texParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1738 }
1739
1740 /* Destination image can be left empty */
1741 if (false == is_source)
1742 {
1743 Texture::Bind(gl, 0, target);
1744 return;
1745 }
1746
1747 /* Prepare texture */
1748 if (R8 == m_test_case)
1749 {
1750 GLubyte source_pixels[image_width * image_height];
1751 for (GLuint i = 0; i < image_width * image_height; ++i)
1752 {
1753 source_pixels[i] = static_cast<GLubyte>(i);
1754 }
1755
1756 Texture::SubImage(gl, GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, width, height,
1757 0 /* depth */, GL_RED, GL_UNSIGNED_BYTE, source_pixels);
1758 }
1759 else if (RG8_SNORM == m_test_case)
1760 {
1761 static const GLuint n_components = 2;
1762
1763 GLbyte source_pixels[image_width * image_height * n_components];
1764 for (GLuint i = 0; i < image_width * image_height; ++i)
1765 {
1766 source_pixels[i * n_components + 0] = static_cast<GLubyte>((i % 16) - 8);
1767 source_pixels[i * n_components + 1] = static_cast<GLubyte>((i / 16) - 8);
1768 }
1769
1770 Texture::SubImage(gl, GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, width, height,
1771 0 /* depth */, GL_RG, GL_BYTE, source_pixels);
1772 }
1773 else if (RGBA32F == m_test_case)
1774 {
1775 static const GLuint n_components = 4;
1776
1777 GLfloat source_pixels[image_width * image_height * n_components];
1778 for (GLuint i = 0; i < image_width * image_height; ++i)
1779 {
1780 source_pixels[i * n_components + 0] = (GLfloat)(i % 16) / 16.0f;
1781 source_pixels[i * n_components + 1] = (GLfloat)(i / 16) / 16.0f;
1782 source_pixels[i * n_components + 2] = (GLfloat)i / 256.0f;
1783 source_pixels[i * n_components + 3] = 1.0f;
1784 }
1785
1786 Texture::SubImage(gl, GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, width, height,
1787 0 /* depth */, GL_RGBA, GL_FLOAT, source_pixels);
1788 }
1789 else if (R32UI_MIPMAP == m_test_case)
1790 {
1791 GLuint source_pixels[image_width * image_height];
1792 for (GLuint i = 0; i < image_width * image_height; ++i)
1793 {
1794 source_pixels[i] = i;
1795 }
1796
1797 Texture::SubImage(gl, GL_TEXTURE_2D, 1 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, image_width, image_height,
1798 0 /* depth */, GL_RED_INTEGER, GL_UNSIGNED_INT, source_pixels);
1799 }
1800 else if (R32UI_MULTISAMPLE == m_test_case)
1801 {
1802 /* Compute shader */
1803 static const GLchar *source =
1804 "${VERSION}\n"
1805 "\n"
1806 "layout (local_size_x = ${LOCAL_SIZE}, local_size_y = ${LOCAL_SIZE}, local_size_z = 1) in;\n"
1807 "layout (${QUALIFIERS}) writeonly uniform highp uimage2DMS uni_image;\n"
1808 "\n"
1809 "void main()\n"
1810 "{\n"
1811 " const ivec2 point = ivec2(gl_WorkGroupID.x, gl_WorkGroupID.y);\n"
1812 " const uint index = gl_WorkGroupID.y * 16U + gl_WorkGroupID.x;\n"
1813 "\n"
1814 " imageStore(uni_image, point, 0, uvec4(index + 0U, 0, 0, 0));\n"
1815 " imageStore(uni_image, point, 1, uvec4(index + 1U, 0, 0, 0));\n"
1816 " imageStore(uni_image, point, 2, uvec4(index + 2U, 0, 0, 0));\n"
1817 " imageStore(uni_image, point, 3, uvec4(index + 3U, 0, 0, 0));\n"
1818 "}\n"
1819 "\n";
1820
1821 if (m_context_is_es)
1822 {
1823 m_specializationMap["LOCAL_SIZE"] = "16";
1824 m_specializationMap["QUALIFIERS"] = "binding = 0, r32ui";
1825 }
1826 else
1827 {
1828 m_specializationMap["LOCAL_SIZE"] = "1";
1829 m_specializationMap["QUALIFIERS"] = "location = 0";
1830 }
1831
1832 Program program(gl);
1833 std::string cs = tcu::StringTemplate(source).specialize(m_specializationMap);
1834 program.Init(cs, "", "", "", "", "");
1835 program.Use();
1836
1837 gl.bindImageTexture(0 /* unit */, texture_id, 0 /* level */, GL_FALSE /* layered */, 0 /* layer */,
1838 GL_WRITE_ONLY, GL_R32UI);
1839 GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
1840
1841 if (!m_context_is_es)
1842 {
1843 gl.uniform1i(0 /* location */, 0 /* image unit*/);
1844 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
1845 }
1846
1847 gl.dispatchCompute(16, 16, 1);
1848 GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
1849 }
1850
1851 Texture::Bind(gl, 0, target);
1852 }
1853
1854 /** Verifies that texutre is filled with 0 or with (0, 0, 0, x),
1855 * where x may be 0, 1 or the biggest representable integer value.
1856 *
1857 * @param texture_id Id of texture
1858 *
1859 * @return true when image is filled with 0, 1 or biggest represetable integer number, false otherwise
1860 **/
verifyInvalidResults(const Functions & gl,glw::GLuint texture_id)1861 bool TexelFetchTest::verifyInvalidResults(const Functions &gl, glw::GLuint texture_id)
1862 {
1863 static const GLuint height = 16;
1864 static const GLuint width = 16;
1865 static const GLuint n_pixels = height * width;
1866
1867 // OpenGL ES has undefined out-of-bound behavior - no verification
1868 if (m_context_is_es)
1869 return true;
1870
1871 bool result = true;
1872
1873 if (R8 == m_test_case)
1874 {
1875 static const GLuint n_channels = 4;
1876
1877 std::vector<GLubyte> pixels(n_pixels * n_channels);
1878 initPixels(pixels, n_pixels, n_channels);
1879
1880 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
1881
1882 Texture::GetData(gl, 0 /* level */, GL_TEXTURE_2D, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]);
1883
1884 /* Unbind */
1885 Texture::Bind(gl, 0, GL_TEXTURE_2D);
1886
1887 /* Verify */
1888 for (GLuint i = 0; i < n_pixels; ++i)
1889 {
1890 const GLubyte expected_red = 0;
1891 const GLubyte drawn_red = pixels[i * n_channels];
1892
1893 if (expected_red != drawn_red)
1894 {
1895 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << (GLuint)drawn_red
1896 << ". Expected value: " << (GLuint)expected_red << " at offset: " << i
1897 << tcu::TestLog::EndMessage;
1898
1899 result = false;
1900 break;
1901 }
1902 }
1903 }
1904 else if (RG8_SNORM == m_test_case)
1905 {
1906 static const GLuint n_channels = 4;
1907
1908 std::vector<GLbyte> pixels(n_pixels * n_channels);
1909 initPixels(pixels, n_pixels, n_channels);
1910
1911 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
1912
1913 Texture::GetData(gl, 0 /* level */, GL_TEXTURE_2D, GL_RGBA, GL_BYTE, &pixels[0]);
1914
1915 /* Unbind */
1916 Texture::Bind(gl, 0, GL_TEXTURE_2D);
1917
1918 /* Verify */
1919 for (GLuint i = 0; i < n_pixels; ++i)
1920 {
1921 const GLbyte expected_red = 0;
1922 const GLbyte expected_green = 0;
1923 const GLbyte drawn_red = pixels[i * n_channels + 0];
1924 const GLbyte drawn_green = pixels[i * n_channels + 1];
1925
1926 if ((expected_red != drawn_red) || (expected_green != drawn_green))
1927 {
1928 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << (GLint)drawn_red << ", "
1929 << (GLint)drawn_green << ". Expected value: " << (GLint)expected_red << ", "
1930 << (GLint)expected_green << ". At offset: " << i << tcu::TestLog::EndMessage;
1931
1932 result = false;
1933 break;
1934 }
1935 }
1936 }
1937 else if (RGBA32F == m_test_case)
1938 {
1939 static const GLuint n_channels = 4;
1940
1941 std::vector<GLfloat> pixels(n_pixels * n_channels);
1942 for (GLuint i = 0; i < n_pixels; ++i)
1943 {
1944 const GLuint idx = i * n_channels;
1945 const GLfloat value = static_cast<GLfloat>(i) / n_pixels;
1946 pixels[idx + 0] = value;
1947 pixels[idx + 1] = value;
1948 pixels[idx + 2] = value;
1949 pixels[idx + 3] = value;
1950 }
1951
1952 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
1953
1954 Texture::GetData(gl, 0 /* level */, GL_TEXTURE_2D, GL_RGBA, GL_FLOAT, &pixels[0]);
1955
1956 /* Unbind */
1957 Texture::Bind(gl, 0, GL_TEXTURE_2D);
1958
1959 /* Verify */
1960 for (GLuint i = 0; i < n_pixels; ++i)
1961 {
1962 const GLfloat expected_red = 0.0f;
1963 const GLfloat expected_green = 0.0f;
1964 const GLfloat expected_blue = 0.0f;
1965 const GLfloat expected_alpha_0 =
1966 0.0f; /* OpenGL 4.5 (and ES) specifies two possiblities (0 or 1) for alpha channel (Chapter 11.1.3.12). */
1967 const GLfloat expected_alpha_1 = 1.0f;
1968 const GLfloat drawn_red = pixels[i * n_channels + 0];
1969 const GLfloat drawn_green = pixels[i * n_channels + 1];
1970 const GLfloat drawn_blue = pixels[i * n_channels + 2];
1971 const GLfloat drawn_alpha = pixels[i * n_channels + 3];
1972
1973 const GLfloat precision = 0.0009765625; /* (1.f / 1024.f) */
1974
1975 if ((de::abs(expected_red - drawn_red) > precision) ||
1976 (de::abs(expected_green - drawn_green) > precision) ||
1977 (de::abs(expected_blue - drawn_blue) > precision) ||
1978 ((de::abs(expected_alpha_0 - drawn_alpha) > precision) &&
1979 (de::abs(expected_alpha_1 - drawn_alpha) > precision)))
1980 {
1981 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red << ", " << drawn_green
1982 << ", " << drawn_blue << ", " << drawn_alpha << ". Expected value: " << expected_red
1983 << ", " << expected_green << ", " << expected_blue << ", " << expected_alpha_0
1984 << " or " << expected_alpha_1 << ". At offset: " << i << tcu::TestLog::EndMessage;
1985
1986 result = false;
1987 break;
1988 }
1989 }
1990 }
1991 else if (R32UI_MIPMAP == m_test_case)
1992 {
1993 static const GLuint n_channels = 4;
1994
1995 std::vector<GLuint> pixels(n_pixels * n_channels);
1996 initPixels(pixels, n_pixels, n_channels);
1997
1998 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
1999
2000 Texture::GetData(gl, texture_id, 1 /* level */, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
2001
2002 /* Unbind */
2003 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2004
2005 /* Verify */
2006 for (GLuint i = 0; i < n_pixels; ++i)
2007 {
2008 const GLuint expected_red = 0;
2009 const GLuint drawn_red = pixels[i * n_channels];
2010
2011 if (expected_red != drawn_red)
2012 {
2013 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red
2014 << ". Expected value: " << expected_red << " at offset: " << i
2015 << tcu::TestLog::EndMessage;
2016
2017 result = false;
2018 break;
2019 }
2020 }
2021 }
2022 else if (R32UI_MULTISAMPLE == m_test_case)
2023 {
2024 static const GLuint n_channels = 4;
2025
2026 /* Compute shader */
2027 static const GLchar *source =
2028 "${VERSION}\n"
2029 "\n"
2030 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2031 "\n"
2032 "layout (location = 1) writeonly uniform uimage2D uni_destination_image;\n"
2033 "layout (location = 0, r32ui) readonly uniform uimage2DMS uni_source_image;\n"
2034 "\n"
2035 "void main()\n"
2036 "{\n"
2037 " const ivec2 point = ivec2(gl_WorkGroupID.x, gl_WorkGroupID.y);\n"
2038 "\n"
2039 " const uvec4 color_0 = imageLoad(uni_source_image, point, 0);\n"
2040 " const uvec4 color_1 = imageLoad(uni_source_image, point, 1);\n"
2041 " const uvec4 color_2 = imageLoad(uni_source_image, point, 2);\n"
2042 " const uvec4 color_3 = imageLoad(uni_source_image, point, 3);\n"
2043 "\n"
2044 " if (any(equal(uvec4(color_0.r, color_1.r, color_2.r, color_3.r), uvec4(0))))\n"
2045 " {\n"
2046 " imageStore(uni_destination_image, point, uvec4(1, 1, 1, 1));\n"
2047 " }\n"
2048 " else\n"
2049 " {\n"
2050 " imageStore(uni_destination_image, point, uvec4(0, 0, 0, 0));\n"
2051 " }\n"
2052 "}\n"
2053 "\n";
2054
2055 Program program(gl);
2056 Texture destination_texture(gl);
2057
2058 Texture::Generate(gl, destination_texture.m_id);
2059 Texture::Bind(gl, destination_texture.m_id, GL_TEXTURE_2D);
2060 Texture::Storage(gl, GL_TEXTURE_2D, 1, GL_R32UI, width, height, 0 /* depth */);
2061
2062 std::string cs = tcu::StringTemplate(source).specialize(m_specializationMap);
2063 program.Init(cs, "", "", "", "", "");
2064 program.Use();
2065 gl.bindImageTexture(0 /* unit */, texture_id, 0 /* level */, GL_FALSE /* layered */, 0 /* layer */,
2066 GL_READ_ONLY, GL_R32UI);
2067 GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
2068 gl.bindImageTexture(1 /* unit */, destination_texture.m_id, 0 /* level */, GL_FALSE /* layered */,
2069 0 /* layer */, GL_WRITE_ONLY, GL_R32UI);
2070 GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
2071
2072 gl.uniform1i(0 /* location */, 0 /* image unit*/);
2073 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
2074
2075 gl.uniform1i(1 /* location */, 1 /* image unit*/);
2076 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
2077
2078 gl.dispatchCompute(16, 16, 1);
2079 GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
2080
2081 /* Pixels buffer initialization */
2082 std::vector<GLuint> pixels(n_pixels * n_channels);
2083 initPixels(pixels, n_pixels, n_channels);
2084
2085 Texture::GetData(gl, 0 /* level */, GL_TEXTURE_2D, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
2086
2087 /* Unbind */
2088 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2089
2090 /* Verify */
2091 for (GLuint i = 0; i < n_pixels; ++i)
2092 {
2093 const GLuint expected_red = 1;
2094 const GLuint drawn_red = pixels[i * n_channels];
2095
2096 if (expected_red != drawn_red)
2097 {
2098 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red
2099 << ". Expected value: " << expected_red << " at offset: " << i
2100 << tcu::TestLog::EndMessage;
2101
2102 result = false;
2103 break;
2104 }
2105 }
2106 }
2107
2108 return result;
2109 }
2110
2111 /** Verifies that texutre is filled with increasing values
2112 *
2113 * @param texture_id Id of texture
2114 *
2115 * @return true when image is filled with increasing values, false otherwise
2116 **/
verifyValidResults(const Functions & gl,glw::GLuint texture_id)2117 bool TexelFetchTest::verifyValidResults(const Functions &gl, glw::GLuint texture_id)
2118 {
2119 static const GLuint height = 16;
2120 static const GLuint width = 16;
2121 static const GLuint n_pixels = height * width;
2122
2123 bool result = true;
2124
2125 if (R8 == m_test_case)
2126 {
2127 static const GLuint n_channels = 4;
2128
2129 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
2130
2131 std::vector<GLubyte> pixels(n_pixels * n_channels);
2132 initPixels(pixels, n_pixels, n_channels);
2133
2134 Texture::GetData(gl, texture_id, 0 /* level */, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]);
2135
2136 /* Unbind */
2137 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2138
2139 /* Verify */
2140 for (GLuint i = 0; i < n_pixels; ++i)
2141 {
2142 const GLubyte expected_red = static_cast<GLubyte>(i);
2143 const GLubyte drawn_red = pixels[i * n_channels];
2144
2145 if (expected_red != drawn_red)
2146 {
2147 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << (GLuint)drawn_red
2148 << ". Expected value: " << (GLuint)expected_red << " at offset: " << i
2149 << tcu::TestLog::EndMessage;
2150
2151 result = false;
2152 break;
2153 }
2154 }
2155 }
2156 else if (RG8_SNORM == m_test_case)
2157 {
2158 static const GLuint n_channels = 4;
2159
2160 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
2161
2162 std::vector<GLbyte> pixels(n_pixels * n_channels);
2163 initPixels(pixels, n_pixels, n_channels);
2164
2165 Texture::GetData(gl, texture_id, 0 /* level */, width, height, GL_RGBA, GL_BYTE, &pixels[0]);
2166
2167 /* Unbind */
2168 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2169
2170 /* Verify */
2171 for (GLuint i = 0; i < n_pixels; ++i)
2172 {
2173 const GLbyte expected_red = static_cast<GLubyte>((i % 16) - 8);
2174 const GLbyte expected_green = static_cast<GLubyte>((i / 16) - 8);
2175 const GLbyte drawn_red = pixels[i * n_channels + 0];
2176 const GLbyte drawn_green = pixels[i * n_channels + 1];
2177
2178 if ((expected_red != drawn_red) || (expected_green != drawn_green))
2179 {
2180 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << (GLint)drawn_red << ", "
2181 << (GLint)drawn_green << ". Expected value: " << (GLint)expected_red << ", "
2182 << (GLint)expected_green << ". At offset: " << i << tcu::TestLog::EndMessage;
2183
2184 result = false;
2185 break;
2186 }
2187 }
2188 }
2189 else if (RGBA32F == m_test_case)
2190 {
2191 static const GLuint n_channels = 4;
2192
2193 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
2194
2195 std::vector<GLfloat> pixels(n_pixels * n_channels);
2196 for (GLuint i = 0; i < n_pixels; ++i)
2197 {
2198 const GLuint idx = i * n_channels;
2199 const GLfloat value = static_cast<GLfloat>(i) / n_pixels;
2200 pixels[idx + 0] = value;
2201 pixels[idx + 1] = value;
2202 pixels[idx + 2] = value;
2203 pixels[idx + 3] = value;
2204 }
2205
2206 Texture::GetData(gl, texture_id, 0 /* level */, width, height, GL_RGBA, GL_FLOAT, &pixels[0]);
2207
2208 /* Unbind */
2209 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2210
2211 /* Verify */
2212 for (GLuint i = 0; i < n_pixels; ++i)
2213 {
2214 const GLfloat expected_red = (GLfloat)(i % 16) / 16.0f;
2215 const GLfloat expected_green = (GLfloat)(i / 16) / 16.0f;
2216 const GLfloat expected_blue = (GLfloat)i / 256.0f;
2217 const GLfloat expected_alpha = 1.0f;
2218 const GLuint idx = i * n_channels;
2219 const GLfloat drawn_red = pixels[idx + 0];
2220 const GLfloat drawn_green = pixels[idx + 1];
2221 const GLfloat drawn_blue = pixels[idx + 2];
2222 const GLfloat drawn_alpha = pixels[idx + 3];
2223
2224 if ((expected_red != drawn_red) || (expected_green != drawn_green) || (expected_blue != drawn_blue) ||
2225 (expected_alpha != drawn_alpha))
2226 {
2227 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red << ", " << drawn_green
2228 << ", " << drawn_blue << ", " << drawn_alpha << ". Expected value: " << expected_red
2229 << ", " << expected_green << ", " << expected_blue << ", " << expected_alpha
2230 << ". At offset: " << i << tcu::TestLog::EndMessage;
2231
2232 result = false;
2233 break;
2234 }
2235 }
2236 }
2237 else if (R32UI_MIPMAP == m_test_case)
2238 {
2239 static const GLuint n_channels = 4;
2240
2241 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
2242
2243 std::vector<GLuint> pixels(n_pixels * n_channels, 0);
2244
2245 Texture::GetData(gl, texture_id, 1 /* level */, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
2246
2247 /* Unbind */
2248 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2249
2250 /* Verify */
2251 for (GLuint i = 0; i < n_pixels; ++i)
2252 {
2253 const GLuint expected_red = i;
2254 const GLuint drawn_red = pixels[i * n_channels];
2255
2256 if (expected_red != drawn_red)
2257 {
2258 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red
2259 << ". Expected value: " << expected_red << " at offset: " << i
2260 << tcu::TestLog::EndMessage;
2261
2262 result = false;
2263 break;
2264 }
2265 }
2266 }
2267 else if (R32UI_MULTISAMPLE == m_test_case)
2268 {
2269 static const GLuint n_channels = 4;
2270
2271 /* Compute shader */
2272 static const GLchar *source =
2273 "${VERSION}\n"
2274 "\n"
2275 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2276 "\n"
2277 "layout (location = 1, r32ui) writeonly uniform uimage2D uni_destination_image;\n"
2278 "layout (location = 0, r32ui) readonly uniform uimage2DMS uni_source_image;\n"
2279 "\n"
2280 "void main()\n"
2281 "{\n"
2282 " const ivec2 point = ivec2(gl_WorkGroupID.x, gl_WorkGroupID.y);\n"
2283 " const uint index = gl_WorkGroupID.y * 16U + gl_WorkGroupID.x;\n"
2284 "\n"
2285 " const uvec4 color_0 = imageLoad(uni_source_image, point, 0);\n"
2286 " const uvec4 color_1 = imageLoad(uni_source_image, point, 1);\n"
2287 " const uvec4 color_2 = imageLoad(uni_source_image, point, 2);\n"
2288 " const uvec4 color_3 = imageLoad(uni_source_image, point, 3);\n"
2289 "\n"
2290 " if (any(equal(uvec4(color_0.r, color_1.r, color_2.r, color_3.r), uvec4(index + 3U))))\n"
2291 " {\n"
2292 " imageStore(uni_destination_image, point, uvec4(1U));\n"
2293 " }\n"
2294 " else\n"
2295 " {\n"
2296 " imageStore(uni_destination_image, point, uvec4(0U));\n"
2297 " }\n"
2298 "}\n"
2299 "\n";
2300
2301 Program program(gl);
2302 Texture destination_texture(gl);
2303
2304 Texture::Generate(gl, destination_texture.m_id);
2305 Texture::Bind(gl, destination_texture.m_id, GL_TEXTURE_2D);
2306 Texture::Storage(gl, GL_TEXTURE_2D, 1, GL_R32UI, width, height, 0 /* depth */);
2307
2308 std::string cs = tcu::StringTemplate(source).specialize(m_specializationMap);
2309 program.Init(cs, "", "", "", "", "");
2310 program.Use();
2311 gl.bindImageTexture(0 /* unit */, texture_id, 0 /* level */, GL_FALSE /* layered */, 0 /* layer */,
2312 GL_READ_ONLY, GL_R32UI);
2313 GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
2314 gl.bindImageTexture(1 /* unit */, destination_texture.m_id, 0 /* level */, GL_FALSE /* layered */,
2315 0 /* layer */, GL_WRITE_ONLY, GL_R32UI);
2316 GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
2317
2318 if (!m_context_is_es)
2319 {
2320 gl.uniform1i(0 /* location */, 0 /* image unit*/);
2321 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
2322
2323 gl.uniform1i(1 /* location */, 1 /* image unit*/);
2324 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
2325 }
2326
2327 gl.dispatchCompute(16, 16, 1);
2328 GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
2329
2330 /* Pixels buffer initialization */
2331 std::vector<GLuint> pixels(n_pixels * n_channels);
2332 initPixels(pixels, n_pixels, n_channels);
2333
2334 Texture::GetData(gl, destination_texture.m_id, 0 /* level */, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT,
2335 &pixels[0]);
2336
2337 /* Unbind */
2338 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2339
2340 /* Verify */
2341 for (GLuint i = 0; i < n_pixels; ++i)
2342 {
2343 const GLuint expected_red = 1;
2344 const GLuint drawn_red = pixels[i * n_channels];
2345
2346 if (expected_red != drawn_red)
2347 {
2348 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red
2349 << ". Expected value: " << expected_red << " at offset: " << i
2350 << tcu::TestLog::EndMessage;
2351
2352 result = false;
2353 break;
2354 }
2355 }
2356 }
2357
2358 return result;
2359 }
2360
2361 /** Constructor
2362 *
2363 * @param testCtx Test context
2364 * @param apiType Api type
2365 **/
ImageLoadStoreTest(tcu::TestContext & testCtx,glu::ApiType apiType)2366 ImageLoadStoreTest::ImageLoadStoreTest(tcu::TestContext &testCtx, glu::ApiType apiType)
2367 : TexelFetchTest(testCtx, "image_load_store", "Verifies that out-of-bound to image result in zero or is discarded",
2368 apiType)
2369 {
2370 }
2371
2372 /** Execute test
2373 *
2374 * @return tcu::TestNode::STOP
2375 **/
iterate()2376 tcu::TestNode::IterateResult ImageLoadStoreTest::iterate()
2377 {
2378 de::SharedPtr<glu::RenderContext> robustContext(createRobustContext());
2379 if (!robustContext.get())
2380 return STOP;
2381
2382 /* Constants */
2383 static const GLuint height = 16;
2384 static const GLuint width = 16;
2385
2386 /* GL entry points */
2387 const Functions &gl = robustContext->getFunctions();
2388
2389 struct FetchingOffset
2390 {
2391 GLuint coord_offset;
2392 GLuint sample_offset;
2393 };
2394 const FetchingOffset fetching_offsets[] = {
2395 {16, 4},
2396 {512, 4},
2397 {1024, 8},
2398 {2048, 8},
2399 };
2400
2401 /* For ES start from RGBA32F as R8, R32UI_MULTISAMPLE and R8_SNORM are not supported */
2402 if (m_context_is_es)
2403 m_test_case = RGBA32F;
2404
2405 /* Test result indicator */
2406 bool test_result = true;
2407
2408 /* Iterate over all cases */
2409 for (; m_test_case < LAST; m_test_case = (TEST_CASES)((GLuint)m_test_case + 1))
2410 {
2411 /* Test case result indicator */
2412 bool case_result = true;
2413
2414 if (R32UI_MULTISAMPLE == m_test_case)
2415 {
2416 // Skip invalid program test in multi sample case
2417 // texelFetch with invalid lod plane results undefined value
2418 // OpenGL 4.5 Core Spec, around page 377
2419 continue;
2420 }
2421
2422 /* Test case objects */
2423 Texture destination_texture(gl);
2424 Texture source_texture(gl);
2425 Program program(gl);
2426
2427 /* Prepare textures */
2428 Texture::Generate(gl, destination_texture.m_id);
2429 Texture::Generate(gl, source_texture.m_id);
2430
2431 if (R32UI_MULTISAMPLE == m_test_case)
2432 {
2433 GLint max_integer_samples;
2434 gl.getIntegerv(GL_MAX_INTEGER_SAMPLES, &max_integer_samples);
2435 GLint max_image_samples;
2436 gl.getIntegerv(GL_MAX_IMAGE_SAMPLES, &max_image_samples);
2437 if (max_integer_samples < 4 || max_image_samples < 4)
2438 {
2439 /* prepareTexture() hard-codes 4 samples (n_levels) for
2440 * R32UI_MULTISAMPLE case. This value exceeds the required
2441 * min-max value (1 in OpenGL ES 3.2) and is not supported
2442 * by all implementations.
2443 *
2444 * Also, the test uses a compute shader with images
2445 * to upload the texture so max_image_samples >= 4
2446 * is also required.
2447 */
2448 m_testCtx.getLog() << tcu::TestLog::Message << "Test case: " << getTestCaseName() << " not supported"
2449 << tcu::TestLog::EndMessage;
2450 continue;
2451 }
2452 }
2453
2454 prepareTexture(gl, false, destination_texture.m_id);
2455 prepareTexture(gl, true, source_texture.m_id);
2456
2457 /* Test invalid source cases */
2458 for (GLuint i = 0; i < DE_LENGTH_OF_ARRAY(fetching_offsets); ++i)
2459 {
2460 const FetchingOffset &fo = fetching_offsets[i];
2461 const std::string &cs = getComputeShader(SOURCE_INVALID, fo.coord_offset, fo.sample_offset);
2462 program.Init(cs, "", "", "", "", "");
2463 program.Use();
2464
2465 /* Set texture */
2466 setTextures(gl, destination_texture.m_id, source_texture.m_id);
2467
2468 /* Dispatch */
2469 gl.dispatchCompute(width, height, 1 /* depth */);
2470 GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
2471
2472 /* Verification */
2473 if (false == verifyInvalidResults(gl, destination_texture.m_id))
2474 {
2475 case_result = false;
2476 }
2477 }
2478
2479 /* Test valid case */
2480 program.Init(getComputeShader(VALID), "", "", "", "", "");
2481 program.Use();
2482
2483 /* Set texture */
2484 setTextures(gl, destination_texture.m_id, source_texture.m_id);
2485
2486 /* Set memory barrier with previous invalid tests */
2487 gl.memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
2488 GLU_EXPECT_NO_ERROR(gl.getError(), "MemoryBarrier");
2489
2490 /* Dispatch */
2491 gl.dispatchCompute(width, height, 1 /* depth */);
2492 GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
2493
2494 /* Verification */
2495 if (false == verifyValidResults(gl, destination_texture.m_id))
2496 {
2497 case_result = false;
2498 }
2499
2500 /* Test invalid destination cases */
2501 for (GLuint i = 0; i < DE_LENGTH_OF_ARRAY(fetching_offsets); ++i)
2502 {
2503 const FetchingOffset &fo = fetching_offsets[i];
2504 const std::string &cs = getComputeShader(DESTINATION_INVALID, fo.coord_offset, fo.sample_offset);
2505 program.Init(cs, "", "", "", "", "");
2506 program.Use();
2507
2508 /* Set texture */
2509 setTextures(gl, destination_texture.m_id, source_texture.m_id);
2510
2511 /* Dispatch */
2512 gl.dispatchCompute(width, height, 1 /* depth */);
2513 GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
2514
2515 /* Verification */
2516 if (false == verifyValidResults(gl, destination_texture.m_id))
2517 {
2518 case_result = false;
2519 }
2520 }
2521
2522 /* Set test result */
2523 if (false == case_result)
2524 {
2525 m_testCtx.getLog() << tcu::TestLog::Message << "Test case: " << getTestCaseName() << " failed"
2526 << tcu::TestLog::EndMessage;
2527
2528 test_result = false;
2529 }
2530 }
2531
2532 /* Set result */
2533 if (true == test_result)
2534 {
2535 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2536 }
2537 else
2538 {
2539 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
2540 }
2541
2542 /* Done */
2543 return tcu::TestNode::STOP;
2544 }
2545
2546 /** Prepare shader for current test case
2547 *
2548 * @param version Specify which version should be prepared
2549 *
2550 * @return Source
2551 **/
getComputeShader(VERSION version,GLuint coord_offset,GLuint sample_offset)2552 std::string ImageLoadStoreTest::getComputeShader(VERSION version, GLuint coord_offset, GLuint sample_offset)
2553 {
2554 static const GLchar *source =
2555 "${VERSION}\n"
2556 "\n"
2557 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2558 "\n"
2559 "layout (${QUALIFIER} = 1, ${FORMAT}) writeonly uniform highp ${IMAGE} uni_destination_image;\n"
2560 "layout (${QUALIFIER} = 0, ${FORMAT}) readonly uniform highp ${IMAGE} uni_source_image;\n"
2561 "\n"
2562 "void main()\n"
2563 "{\n"
2564 " ivec2 point_destination = ivec2(gl_WorkGroupID.xy) + ivec2(${DST_COORD_OFFSET}U);\n"
2565 " ivec2 point_source = ivec2(gl_WorkGroupID.xy) + ivec2(${SRC_COORD_OFFSET}U);\n"
2566 "\n"
2567 "${COPY}"
2568 "}\n";
2569
2570 static const GLchar *copy_multisampled =
2571 " ${TYPE} color_0 = imageLoad(uni_source_image, point_source, 0 + ${SRC_SAMPLE_OFFSET});\n"
2572 " ${TYPE} color_1 = imageLoad(uni_source_image, point_source, 1 + ${SRC_SAMPLE_OFFSET});\n"
2573 " ${TYPE} color_2 = imageLoad(uni_source_image, point_source, 2 + ${SRC_SAMPLE_OFFSET});\n"
2574 " ${TYPE} color_3 = imageLoad(uni_source_image, point_source, 3 + ${SRC_SAMPLE_OFFSET});\n"
2575 " imageStore(uni_destination_image, point_destination, 0 + ${DST_SAMPLE_OFFSET}, color_0);\n"
2576 " imageStore(uni_destination_image, point_destination, 1 + ${DST_SAMPLE_OFFSET}, color_1);\n"
2577 " imageStore(uni_destination_image, point_destination, 2 + ${DST_SAMPLE_OFFSET}, color_2);\n"
2578 " imageStore(uni_destination_image, point_destination, 3 + ${DST_SAMPLE_OFFSET}, color_3);\n";
2579
2580 static const GLchar *copy_regular = " ${TYPE} color = imageLoad(uni_source_image, point_source);\n"
2581 " imageStore(uni_destination_image, point_destination, color);\n";
2582
2583 std::string src_coord_offset_str("0");
2584 std::string dst_coord_offset_str("0");
2585 std::string src_sample_offset_str("0");
2586 std::string dst_sample_offset_str("0");
2587
2588 std::stringstream coord_offset_stream;
2589 coord_offset_stream << coord_offset;
2590 std::stringstream sample_offset_stream;
2591 sample_offset_stream << sample_offset;
2592
2593 m_specializationMap["QUALIFIER"] = m_context_is_es ? "binding" : "location";
2594 m_specializationMap["IMAGE"] = "image2D";
2595 m_specializationMap["TYPE"] = "vec4";
2596 switch (m_test_case)
2597 {
2598 case R8:
2599 m_specializationMap["FORMAT"] = "r8";
2600 break;
2601 case RG8_SNORM:
2602 m_specializationMap["FORMAT"] = "rg8_snorm";
2603 break;
2604 case RGBA32F:
2605 m_specializationMap["FORMAT"] = "rgba32f";
2606 break;
2607 case R32UI_MIPMAP:
2608 m_specializationMap["FORMAT"] = "r32ui";
2609 m_specializationMap["IMAGE"] = "uimage2D";
2610 m_specializationMap["TYPE"] = "uvec4";
2611 break;
2612 case R32UI_MULTISAMPLE:
2613 m_specializationMap["FORMAT"] = "r32ui";
2614 m_specializationMap["IMAGE"] = "uimage2DMS";
2615 m_specializationMap["TYPE"] = "uvec4";
2616 break;
2617 default:
2618 TCU_FAIL("Invalid enum");
2619 }
2620
2621 m_specializationMap["SRC_COORD_OFFSET"] = "0";
2622 m_specializationMap["SRC_SAMPLE_OFFSET"] = "0";
2623 m_specializationMap["DST_COORD_OFFSET"] = "0";
2624 m_specializationMap["DST_SAMPLE_OFFSET"] = "0";
2625
2626 if (version == SOURCE_INVALID)
2627 {
2628 m_specializationMap["SRC_COORD_OFFSET"] = coord_offset_stream.str();
2629 m_specializationMap["SRC_SAMPLE_OFFSET"] = sample_offset_stream.str();
2630 }
2631 else if (version == DESTINATION_INVALID)
2632 {
2633 m_specializationMap["DST_COORD_OFFSET"] = coord_offset_stream.str();
2634 m_specializationMap["DST_SAMPLE_OFFSET"] = sample_offset_stream.str();
2635 }
2636
2637 const GLchar *copy = (m_test_case == R32UI_MULTISAMPLE) ? copy_multisampled : copy_regular;
2638 m_specializationMap["COPY"] = tcu::StringTemplate(copy).specialize(m_specializationMap);
2639
2640 return tcu::StringTemplate(source).specialize(m_specializationMap);
2641 }
2642
2643 /** Set textures as images
2644 *
2645 * @param id_destination Id of texture used as destination
2646 * @param id_source Id of texture used as source
2647 **/
setTextures(const Functions & gl,glw::GLuint id_destination,glw::GLuint id_source)2648 void ImageLoadStoreTest::setTextures(const Functions &gl, glw::GLuint id_destination, glw::GLuint id_source)
2649 {
2650 GLenum format = 0;
2651 GLint level = 0;
2652
2653 switch (m_test_case)
2654 {
2655 case R8:
2656 format = GL_R8;
2657 break;
2658 case RG8_SNORM:
2659 format = GL_RG8_SNORM;
2660 break;
2661 case RGBA32F:
2662 format = GL_RGBA32F;
2663 break;
2664 case R32UI_MIPMAP:
2665 format = GL_R32UI;
2666 level = 1;
2667 break;
2668 case R32UI_MULTISAMPLE:
2669 format = GL_R32UI;
2670 break;
2671 default:
2672 TCU_FAIL("Invalid enum");
2673 }
2674
2675 gl.bindImageTexture(0 /* unit */, id_source, level, GL_FALSE /* layered */, 0 /* layer */, GL_READ_ONLY, format);
2676 GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
2677
2678 gl.bindImageTexture(1 /* unit */, id_destination, level, GL_FALSE /* layered */, 0 /* layer */, GL_WRITE_ONLY,
2679 format);
2680 GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
2681
2682 if (!m_context_is_es)
2683 {
2684 gl.uniform1i(0 /* location */, 0 /* image unit*/);
2685 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
2686
2687 gl.uniform1i(1 /* location */, 1 /* image unit*/);
2688 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
2689 }
2690 }
2691
2692 /** Verifies that texutre is filled with 0
2693 *
2694 * @param texture_id Id of texture
2695 *
2696 * @return true when image is filled with 0, false otherwise
2697 **/
verifyInvalidResults(const Functions & gl,glw::GLuint texture_id)2698 bool ImageLoadStoreTest::verifyInvalidResults(const Functions &gl, glw::GLuint texture_id)
2699 {
2700 static const GLuint height = 16;
2701 static const GLuint width = 16;
2702 static const GLuint n_pixels = height * width;
2703
2704 // OpenGL ES has undefined out-of-bound behavior - no verification
2705 if (m_context_is_es)
2706 return true;
2707
2708 gl.memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
2709 GLU_EXPECT_NO_ERROR(gl.getError(), "MemoryBarrier");
2710
2711 bool result = true;
2712
2713 if (R8 == m_test_case)
2714 {
2715 static const GLuint n_channels = 1;
2716
2717 std::vector<GLubyte> pixels(n_pixels * n_channels);
2718 initPixels(pixels, n_pixels, n_channels);
2719
2720 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
2721
2722 Texture::GetData(gl, 0 /* level */, GL_TEXTURE_2D, GL_RED, GL_UNSIGNED_BYTE, &pixels[0]);
2723
2724 /* Unbind */
2725 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2726
2727 /* Verify */
2728 for (GLuint i = 0; i < n_pixels; ++i)
2729 {
2730 const GLubyte expected_red = 0;
2731 const GLubyte drawn_red = pixels[i];
2732
2733 if (expected_red != drawn_red)
2734 {
2735 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << (GLuint)drawn_red
2736 << ". Expected value: " << (GLuint)expected_red << " at offset: " << i
2737 << tcu::TestLog::EndMessage;
2738
2739 result = false;
2740 break;
2741 }
2742 }
2743 }
2744 else if (RG8_SNORM == m_test_case)
2745 {
2746 static const GLuint n_channels = 2;
2747
2748 std::vector<GLbyte> pixels(n_pixels * n_channels);
2749 initPixels(pixels, n_pixels, n_channels);
2750
2751 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
2752
2753 Texture::GetData(gl, 0 /* level */, GL_TEXTURE_2D, GL_RG, GL_BYTE, &pixels[0]);
2754
2755 /* Unbind */
2756 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2757
2758 /* Verify */
2759 for (GLuint i = 0; i < n_pixels; ++i)
2760 {
2761 const GLbyte expected_red = 0;
2762 const GLbyte expected_green = 0;
2763 const GLbyte drawn_red = pixels[i * n_channels + 0];
2764 const GLbyte drawn_green = pixels[i * n_channels + 1];
2765
2766 if ((expected_red != drawn_red) || (expected_green != drawn_green))
2767 {
2768 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << (GLint)drawn_red << ", "
2769 << (GLint)drawn_green << ". Expected value: " << (GLint)expected_red << ", "
2770 << (GLint)expected_green << ". At offset: " << i << tcu::TestLog::EndMessage;
2771
2772 result = false;
2773 break;
2774 }
2775 }
2776 }
2777 else if (RGBA32F == m_test_case)
2778 {
2779 static const GLuint n_channels = 4;
2780
2781 std::vector<GLfloat> pixels(n_pixels * n_channels);
2782 for (GLuint i = 0; i < n_pixels; ++i)
2783 {
2784 GLuint idx = i * n_channels;
2785 GLfloat value = static_cast<GLfloat>(i) / n_pixels;
2786 pixels[idx + 0] = value;
2787 pixels[idx + 1] = value;
2788 pixels[idx + 2] = value;
2789 pixels[idx + 3] = value;
2790 }
2791
2792 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
2793
2794 Texture::GetData(gl, 0 /* level */, GL_TEXTURE_2D, GL_RGBA, GL_FLOAT, &pixels[0]);
2795
2796 /* Unbind */
2797 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2798
2799 /* Verify */
2800 for (GLuint i = 0; i < n_pixels; ++i)
2801 {
2802 const GLfloat expected_red = 0.0f;
2803 const GLfloat expected_green = 0.0f;
2804 const GLfloat expected_blue = 0.0f;
2805 const GLfloat expected_alpha = 0.0f;
2806 const GLuint idx = i * n_channels;
2807 const GLfloat drawn_red = pixels[idx + 0];
2808 const GLfloat drawn_green = pixels[idx + 1];
2809 const GLfloat drawn_blue = pixels[idx + 2];
2810 const GLfloat drawn_alpha = pixels[idx + 3];
2811
2812 if ((expected_red != drawn_red) || (expected_green != drawn_green) || (expected_blue != drawn_blue) ||
2813 (expected_alpha != drawn_alpha))
2814 {
2815 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red << ", " << drawn_green
2816 << ", " << drawn_blue << ", " << drawn_alpha << ". Expected value: " << expected_red
2817 << ", " << expected_green << ", " << expected_blue << ", " << expected_alpha
2818 << ". At offset: " << i << tcu::TestLog::EndMessage;
2819
2820 result = false;
2821 break;
2822 }
2823 }
2824 }
2825 else if (R32UI_MIPMAP == m_test_case)
2826 {
2827 static const GLuint n_channels = 1;
2828
2829 std::vector<GLuint> pixels(n_pixels * n_channels);
2830 initPixels(pixels, n_pixels, n_channels);
2831
2832 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
2833
2834 Texture::GetData(gl, 1 /* level */, GL_TEXTURE_2D, GL_RED_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
2835
2836 /* Unbind */
2837 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2838
2839 /* Verify */
2840 for (GLuint i = 0; i < n_pixels; ++i)
2841 {
2842 const GLuint expected_red = 0;
2843 const GLuint drawn_red = pixels[i];
2844
2845 if (expected_red != drawn_red)
2846 {
2847 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red
2848 << ". Expected value: " << expected_red << " at offset: " << i
2849 << tcu::TestLog::EndMessage;
2850
2851 result = false;
2852 break;
2853 }
2854 }
2855 }
2856 else if (R32UI_MULTISAMPLE == m_test_case)
2857 {
2858 static const GLuint n_channels = 1;
2859
2860 /* Compute shader */
2861 static const GLchar *cs = "${VERSION}\n"
2862 "\n"
2863 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2864 "\n"
2865 "layout (location = 1) writeonly uniform uimage2D uni_destination_image;\n"
2866 "layout (location = 0, r32ui) readonly uniform uimage2DMS uni_source_image;\n"
2867 "\n"
2868 "void main()\n"
2869 "{\n"
2870 " const ivec2 point = ivec2(gl_WorkGroupID.x, gl_WorkGroupID.y);\n"
2871 "\n"
2872 " const uvec4 color_0 = imageLoad(uni_source_image, point, 0);\n"
2873 " const uvec4 color_1 = imageLoad(uni_source_image, point, 1);\n"
2874 " const uvec4 color_2 = imageLoad(uni_source_image, point, 2);\n"
2875 " const uvec4 color_3 = imageLoad(uni_source_image, point, 3);\n"
2876 "\n"
2877 " if (any(equal(uvec4(color_0.r, color_1.r, color_2.r, color_3.r), uvec4(0))))\n"
2878 " {\n"
2879 " imageStore(uni_destination_image, point, uvec4(1, 1, 1, 1));\n"
2880 " }\n"
2881 " else\n"
2882 " {\n"
2883 " imageStore(uni_destination_image, point, uvec4(0, 0, 0, 0));\n"
2884 " }\n"
2885 "}\n"
2886 "\n";
2887
2888 Program program(gl);
2889 Texture destination_texture(gl);
2890
2891 Texture::Generate(gl, destination_texture.m_id);
2892 Texture::Bind(gl, destination_texture.m_id, GL_TEXTURE_2D);
2893 Texture::Storage(gl, GL_TEXTURE_2D, 1, GL_R32UI, width, height, 0 /* depth */);
2894
2895 program.Init(cs, "", "", "", "", "");
2896 program.Use();
2897 gl.bindImageTexture(0 /* unit */, texture_id, 0 /* level */, GL_FALSE /* layered */, 0 /* layer */,
2898 GL_READ_ONLY, GL_R32UI);
2899 GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
2900 gl.bindImageTexture(1 /* unit */, destination_texture.m_id, 0 /* level */, GL_FALSE /* layered */,
2901 0 /* layer */, GL_WRITE_ONLY, GL_R32UI);
2902 GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
2903
2904 gl.uniform1i(0 /* location */, 0 /* image unit*/);
2905 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
2906
2907 gl.uniform1i(1 /* location */, 1 /* image unit*/);
2908 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
2909
2910 gl.dispatchCompute(16, 16, 1);
2911 GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
2912
2913 /* Pixels buffer initialization */
2914 std::vector<GLuint> pixels(n_pixels * n_channels);
2915 initPixels(pixels, n_pixels, n_channels);
2916
2917 Texture::GetData(gl, 0 /* level */, GL_TEXTURE_2D, GL_RED_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
2918
2919 /* Unbind */
2920 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2921
2922 /* Verify */
2923 for (GLuint i = 0; i < n_pixels; ++i)
2924 {
2925 const GLuint expected_red = 1;
2926 const GLuint drawn_red = pixels[i];
2927
2928 if (expected_red != drawn_red)
2929 {
2930 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red
2931 << ". Expected value: " << expected_red << " at offset: " << i
2932 << tcu::TestLog::EndMessage;
2933
2934 result = false;
2935 break;
2936 }
2937 }
2938 }
2939
2940 return result;
2941 }
2942
2943 /** Verifies that texutre is filled with increasing values
2944 *
2945 * @param texture_id Id of texture
2946 *
2947 * @return true when image is filled with increasing values, false otherwise
2948 **/
verifyValidResults(const glw::Functions & gl,glw::GLuint texture_id)2949 bool ImageLoadStoreTest::verifyValidResults(const glw::Functions &gl, glw::GLuint texture_id)
2950 {
2951 static const GLuint height = 16;
2952 static const GLuint width = 16;
2953 static const GLuint n_pixels = height * width;
2954
2955 gl.memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
2956 GLU_EXPECT_NO_ERROR(gl.getError(), "MemoryBarrier");
2957
2958 bool result = true;
2959
2960 if (R8 == m_test_case)
2961 {
2962 static const GLuint n_channels = 1;
2963
2964 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
2965
2966 std::vector<GLubyte> pixels(n_pixels * n_channels);
2967 initPixels(pixels, n_pixels, n_channels);
2968
2969 Texture::GetData(gl, 0 /* level */, GL_TEXTURE_2D, GL_RED, GL_UNSIGNED_BYTE, &pixels[0]);
2970
2971 /* Unbind */
2972 Texture::Bind(gl, 0, GL_TEXTURE_2D);
2973
2974 /* Verify */
2975 for (GLuint i = 0; i < n_pixels; ++i)
2976 {
2977 const GLubyte expected_red = static_cast<GLubyte>(i);
2978 const GLubyte drawn_red = pixels[i];
2979
2980 if (expected_red != drawn_red)
2981 {
2982 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << (GLuint)drawn_red
2983 << ". Expected value: " << (GLuint)expected_red << " at offset: " << i
2984 << tcu::TestLog::EndMessage;
2985
2986 result = false;
2987 break;
2988 }
2989 }
2990 }
2991 else if (RG8_SNORM == m_test_case)
2992 {
2993 static const GLuint n_channels = 2;
2994
2995 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
2996
2997 std::vector<GLbyte> pixels(n_pixels * n_channels);
2998 initPixels(pixels, n_pixels, n_channels);
2999
3000 Texture::GetData(gl, 0 /* level */, GL_TEXTURE_2D, GL_RG, GL_BYTE, &pixels[0]);
3001
3002 /* Unbind */
3003 Texture::Bind(gl, 0, GL_TEXTURE_2D);
3004
3005 /* Verify */
3006 for (GLuint i = 0; i < n_pixels; ++i)
3007 {
3008 const GLbyte expected_red = static_cast<GLubyte>((i % 16) - 8);
3009 const GLbyte expected_green = static_cast<GLubyte>((i / 16) - 8);
3010 const GLbyte drawn_red = pixels[i * n_channels + 0];
3011 const GLbyte drawn_green = pixels[i * n_channels + 1];
3012
3013 if ((expected_red != drawn_red) || (expected_green != drawn_green))
3014 {
3015 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << (GLint)drawn_red << ", "
3016 << (GLint)drawn_green << ". Expected value: " << (GLint)expected_red << ", "
3017 << (GLint)expected_green << ". At offset: " << i << tcu::TestLog::EndMessage;
3018
3019 result = false;
3020 break;
3021 }
3022 }
3023 }
3024 else if (RGBA32F == m_test_case)
3025 {
3026 static const GLuint n_channels = 4;
3027
3028 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
3029
3030 std::vector<GLfloat> pixels(n_pixels * n_channels);
3031 for (GLuint i = 0; i < n_pixels; ++i)
3032 {
3033 GLfloat value = static_cast<GLfloat>(i) / n_pixels;
3034 pixels[i * n_channels + 0] = value;
3035 pixels[i * n_channels + 1] = value;
3036 pixels[i * n_channels + 2] = value;
3037 pixels[i * n_channels + 3] = value;
3038 }
3039
3040 Texture::GetData(gl, texture_id, 0 /* level */, width, height, GL_RGBA, GL_FLOAT, &pixels[0]);
3041
3042 /* Unbind */
3043 Texture::Bind(gl, 0, GL_TEXTURE_2D);
3044
3045 /* Verify */
3046 for (GLuint i = 0; i < n_pixels; ++i)
3047 {
3048 const GLfloat expected_red = (GLfloat)(i % 16) / 16.0f;
3049 const GLfloat expected_green = (GLfloat)(i / 16) / 16.0f;
3050 const GLfloat expected_blue = (GLfloat)i / 256.0f;
3051 const GLfloat expected_alpha = 1.0f;
3052 const GLuint idx = i * n_channels;
3053 const GLfloat drawn_red = pixels[idx + 0];
3054 const GLfloat drawn_green = pixels[idx + 1];
3055 const GLfloat drawn_blue = pixels[idx + 2];
3056 const GLfloat drawn_alpha = pixels[idx + 3];
3057
3058 if ((expected_red != drawn_red) || (expected_green != drawn_green) || (expected_blue != drawn_blue) ||
3059 (expected_alpha != drawn_alpha))
3060 {
3061 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red << ", " << drawn_green
3062 << ", " << drawn_blue << ", " << drawn_alpha << ". Expected value: " << expected_red
3063 << ", " << expected_green << ", " << expected_blue << ", " << expected_alpha
3064 << ". At offset: " << i << tcu::TestLog::EndMessage;
3065
3066 result = false;
3067 break;
3068 }
3069 }
3070 }
3071 else if (R32UI_MIPMAP == m_test_case)
3072 {
3073 static const GLuint n_channels = 4;
3074
3075 Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
3076
3077 std::vector<GLuint> pixels(n_pixels * n_channels);
3078 initPixels(pixels, n_pixels, n_channels);
3079
3080 Texture::GetData(gl, texture_id, 1 /* level */, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
3081
3082 /* Unbind */
3083 Texture::Bind(gl, 0, GL_TEXTURE_2D);
3084
3085 /* Verify */
3086 for (GLuint i = 0; i < n_pixels; ++i)
3087 {
3088 const GLuint expected_red = i;
3089 const GLuint drawn_red = pixels[i * n_channels];
3090
3091 if (expected_red != drawn_red)
3092 {
3093 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red
3094 << ". Expected value: " << expected_red << " at offset: " << i
3095 << tcu::TestLog::EndMessage;
3096
3097 result = false;
3098 break;
3099 }
3100 }
3101 }
3102 else if (R32UI_MULTISAMPLE == m_test_case)
3103 {
3104 static const GLuint n_channels = 1;
3105
3106 /* Compute shader */
3107 static const GLchar *cs =
3108 "${VERSION}\n"
3109 "\n"
3110 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
3111 "\n"
3112 "layout (location = 1) writeonly uniform uimage2D uni_destination_image;\n"
3113 "layout (location = 0, r32ui) readonly uniform uimage2DMS uni_source_image;\n"
3114 "\n"
3115 "void main()\n"
3116 "{\n"
3117 " const ivec2 point = ivec2(gl_WorkGroupID.x, gl_WorkGroupID.y);\n"
3118 " const uint index = gl_WorkGroupID.y * 16 + gl_WorkGroupID.x;\n"
3119 "\n"
3120 " const uvec4 color_0 = imageLoad(uni_source_image, point, 0);\n"
3121 " const uvec4 color_1 = imageLoad(uni_source_image, point, 1);\n"
3122 " const uvec4 color_2 = imageLoad(uni_source_image, point, 2);\n"
3123 " const uvec4 color_3 = imageLoad(uni_source_image, point, 3);\n"
3124 "\n"
3125 " if (any(equal(uvec4(color_0.r, color_1.r, color_2.r, color_3.r), uvec4(index + 3))))\n"
3126 " {\n"
3127 " imageStore(uni_destination_image, point, uvec4(1, 1, 1, 1));\n"
3128 " }\n"
3129 " else\n"
3130 " {\n"
3131 " imageStore(uni_destination_image, point, uvec4(0, 0, 0, 0));\n"
3132 " }\n"
3133 "}\n"
3134 "\n";
3135
3136 Program program(gl);
3137 Texture destination_texture(gl);
3138
3139 Texture::Generate(gl, destination_texture.m_id);
3140 Texture::Bind(gl, destination_texture.m_id, GL_TEXTURE_2D);
3141 Texture::Storage(gl, GL_TEXTURE_2D, 1, GL_R32UI, width, height, 0 /* depth */);
3142
3143 program.Init(cs, "", "", "", "", "");
3144 program.Use();
3145 gl.bindImageTexture(0 /* unit */, texture_id, 0 /* level */, GL_FALSE /* layered */, 0 /* layer */,
3146 GL_READ_ONLY, GL_R32UI);
3147 GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
3148 gl.bindImageTexture(1 /* unit */, destination_texture.m_id, 0 /* level */, GL_FALSE /* layered */,
3149 0 /* layer */, GL_WRITE_ONLY, GL_R32UI);
3150 GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture");
3151
3152 gl.uniform1i(0 /* location */, 0 /* image unit*/);
3153 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
3154
3155 gl.uniform1i(1 /* location */, 1 /* image unit*/);
3156 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform1i");
3157
3158 gl.dispatchCompute(16, 16, 1);
3159 GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
3160
3161 /* Pixels buffer initialization */
3162 std::vector<GLuint> pixels(n_pixels * n_channels);
3163 initPixels(pixels, n_pixels, n_channels);
3164
3165 Texture::GetData(gl, 0 /* level */, GL_TEXTURE_2D, GL_RED_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
3166
3167 /* Unbind */
3168 Texture::Bind(gl, 0, GL_TEXTURE_2D);
3169
3170 /* Verify */
3171 for (GLuint i = 0; i < n_pixels; ++i)
3172 {
3173 const GLuint expected_red = 1;
3174 const GLuint drawn_red = pixels[i];
3175
3176 if (expected_red != drawn_red)
3177 {
3178 m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red
3179 << ". Expected value: " << expected_red << " at offset: " << i
3180 << tcu::TestLog::EndMessage;
3181
3182 result = false;
3183 break;
3184 }
3185 }
3186 }
3187
3188 return result;
3189 }
3190
3191 /* StorageBufferTest constants */
3192 const GLfloat StorageBufferTest::m_destination_data[4] = {1.0f, 1.0f, 1.0f, 1.0f};
3193 const GLfloat StorageBufferTest::m_source_data[4] = {2.0f, 3.0f, 4.0f, 5.0f};
3194
3195 /** Constructor
3196 *
3197 * @param testCtx Test context
3198 * @param apiType Api type
3199 **/
StorageBufferTest(tcu::TestContext & testCtx,glu::ApiType apiType)3200 StorageBufferTest::StorageBufferTest(tcu::TestContext &testCtx, glu::ApiType apiType)
3201 : RobustnessBase(testCtx, "storage_buffer", "Verifies that out-of-bound access to SSBO is discared or resutls in 0",
3202 apiType)
3203 , m_test_case(VALID)
3204 {
3205 /* Nothing to be done here */
3206 }
3207
3208 /** Execute test
3209 *
3210 * @return tcu::TestNode::STOP
3211 **/
iterate()3212 tcu::TestNode::IterateResult StorageBufferTest::iterate()
3213 {
3214 de::SharedPtr<glu::RenderContext> robustContext(createRobustContext());
3215 if (!robustContext.get())
3216 return STOP;
3217
3218 /* GL entry points */
3219 const Functions &gl = robustContext->getFunctions();
3220
3221 /* Test result indicator */
3222 bool test_result = true;
3223
3224 GLuint test_offsets[] = {
3225 16, // close fetch
3226 4 * 1024, // near fetch (4K of the end of the object)
3227 1024 * 1024, // medium fetch (1MB past the end of the object)
3228 10 * 1024 * 1024 // high fetch (10MB beyond the end of the object)
3229 };
3230
3231 /* Iterate over all cases */
3232 while (LAST != m_test_case)
3233 {
3234 /* Test case objects */
3235 Buffer destination_buffer(gl);
3236 Buffer source_buffer(gl);
3237 Program program(gl);
3238
3239 /* Buffers initialization */
3240 destination_buffer.InitData(GL_SHADER_STORAGE_BUFFER, GL_DYNAMIC_COPY, sizeof(m_destination_data),
3241 m_destination_data);
3242 source_buffer.InitData(GL_SHADER_STORAGE_BUFFER, GL_DYNAMIC_COPY, sizeof(m_source_data), m_source_data);
3243
3244 destination_buffer.BindBase(0);
3245 source_buffer.BindBase(1);
3246
3247 for (GLuint i = 0; i < DE_LENGTH_OF_ARRAY(test_offsets); ++i)
3248 {
3249 /* Initialize shader */
3250 const std::string &cs = getComputeShader(test_offsets[i]);
3251 program.Init(cs, "" /* fs */, "" /* gs */, "" /* tcs */, "" /* tes */, "" /* vs */);
3252 program.Use();
3253
3254 /* Dispatch compute */
3255 gl.dispatchCompute(1 /* x */, 1 /* y */, 1 /* z */);
3256 GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
3257
3258 /* Set memory barrier */
3259 gl.memoryBarrier(GL_ALL_BARRIER_BITS);
3260 GLU_EXPECT_NO_ERROR(gl.getError(), "MemoryBarrier");
3261
3262 /* Verify results */
3263 destination_buffer.Bind();
3264 GLfloat *buffer_data =
3265 (GLfloat *)gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(m_destination_data), GL_MAP_READ_BIT);
3266 GLU_EXPECT_NO_ERROR(gl.getError(), "MapBufferRange");
3267
3268 test_result &= verifyResults(buffer_data);
3269
3270 gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
3271 GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
3272 }
3273
3274 /* Increment */
3275 m_test_case = (VERSION)((GLuint)m_test_case + 1);
3276 }
3277
3278 /* Set result */
3279 if (true == test_result)
3280 {
3281 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3282 }
3283 else
3284 {
3285 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
3286 }
3287
3288 /* Done */
3289 return tcu::TestNode::STOP;
3290 }
3291
3292 /** Prepare shader for current test case
3293 *
3294 * @return Source
3295 **/
getComputeShader(GLuint offset)3296 std::string StorageBufferTest::getComputeShader(GLuint offset)
3297 {
3298 static const GLchar *source = "${VERSION}\n"
3299 "\n"
3300 "layout (local_size_x = 4, local_size_y = 1, local_size_z = 1) in;\n"
3301 "\n"
3302 "layout (binding = 1, std430) buffer Source {\n"
3303 " float data[];\n"
3304 "} source;\n"
3305 "\n"
3306 "layout (binding = 0, std430) buffer Destination {\n"
3307 " float data[];\n"
3308 "} destination;\n"
3309 "\n"
3310 "void main()\n"
3311 "{\n"
3312 " uint index_destination = gl_LocalInvocationID.x + ${DST_OFFSET}U;\n"
3313 " uint index_source = gl_LocalInvocationID.x + ${SRC_OFFSET}U;\n"
3314 "\n"
3315 " destination.data[index_destination] = source.data[index_source];\n"
3316 "}\n"
3317 "\n";
3318
3319 std::stringstream offset_stream;
3320 offset_stream << offset;
3321
3322 m_specializationMap["DST_OFFSET"] = "0";
3323 m_specializationMap["SRC_OFFSET"] = "0";
3324 if (m_test_case == SOURCE_INVALID)
3325 m_specializationMap["SRC_OFFSET"] = offset_stream.str();
3326 else if (m_test_case == DESTINATION_INVALID)
3327 m_specializationMap["DST_OFFSET"] = offset_stream.str();
3328
3329 return tcu::StringTemplate(source).specialize(m_specializationMap);
3330 }
3331
3332 /** Verify test case results
3333 *
3334 * @param buffer_data Buffer data to verify
3335 *
3336 * @return true if buffer_data is as expected, false othrewise
3337 **/
verifyResults(GLfloat * buffer_data)3338 bool StorageBufferTest::verifyResults(GLfloat *buffer_data)
3339 {
3340 /* KHR_robust_buffer_access_behavior (and also GL 4.5 and later) states
3341 * which values can be expected when reading or writing outside of a
3342 * buffer's range. If supported, we will compare results against those
3343 * expectations.
3344 *
3345 * Otherwise, we will attempt to match results against previously observed
3346 * and valid behavior.
3347 */
3348 static const GLfloat expected_data_valid[4] = {2.0f, 3.0f, 4.0f, 5.0f};
3349 static const GLfloat expected_data_invalid_source[4] = {0.0f, 0.0f, 0.0f, 0.0f};
3350 static const GLfloat expected_data_invalid_destination[4] = {1.0f, 1.0f, 1.0f, 1.0f};
3351
3352 /* OpenGL ES has undefined out-of-bound behavior - verify only valid result*/
3353 if (m_context_is_es && (m_test_case != VALID))
3354 return true;
3355
3356 /* Prepare expected data const for proper case*/
3357 const GLchar *name = 0;
3358 bool check_expected_data = false;
3359 const GLfloat *expected_data = 0;
3360 switch (m_test_case)
3361 {
3362 case VALID:
3363 name = "valid indices";
3364 check_expected_data = true;
3365 expected_data = expected_data_valid;
3366 break;
3367 case SOURCE_INVALID:
3368 name = "invalid source indices";
3369 if (m_has_khr_robust_buffer_access)
3370 {
3371 for (int b = 0; b < 4; b++)
3372 {
3373 /* Each out-of-range read can either be 0 or any value within
3374 * the source buffer.
3375 * */
3376 bool valid = false;
3377 if (buffer_data[b] == 0.0f)
3378 {
3379 valid = true;
3380 }
3381 else
3382 {
3383 for (int c = 0; c < 4 && !valid; c++)
3384 {
3385 if (buffer_data[b] == m_source_data[c])
3386 {
3387 valid = true;
3388 }
3389 }
3390 }
3391 if (!valid)
3392 {
3393 m_testCtx.getLog() << tcu::TestLog::Message << "Test case: " << name << " failed"
3394 << tcu::TestLog::EndMessage;
3395 }
3396 }
3397 }
3398 else
3399 {
3400 check_expected_data = true;
3401 expected_data = expected_data_invalid_source;
3402 }
3403 break;
3404 case DESTINATION_INVALID:
3405 name = "invalid destination indices";
3406 if (m_has_khr_robust_buffer_access)
3407 {
3408 for (int b = 0; b < 4; b++)
3409 {
3410 bool valid = false;
3411 /* Each out-of-range write can either be discarded (in which
3412 * case it would have the original destination value) or it
3413 * could write any value within the buffer (so we need to check
3414 * against each possible source value).
3415 */
3416 if (buffer_data[b] == m_destination_data[b])
3417 {
3418 valid = true;
3419 }
3420 else
3421 {
3422 for (int c = 0; c < 4 && !valid; c++)
3423 {
3424 if (buffer_data[b] == m_source_data[c])
3425 {
3426 valid = true;
3427 }
3428 }
3429 }
3430 if (!valid)
3431 {
3432 m_testCtx.getLog() << tcu::TestLog::Message << "Test case: " << name << " failed"
3433 << tcu::TestLog::EndMessage;
3434 }
3435 }
3436 }
3437 else
3438 {
3439 check_expected_data = true;
3440 expected_data = expected_data_invalid_destination;
3441 }
3442 break;
3443 default:
3444 TCU_FAIL("Invalid enum");
3445 }
3446
3447 if (check_expected_data)
3448 {
3449 /* Verify buffer data */
3450 int size = static_cast<int>(sizeof(GLfloat) * 4);
3451 if (0 != memcmp(expected_data, buffer_data, size))
3452 {
3453 m_testCtx.getLog() << tcu::TestLog::Message << "Test case: " << name << " failed"
3454 << tcu::TestLog::EndMessage;
3455 return false;
3456 }
3457 }
3458
3459 return true;
3460 }
3461
3462 /** Constructor
3463 *
3464 * @param context Test context
3465 **/
UniformBufferTest(tcu::TestContext & testCtx,glu::ApiType apiType)3466 UniformBufferTest::UniformBufferTest(tcu::TestContext &testCtx, glu::ApiType apiType)
3467 : RobustnessBase(testCtx, "uniform_buffer", "Verifies that out-of-bound access to UBO resutls in 0", apiType)
3468 , m_test_case(VALID)
3469 {
3470 /* Nothing to be done here */
3471 }
3472
3473 /** Execute test
3474 *
3475 * @return tcu::TestNode::STOP
3476 **/
iterate()3477 tcu::TestNode::IterateResult UniformBufferTest::iterate()
3478 {
3479 de::SharedPtr<glu::RenderContext> robustContext(createRobustContext());
3480 if (!robustContext.get())
3481 return STOP;
3482
3483 static const GLfloat destination_data[4] = {1.0f, 1.0f, 1.0f, 1.0f};
3484 /* The source buffer is packed std140 so we need vec4s */
3485 static const GLfloat source_data[16] = {
3486 2.0f, 0.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 4.0f, 0.0f, 0.0f, 0.0f, 5.0f, 0.0f, 0.0f, 0.0f,
3487 };
3488
3489 GLuint test_offsets[] = {
3490 16, // close fetch
3491 4 * 1024, // near fetch (4K of the end of the object)
3492 1024 * 1024, // medium fetch (1MB past the end of the object)
3493 10 * 1024 * 1024 // high fetch (10MB beyond the end of the object)
3494 };
3495
3496 /* GL entry points */
3497 const Functions &gl = robustContext->getFunctions();
3498
3499 /* Test result indicator */
3500 bool test_result = true;
3501
3502 /* Iterate over all cases */
3503 while (LAST != m_test_case)
3504 {
3505 /* Test case objects */
3506 Buffer destination_buffer(gl);
3507 Buffer source_buffer(gl);
3508 Program program(gl);
3509
3510 /* Buffers initialization */
3511 destination_buffer.InitData(GL_SHADER_STORAGE_BUFFER, GL_DYNAMIC_COPY, sizeof(destination_data),
3512 destination_data);
3513 source_buffer.InitData(GL_UNIFORM_BUFFER, GL_DYNAMIC_COPY, sizeof(source_data), source_data);
3514
3515 destination_buffer.BindBase(0);
3516 source_buffer.BindBase(0);
3517
3518 for (GLuint i = 0; i < DE_LENGTH_OF_ARRAY(test_offsets); ++i)
3519 {
3520 /* Initialize shader */
3521 const std::string &cs = getComputeShader(test_offsets[i]);
3522 program.Init(cs, "" /* fs */, "" /* gs */, "" /* tcs */, "" /* tes */, "" /* vs */);
3523 program.Use();
3524
3525 /* Dispatch compute */
3526 gl.dispatchCompute(1 /* x */, 1 /* y */, 1 /* z */);
3527 GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
3528
3529 /* Set memory barrier */
3530 gl.memoryBarrier(GL_ALL_BARRIER_BITS);
3531 GLU_EXPECT_NO_ERROR(gl.getError(), "MemoryBarrier");
3532
3533 /* Verify results */
3534 destination_buffer.Bind();
3535 GLfloat *buffer_data =
3536 (GLfloat *)gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(destination_data), GL_MAP_READ_BIT);
3537 GLU_EXPECT_NO_ERROR(gl.getError(), "MapBufferRange");
3538
3539 test_result &= verifyResults(buffer_data);
3540
3541 gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
3542 GLU_EXPECT_NO_ERROR(gl.getError(), "UnmapBuffer");
3543 }
3544
3545 /* Increment */
3546 m_test_case = (VERSION)((GLuint)m_test_case + 1);
3547 }
3548
3549 /* Set result */
3550 if (true == test_result)
3551 {
3552 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3553 }
3554 else
3555 {
3556 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
3557 }
3558
3559 /* Done */
3560 return tcu::TestNode::STOP;
3561 }
3562
3563 /** Prepare shader for current test case
3564 *
3565 * @return Source
3566 **/
getComputeShader(GLuint offset)3567 std::string UniformBufferTest::getComputeShader(GLuint offset)
3568 {
3569 static const GLchar *source = "${VERSION}\n"
3570 "\n"
3571 "layout (local_size_x = 4, local_size_y = 1, local_size_z = 1) in;\n"
3572 "\n"
3573 "layout (binding = 0, std140) uniform Source {\n"
3574 " float data[16];\n"
3575 "} source;\n"
3576 "\n"
3577 "layout (binding = 0, std430) buffer Destination {\n"
3578 " float data[];\n"
3579 "} destination;\n"
3580 "\n"
3581 "void main()\n"
3582 "{\n"
3583 " uint index_destination = gl_LocalInvocationID.x;\n"
3584 " uint index_source = gl_LocalInvocationID.x + ${OFFSET}U;\n"
3585 "\n"
3586 " destination.data[index_destination] = source.data[index_source];\n"
3587 "}\n"
3588 "\n";
3589
3590 m_specializationMap["OFFSET"] = "0";
3591 if (m_test_case == SOURCE_INVALID)
3592 {
3593 std::stringstream offset_stream;
3594 offset_stream << offset;
3595 m_specializationMap["OFFSET"] = offset_stream.str();
3596 }
3597
3598 return tcu::StringTemplate(source).specialize(m_specializationMap);
3599 }
3600
3601 /** Verify test case results
3602 *
3603 * @param buffer_data Buffer data to verify
3604 *
3605 * @return true if buffer_data is as expected, false othrewise
3606 **/
verifyResults(GLfloat * buffer_data)3607 bool UniformBufferTest::verifyResults(GLfloat *buffer_data)
3608 {
3609 static const GLfloat expected_data_valid[4] = {2.0f, 3.0f, 4.0f, 5.0f};
3610 static const GLfloat expected_data_invalid_source[4] = {0.0f, 0.0f, 0.0f, 0.0f};
3611
3612 int size = static_cast<int>(sizeof(GLfloat) * 4);
3613
3614 /* Prepare expected data const for proper case*/
3615 const GLfloat *expected_data = 0;
3616 const GLchar *name = 0;
3617 switch (m_test_case)
3618 {
3619 case VALID:
3620 expected_data = expected_data_valid;
3621 name = "valid indices";
3622 break;
3623 case SOURCE_INVALID:
3624 name = "invalid source indices";
3625
3626 if (m_has_khr_robust_buffer_access)
3627 {
3628 /* KHR_robust_buffer_access_behavior (and also GL 4.5 and later) states
3629 * which values can be expected when reading or writing outside of a
3630 * buffer's range. If supported, we will compare results against those
3631 * expectations.
3632 *
3633 * Otherwise, we will attempt to match results against previously observed
3634 * and valid behavior.
3635 */
3636 for (int b = 0; b < 4; b++)
3637 {
3638 /* Each out-of-range read can either be 0 or any value within
3639 * the source buffer.
3640 * */
3641 if (buffer_data[b] == 0.0f)
3642 continue;
3643
3644 bool valid = false;
3645 for (int c = 0; c < 4 && !valid; c++)
3646 {
3647 if (buffer_data[b] == expected_data_valid[c])
3648 {
3649 valid = true;
3650 break;
3651 }
3652 }
3653
3654 if (!valid)
3655 {
3656 m_testCtx.getLog() << tcu::TestLog::Message << "Test case: " << name << " failed"
3657 << tcu::TestLog::EndMessage;
3658 return false;
3659 }
3660 }
3661
3662 return true;
3663 }
3664
3665 expected_data = expected_data_invalid_source;
3666 break;
3667 default:
3668 TCU_FAIL("Invalid enum");
3669 }
3670
3671 /* Verify buffer data */
3672 if (0 != memcmp(expected_data, buffer_data, size))
3673 {
3674 m_testCtx.getLog() << tcu::TestLog::Message << "Test case: " << name << " failed" << tcu::TestLog::EndMessage;
3675 return false;
3676 }
3677
3678 return true;
3679 }
3680 } // namespace RobustBufferAccessBehavior
3681
3682 /** Constructor.
3683 *
3684 * @param context Rendering context.
3685 **/
RobustBufferAccessBehaviorTests(tcu::TestContext & testCtx,glu::ApiType apiType)3686 RobustBufferAccessBehaviorTests::RobustBufferAccessBehaviorTests(tcu::TestContext &testCtx, glu::ApiType apiType)
3687 : tcu::TestCaseGroup(testCtx, "robust_buffer_access_behavior",
3688 "Verifies \"robust buffer access behavior\" functionality")
3689 , m_ApiType(apiType)
3690 {
3691 /* Left blank on purpose */
3692 }
3693
3694 /** Initializes a multi_bind test group.
3695 *
3696 **/
init(void)3697 void RobustBufferAccessBehaviorTests::init(void)
3698 {
3699 addChild(new RobustBufferAccessBehavior::VertexBufferObjectsTest(m_testCtx, m_ApiType));
3700 addChild(new RobustBufferAccessBehavior::TexelFetchTest(m_testCtx, m_ApiType));
3701 addChild(new RobustBufferAccessBehavior::ImageLoadStoreTest(m_testCtx, m_ApiType));
3702 addChild(new RobustBufferAccessBehavior::StorageBufferTest(m_testCtx, m_ApiType));
3703 addChild(new RobustBufferAccessBehavior::UniformBufferTest(m_testCtx, m_ApiType));
3704 }
3705
3706 } // namespace glcts
3707