| /*------------------------------------------------------------------------- |
| * OpenGL Conformance Test Suite |
| * ----------------------------- |
| * |
| * Copyright (c) 2016 The Khronos Group Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ /*! |
| * \file |
| * \brief |
| */ /*-------------------------------------------------------------------*/ |
| |
| /** |
| * \file es32cRobustBufferAccessBehaviorTests.cpp |
| * \brief Implements conformance tests for "Robust Buffer Access Behavior" functionality. |
| */ /*-------------------------------------------------------------------*/ |
| |
| #include "es32cRobustBufferAccessBehaviorTests.hpp" |
| |
| #include "gluDefs.hpp" |
| #include "gluStrUtil.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuTestLog.hpp" |
| |
| #include <cstring> |
| #include <string> |
| |
| using namespace glw; |
| using namespace deqp::RobustBufferAccessBehavior; |
| |
| namespace es32cts |
| { |
| namespace RobustBufferAccessBehavior |
| { |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| VertexBufferObjectsTest::VertexBufferObjectsTest(deqp::Context& context) |
| : deqp::RobustBufferAccessBehavior::VertexBufferObjectsTest( |
| context, "vertex_buffer_objects", "Verifies that out-of-bound reads from VB result in zero") |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult VertexBufferObjectsTest::iterate() |
| { |
| return deqp::RobustBufferAccessBehavior::VertexBufferObjectsTest::iterate(); |
| } |
| |
| /** Prepare shader for current test case |
| * |
| * @return Source |
| **/ |
| std::string VertexBufferObjectsTest::getFragmentShader() |
| { |
| return std::string("#version 320 es\n" |
| "\n" |
| "layout (location = 0) out lowp uvec4 out_fs_color;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " out_fs_color = uvec4(1, 255, 255, 255);\n" |
| "}\n" |
| "\n"); |
| } |
| |
| /** Prepare shader for current test case |
| * |
| * @return Source |
| **/ |
| std::string VertexBufferObjectsTest::getVertexShader() |
| { |
| return std::string("#version 320 es\n" |
| "\n" |
| "layout (location = 0) in vec4 in_vs_position;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = in_vs_position;\n" |
| "}\n" |
| "\n"); |
| } |
| |
| /** No verification because of undefined out-of-bound behavior in OpenGL ES |
| * |
| * @param texture_id Id of texture |
| * |
| * @return true |
| **/ |
| bool VertexBufferObjectsTest::verifyInvalidResults(glw::GLuint texture_id) |
| { |
| (void)texture_id; |
| return true; |
| } |
| |
| /** Verifies that texutre is filled with 1 |
| * |
| * @param texture_id Id of texture |
| * |
| * @return true when image is filled with 1, false otherwise |
| **/ |
| bool VertexBufferObjectsTest::verifyResults(glw::GLuint texture_id) |
| { |
| static const GLuint height = 8; |
| static const GLuint width = 8; |
| static const GLuint pixel_size = 4 * sizeof(GLuint); |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| const GLint buf_size = width * height * pixel_size; |
| GLubyte pixels[buf_size]; |
| deMemset(pixels, 0, buf_size); |
| |
| Texture::Bind(gl, texture_id, GL_TEXTURE_2D); |
| |
| Texture::GetData(gl, texture_id, 0 /* level */, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, pixels); |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, GL_TEXTURE_2D); |
| |
| /* Verify */ |
| for (GLuint i = 0; i < buf_size; i += pixel_size) |
| { |
| if (1 != pixels[i]) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid value: " << (GLuint)pixels[i] |
| << " at offset: " << i << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| TexelFetchTest::TexelFetchTest(deqp::Context& context) |
| : deqp::RobustBufferAccessBehavior::TexelFetchTest(context, "texel_fetch", |
| "Verifies that out-of-bound fetches from texture result in zero") |
| { |
| /* Nothing to be done */ |
| } |
| |
| TexelFetchTest::TexelFetchTest(deqp::Context& context, const glw::GLchar* name, const glw::GLchar* description) |
| : deqp::RobustBufferAccessBehavior::TexelFetchTest(context, name, description) |
| { |
| /* Nothing to be done */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult TexelFetchTest::iterate() |
| { |
| return deqp::RobustBufferAccessBehavior::TexelFetchTest::iterate(); |
| } |
| |
| /** Prepares source code for fragment shader |
| * |
| * @param is_case_valid Selects if valid or invalid case is tested |
| * |
| * @return string with prepared code |
| **/ |
| std::string TexelFetchTest::getFragmentShader(bool is_case_valid) |
| { |
| static const GLchar* plane_0 = " int plane = 0;\n"; |
| |
| static const GLchar* plane_1 = " int plane = 1;\n"; |
| |
| static const GLchar* plane_2 = " int plane = 2;\n"; |
| |
| static const GLchar* plane_sample_invalid = " int plane = 9;\n"; |
| |
| static const GLchar* plane_sample_valid = " int plane = gl_SampleID;\n"; |
| |
| static const GLchar* point_invalid = " ivec2 point = ivec2(gs_fs_tex_coord * 16.0) + ivec2(16, 16);\n"; |
| |
| static const GLchar* point_valid = " ivec2 point = ivec2(gs_fs_tex_coord * 16.0);\n"; |
| |
| static const GLchar* sampler_regular = "sampler2D"; |
| static const GLchar* sampler_regular_u = "usampler2D"; |
| static const GLchar* sampler_multisampled_u = "usampler2DMS"; |
| |
| static const GLchar* template_code = "#version 320 es\n" |
| "\n" |
| " in lowp vec2 gs_fs_tex_coord;\n" |
| "layout (location = 0) out lowp TYPE out_fs_color;\n" |
| "\n" |
| "layout (location = 0) uniform lowp SAMPLER uni_texture;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| "PLANE\n" |
| "POINT\n" |
| " out_fs_color = texelFetch(uni_texture, point, plane);\n" |
| "}\n" |
| "\n"; |
| |
| static const GLchar* type_vec4 = "vec4"; |
| static const GLchar* type_uvec4 = "uvec4"; |
| |
| const GLchar* plane = plane_0; |
| const GLchar* point = point_valid; |
| const GLchar* sampler = sampler_regular; |
| const GLchar* type = type_vec4; |
| |
| if ((R8 == m_test_case) || (RG8_SNORM == m_test_case) || (RGBA32F == m_test_case)) |
| { |
| if (false == is_case_valid) |
| { |
| point = point_invalid; |
| } |
| } |
| else if (R32UI_MIPMAP == m_test_case) |
| { |
| plane = plane_1; |
| sampler = sampler_regular_u; |
| type = type_uvec4; |
| |
| if (false == is_case_valid) |
| { |
| plane = plane_2; |
| } |
| } |
| else if (R32UI_MULTISAMPLE == m_test_case) |
| { |
| plane = plane_sample_valid; |
| sampler = sampler_multisampled_u; |
| type = type_uvec4; |
| |
| if (false == is_case_valid) |
| { |
| plane = plane_sample_invalid; |
| } |
| } |
| |
| size_t position = 0; |
| std::string source = template_code; |
| replaceToken("TYPE", position, type, source); |
| replaceToken("SAMPLER", position, sampler, source); |
| replaceToken("PLANE", position, plane, source); |
| replaceToken("POINT", position, point, source); |
| |
| return source; |
| } |
| |
| /** Prepare shader for current test case |
| * |
| * @return Source |
| **/ |
| std::string TexelFetchTest::getGeometryShader() |
| { |
| return std::string("#version 320 es\n" |
| "\n" |
| "layout(points) in;\n" |
| "layout(triangle_strip, max_vertices = 4) out;\n" |
| "\n" |
| "out vec2 gs_fs_tex_coord;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gs_fs_tex_coord = vec2(0, 0);\n" |
| " gl_Position = vec4(-1, -1, 0, 1);\n" |
| " EmitVertex();\n" |
| "\n" |
| " gs_fs_tex_coord = vec2(0, 1);\n" |
| " gl_Position = vec4(-1, 1, 0, 1);\n" |
| " EmitVertex();\n" |
| "\n" |
| " gs_fs_tex_coord = vec2(1, 0);\n" |
| " gl_Position = vec4(1, -1, 0, 1);\n" |
| " EmitVertex();\n" |
| "\n" |
| " gs_fs_tex_coord = vec2(1, 1);\n" |
| " gl_Position = vec4(1, 1, 0, 1);\n" |
| " EmitVertex();\n" |
| "}\n" |
| "\n"); |
| } |
| |
| /** Prepare shader for current test case |
| * |
| * @return Source |
| **/ |
| std::string TexelFetchTest::getVertexShader() |
| { |
| return std::string("#version 320 es\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(0, 0, 0, 1);\n" |
| "}\n" |
| "\n"); |
| } |
| |
| /** Prepare a texture |
| * |
| * @param is_source Selects if texutre will be used as source or destination |
| * @param texture_id Id of texutre |
| **/ |
| void TexelFetchTest::prepareTexture(bool is_source, glw::GLuint texture_id) |
| { |
| /* Image size */ |
| static const GLuint image_height = 16; |
| static const GLuint image_width = 16; |
| |
| /* GL entry points */ |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Texture storage parameters */ |
| GLuint height = image_height; |
| GLenum internal_format = 0; |
| GLsizei n_levels = 1; |
| GLenum target = GL_TEXTURE_2D; |
| GLuint width = image_width; |
| |
| /* Prepare texture storage parameters */ |
| switch (m_test_case) |
| { |
| case R8: |
| internal_format = GL_R8; |
| break; |
| case RG8_SNORM: |
| internal_format = GL_RG8_SNORM; |
| break; |
| case RGBA32F: |
| internal_format = GL_RGBA32F; |
| break; |
| case R32UI_MIPMAP: |
| height = 2 * image_height; |
| internal_format = GL_R32UI; |
| n_levels = 2; |
| width = 2 * image_width; |
| break; |
| case R32UI_MULTISAMPLE: |
| internal_format = GL_R32UI; |
| n_levels = 4; |
| target = GL_TEXTURE_2D_MULTISAMPLE; |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| |
| /* Prepare storage */ |
| Texture::Bind(gl, texture_id, target); |
| Texture::Storage(gl, target, n_levels, internal_format, width, height, 0); |
| |
| /* Set samplers to NEAREST/NEAREST if required */ |
| if (R32UI_MULTISAMPLE != m_test_case) |
| { |
| gl.texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| gl.texParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| } |
| |
| /* Destination image can be left empty */ |
| if (false == is_source) |
| { |
| Texture::Bind(gl, 0, target); |
| return; |
| } |
| |
| /* Prepare texture */ |
| if (R8 == m_test_case) |
| { |
| GLubyte source_pixels[image_width * image_height]; |
| for (GLuint i = 0; i < image_width * image_height; ++i) |
| { |
| source_pixels[i] = (GLubyte)i; |
| } |
| |
| Texture::SubImage(gl, GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, width, height, |
| 0 /* depth */, GL_RED, GL_UNSIGNED_BYTE, source_pixels); |
| } |
| else if (RG8_SNORM == m_test_case) |
| { |
| static const GLuint n_components = 2; |
| |
| GLbyte source_pixels[image_width * image_height * n_components]; |
| for (GLuint i = 0; i < image_width * image_height; ++i) |
| { |
| source_pixels[i * n_components + 0] = (GLubyte)((i % 16)); |
| source_pixels[i * n_components + 1] = (GLubyte)((i / 16)); |
| } |
| |
| Texture::SubImage(gl, GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, width, height, |
| 0 /* depth */, GL_RG, GL_BYTE, source_pixels); |
| } |
| else if (RGBA32F == m_test_case) |
| { |
| static const GLuint n_components = 4; |
| |
| GLfloat source_pixels[image_width * image_height * n_components]; |
| for (GLuint i = 0; i < image_width * image_height; ++i) |
| { |
| source_pixels[i * n_components + 0] = (GLfloat)(i % 16) / 16.0f; |
| source_pixels[i * n_components + 1] = (GLfloat)(i / 16) / 16.0f; |
| source_pixels[i * n_components + 2] = (GLfloat)i / 256.0f; |
| source_pixels[i * n_components + 3] = 1.0f; |
| } |
| |
| Texture::SubImage(gl, GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, width, height, |
| 0 /* depth */, GL_RGBA, GL_FLOAT, source_pixels); |
| } |
| else if (R32UI_MIPMAP == m_test_case) |
| { |
| GLuint source_pixels[image_width * image_height]; |
| for (GLuint i = 0; i < image_width * image_height; ++i) |
| { |
| source_pixels[i] = i; |
| } |
| |
| Texture::SubImage(gl, GL_TEXTURE_2D, 1 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, image_width, image_height, |
| 0 /* depth */, GL_RED_INTEGER, GL_UNSIGNED_INT, source_pixels); |
| |
| /* texelFetch() undefined if the computed level of detail is not the texture’s base level and the texture’s |
| minification filter is NEAREST or LINEAR */ |
| gl.texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); |
| } |
| else if (R32UI_MULTISAMPLE == m_test_case) |
| { |
| /* Compute Shader */ |
| static const GLchar* cs = "#version 320 es\n" |
| "\n" |
| "layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;\n" |
| "\n" |
| "layout (binding = 0, r32ui) writeonly uniform highp uimage2DMS uni_image;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " ivec2 point = ivec2(gl_WorkGroupID.x, gl_WorkGroupID.y);\n" |
| " uint index = gl_WorkGroupID.y * 16U + gl_WorkGroupID.x;\n" |
| "\n" |
| " imageStore(uni_image, point, 0, uvec4(index + 0U, 0, 0, 0));\n" |
| " imageStore(uni_image, point, 1, uvec4(index + 1U, 0, 0, 0));\n" |
| " imageStore(uni_image, point, 2, uvec4(index + 2U, 0, 0, 0));\n" |
| " imageStore(uni_image, point, 3, uvec4(index + 3U, 0, 0, 0));\n" |
| "}\n" |
| "\n"; |
| |
| Program program(m_context); |
| program.Init(cs, "", "", "", "", ""); |
| program.Use(); |
| |
| gl.bindImageTexture(0 /* unit */, texture_id, 0 /* level */, GL_FALSE /* layered */, 0 /* layer */, |
| GL_WRITE_ONLY, GL_R32UI); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture"); |
| |
| gl.dispatchCompute(16, 16, 1); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute"); |
| } |
| |
| Texture::Bind(gl, 0, target); |
| } |
| |
| /** No verification because of undefined out-of-bound behavior in OpenGL ES |
| * |
| * @param texture_id Id of texture |
| * |
| * @return true |
| **/ |
| bool TexelFetchTest::verifyInvalidResults(glw::GLuint texture_id) |
| { |
| (void)texture_id; |
| return true; |
| } |
| |
| /** Verifies that texutre is filled with increasing values |
| * |
| * @param texture_id Id of texture |
| * |
| * @return true when image is filled with increasing values, false otherwise |
| **/ |
| bool TexelFetchTest::verifyValidResults(glw::GLuint texture_id) |
| { |
| static const GLuint height = 16; |
| static const GLuint width = 16; |
| static const GLuint n_pixels = height * width; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| bool result = true; |
| |
| if (R8 == m_test_case) |
| { |
| static const GLuint n_channels = 4; |
| |
| Texture::Bind(gl, texture_id, GL_TEXTURE_2D); |
| |
| std::vector<GLubyte> pixels; |
| pixels.resize(n_pixels * n_channels); |
| for (GLuint i = 0; i < n_pixels; ++i) |
| { |
| pixels[i] = (GLubyte)i; |
| } |
| |
| Texture::GetData(gl, texture_id, 0 /* level */, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]); |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, GL_TEXTURE_2D); |
| |
| /* Verify */ |
| for (GLuint i = 0; i < n_pixels; ++i) |
| { |
| const GLubyte expected_red = (GLubyte)i; |
| const GLubyte drawn_red = pixels[i * n_channels]; |
| |
| if (expected_red != drawn_red) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid value: " << (GLuint)drawn_red |
| << ". Expected value: " << (GLuint)expected_red |
| << " at offset: " << i << tcu::TestLog::EndMessage; |
| |
| result = false; |
| break; |
| } |
| } |
| } |
| else if (RG8_SNORM == m_test_case) |
| { |
| static const GLuint n_channels = 4; |
| |
| Texture::Bind(gl, texture_id, GL_TEXTURE_2D); |
| |
| std::vector<GLubyte> pixels; |
| pixels.resize(n_pixels * n_channels); |
| for (GLuint i = 0; i < n_pixels; ++i) |
| { |
| pixels[i * n_channels + 0] = (GLubyte)i; |
| pixels[i * n_channels + 1] = (GLubyte)i; |
| } |
| |
| Texture::GetData(gl, texture_id, 0 /* level */, width, height, GL_RGBA, GL_BYTE, &pixels[0]); |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, GL_TEXTURE_2D); |
| |
| /* Verify */ |
| for (GLuint i = 0; i < n_pixels; ++i) |
| { |
| const GLbyte expected_red = (GLubyte)(i % 16); |
| const GLbyte expected_green = (GLubyte)(i / 16); |
| const GLbyte drawn_red = pixels[i * n_channels + 0]; |
| const GLbyte drawn_green = pixels[i * n_channels + 1]; |
| |
| if ((expected_red != drawn_red) || (expected_green != drawn_green)) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid value: " << (GLint)drawn_red << ", " << (GLint)drawn_green |
| << ". Expected value: " << (GLint)expected_red << ", " << (GLint)expected_green |
| << ". At offset: " << i << tcu::TestLog::EndMessage; |
| |
| result = false; |
| break; |
| } |
| } |
| } |
| else if (RGBA32F == m_test_case) |
| { |
| static const GLuint n_channels = 4; |
| |
| Texture::Bind(gl, texture_id, GL_TEXTURE_2D); |
| |
| std::vector<GLfloat> pixels; |
| pixels.resize(n_pixels * n_channels); |
| for (GLuint i = 0; i < n_pixels; ++i) |
| { |
| pixels[i * n_channels + 0] = (GLfloat)i / (GLfloat)n_pixels; |
| pixels[i * n_channels + 1] = (GLfloat)i / (GLfloat)n_pixels; |
| pixels[i * n_channels + 2] = (GLfloat)i / (GLfloat)n_pixels; |
| pixels[i * n_channels + 3] = (GLfloat)i / (GLfloat)n_pixels; |
| } |
| |
| Texture::GetData(gl, texture_id, 0 /* level */, width, height, GL_RGBA, GL_FLOAT, &pixels[0]); |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, GL_TEXTURE_2D); |
| |
| /* Verify */ |
| for (GLuint i = 0; i < n_pixels; ++i) |
| { |
| const GLfloat expected_red = (GLfloat)(i % 16) / 16.0f; |
| const GLfloat expected_green = (GLfloat)(i / 16) / 16.0f; |
| const GLfloat expected_blue = (GLfloat)i / 256.0f; |
| const GLfloat expected_alpha = 1.0f; |
| const GLfloat drawn_red = pixels[i * n_channels + 0]; |
| const GLfloat drawn_green = pixels[i * n_channels + 1]; |
| const GLfloat drawn_blue = pixels[i * n_channels + 2]; |
| const GLfloat drawn_alpha = pixels[i * n_channels + 3]; |
| |
| if ((expected_red != drawn_red) || (expected_green != drawn_green) || (expected_blue != drawn_blue) || |
| (expected_alpha != drawn_alpha)) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid value: " << drawn_red << ", " << drawn_green << ", " |
| << drawn_blue << ", " << drawn_alpha << ". Expected value: " << expected_red << ", " |
| << expected_green << ", " << expected_blue << ", " << expected_alpha << ". At offset: " << i |
| << tcu::TestLog::EndMessage; |
| |
| result = false; |
| break; |
| } |
| } |
| } |
| else if (R32UI_MIPMAP == m_test_case) |
| { |
| static const GLuint n_channels = 4; |
| |
| Texture::Bind(gl, texture_id, GL_TEXTURE_2D); |
| |
| std::vector<GLuint> pixels; |
| pixels.resize(n_pixels * n_channels); |
| deMemset(&pixels[0], 0, n_pixels * n_channels * sizeof(GLuint)); |
| |
| Texture::GetData(gl, texture_id, 1 /* level */, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]); |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, GL_TEXTURE_2D); |
| |
| /* Verify */ |
| for (GLuint i = 0; i < n_pixels; ++i) |
| { |
| const GLuint expected_red = i; |
| const GLuint drawn_red = pixels[i * n_channels]; |
| |
| if (expected_red != drawn_red) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red |
| << ". Expected value: " << expected_red << " at offset: " << i |
| << tcu::TestLog::EndMessage; |
| |
| result = false; |
| break; |
| } |
| } |
| } |
| else if (R32UI_MULTISAMPLE == m_test_case) |
| { |
| static const GLuint n_channels = 4; |
| |
| /* Compute Shader */ |
| static const GLchar* cs = |
| "#version 320 es\n" |
| "\n" |
| "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" |
| "\n" |
| "layout (binding = 1, r32ui) writeonly uniform lowp uimage2D uni_destination_image;\n" |
| "layout (binding = 0, r32ui) readonly uniform lowp uimage2DMS uni_source_image;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " ivec2 point = ivec2(gl_WorkGroupID.x, gl_WorkGroupID.y);\n" |
| " uint index = gl_WorkGroupID.y * 16U + gl_WorkGroupID.x;\n" |
| "\n" |
| " uvec4 color_0 = imageLoad(uni_source_image, point, 0);\n" |
| " uvec4 color_1 = imageLoad(uni_source_image, point, 1);\n" |
| " uvec4 color_2 = imageLoad(uni_source_image, point, 2);\n" |
| " uvec4 color_3 = imageLoad(uni_source_image, point, 3);\n" |
| "\n" |
| " if (any(equal(uvec4(color_0.r, color_1.r, color_2.r, color_3.r), uvec4(index + 3U))))\n" |
| " {\n" |
| " imageStore(uni_destination_image, point, uvec4(1U));\n" |
| " }\n" |
| " else\n" |
| " {\n" |
| " imageStore(uni_destination_image, point, uvec4(0U));\n" |
| " }\n" |
| "}\n" |
| "\n"; |
| |
| Program program(m_context); |
| Texture destination_texture(m_context); |
| |
| Texture::Generate(gl, destination_texture.m_id); |
| Texture::Bind(gl, destination_texture.m_id, GL_TEXTURE_2D); |
| Texture::Storage(gl, GL_TEXTURE_2D, 1, GL_R32UI, width, height, 0 /* depth */); |
| |
| program.Init(cs, "", "", "", "", ""); |
| program.Use(); |
| gl.bindImageTexture(0 /* unit */, texture_id, 0 /* level */, GL_FALSE /* layered */, 0 /* layer */, |
| GL_READ_ONLY, GL_R32UI); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture"); |
| gl.bindImageTexture(1 /* unit */, destination_texture.m_id, 0 /* level */, GL_FALSE /* layered */, |
| 0 /* layer */, GL_WRITE_ONLY, GL_R32UI); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture"); |
| |
| gl.dispatchCompute(16, 16, 1); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute"); |
| |
| /* Pixels buffer initialization */ |
| std::vector<GLuint> pixels; |
| pixels.resize(n_pixels * n_channels); |
| for (GLuint i = 0; i < n_pixels * n_channels; ++i) |
| { |
| pixels[i] = i; |
| } |
| |
| Texture::GetData(gl, destination_texture.m_id, 0 /* level */, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, |
| &pixels[0]); |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, GL_TEXTURE_2D); |
| |
| /* Verify */ |
| for (GLuint i = 0; i < n_pixels; ++i) |
| { |
| const GLuint expected_red = 1; |
| const GLuint drawn_red = pixels[i * n_channels]; |
| |
| if (expected_red != drawn_red) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red |
| << ". Expected value: " << expected_red << " at offset: " << i |
| << tcu::TestLog::EndMessage; |
| |
| result = false; |
| break; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| ImageLoadStoreTest::ImageLoadStoreTest(deqp::Context& context) |
| : TexelFetchTest(context, "image_load_store", "Verifies that out-of-bound to image result in zero or is discarded") |
| { |
| /* start from RGBA32F as R8, R32UI_MULTISAMPLE and R8_SNORM are not supported under GLES */ |
| m_test_case = RGBA32F; |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult ImageLoadStoreTest::iterate() |
| { |
| /* Constants */ |
| static const GLuint height = 16; |
| static const GLuint width = 16; |
| |
| /* GL entry points */ |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Test result indicator */ |
| bool test_result = true; |
| |
| /* Iterate over all cases */ |
| while (LAST != m_test_case) |
| { |
| /* Test case result indicator */ |
| bool case_result = true; |
| |
| /* Test case objects */ |
| Texture destination_texture(m_context); |
| Program invalid_destination_program(m_context); |
| Program invalid_source_program(m_context); |
| Texture source_texture(m_context); |
| Program valid_program(m_context); |
| |
| const std::string& cs_invalid_destination = getComputeShader(DESTINATION_INVALID); |
| const std::string& cs_invalid_source = getComputeShader(SOURCE_INVALID); |
| const std::string& cs_valid = getComputeShader(VALID); |
| |
| /* Prepare textures */ |
| Texture::Generate(gl, destination_texture.m_id); |
| Texture::Generate(gl, source_texture.m_id); |
| |
| prepareTexture(false, destination_texture.m_id); |
| prepareTexture(true, source_texture.m_id); |
| |
| /* Prepare programs */ |
| invalid_destination_program.Init(cs_invalid_destination, "" /* fs */, "" /* gs */, "" /* tcs */, "" /* tes */, |
| "" /* vs */); |
| invalid_source_program.Init(cs_invalid_source, "" /* fs */, "" /* gs */, "" /* tcs */, "" /* tes */, |
| "" /* vs */); |
| valid_program.Init(cs_valid, "" /* fs */, "" /* gs */, "" /* tcs */, "" /* tes */, "" /* vs */); |
| |
| /* Test invalid source case */ |
| /* Set program */ |
| invalid_source_program.Use(); |
| |
| /* Set texture */ |
| setTextures(destination_texture.m_id, source_texture.m_id); |
| |
| /* Dispatch */ |
| gl.dispatchCompute(width, height, 1 /* depth */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute"); |
| |
| /* Verification */ |
| if (false == verifyInvalidResults(destination_texture.m_id)) |
| { |
| case_result = false; |
| } |
| |
| /* Test valid case */ |
| /* Set program */ |
| valid_program.Use(); |
| |
| /* Set texture */ |
| setTextures(destination_texture.m_id, source_texture.m_id); |
| |
| /* Dispatch */ |
| gl.dispatchCompute(width, height, 1 /* depth */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute"); |
| |
| /* Verification */ |
| if (false == verifyValidResults(destination_texture.m_id)) |
| { |
| case_result = false; |
| } |
| |
| /* Test invalid destination case */ |
| /* Set program */ |
| invalid_destination_program.Use(); |
| |
| /* Set texture */ |
| setTextures(destination_texture.m_id, source_texture.m_id); |
| |
| /* Dispatch */ |
| gl.dispatchCompute(width, height, 1 /* depth */); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute"); |
| |
| /* Verification */ |
| if (false == verifyValidResults(destination_texture.m_id)) |
| { |
| case_result = false; |
| } |
| |
| /* Set test result */ |
| if (false == case_result) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Test case: " << getTestCaseName() |
| << " failed" << tcu::TestLog::EndMessage; |
| |
| test_result = false; |
| } |
| |
| /* Increment */ |
| m_test_case = (TEST_CASES)((GLuint)m_test_case + 1); |
| } |
| |
| /* Set result */ |
| if (true == test_result) |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| /* Done */ |
| return tcu::TestNode::STOP; |
| } |
| |
| /** Prepare shader for current test case |
| * |
| * @param version Specify which version should be prepared |
| * |
| * @return Source |
| **/ |
| std::string ImageLoadStoreTest::getComputeShader(VERSION version) |
| { |
| static const GLchar* template_code = |
| "#version 320 es\n" |
| "\n" |
| "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" |
| "\n" |
| "layout (binding = 1, FORMAT) writeonly uniform highp IMAGE uni_destination_image;\n" |
| "layout (binding = 0, FORMAT) readonly uniform highp IMAGE uni_source_image;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " ivec2 point_destination = POINT;\n" |
| " ivec2 point_source = POINT;\n" |
| "\n" |
| "COPY" |
| "}\n" |
| "\n"; |
| |
| static const GLchar* copy_regular = " TYPE color = imageLoad(uni_source_image, point_source);\n" |
| " imageStore(uni_destination_image, point_destination, color);\n"; |
| |
| static const GLchar* format_rgba32f = "rgba32f"; |
| static const GLchar* format_r32ui = "r32ui"; |
| |
| static const GLchar* image_vec4 = "image2D"; |
| |
| static const GLchar* image_uvec4 = "uimage2D"; |
| |
| static const GLchar* point_invalid = "ivec2(gl_WorkGroupID.x + 16U, gl_WorkGroupID.y + 16U)"; |
| |
| static const GLchar* point_valid = "ivec2(gl_WorkGroupID.x, gl_WorkGroupID.y)"; |
| |
| static const GLchar* type_vec4 = "vec4"; |
| static const GLchar* type_uvec4 = "uvec4"; |
| |
| const GLchar* copy = copy_regular; |
| const GLchar* format = format_rgba32f; |
| const GLchar* image = image_vec4; |
| const GLchar* point_destination = point_valid; |
| const GLchar* point_source = point_valid; |
| const GLchar* type = type_vec4; |
| |
| switch (version) |
| { |
| case VALID: |
| break; |
| case SOURCE_INVALID: |
| point_source = point_invalid; |
| break; |
| case DESTINATION_INVALID: |
| point_destination = point_invalid; |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| |
| switch (m_test_case) |
| { |
| case RGBA32F: |
| format = format_rgba32f; |
| break; |
| case R32UI_MIPMAP: |
| format = format_r32ui; |
| image = image_uvec4; |
| type = type_uvec4; |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| }; |
| |
| size_t position = 0; |
| std::string source = template_code; |
| |
| replaceToken("FORMAT", position, format, source); |
| replaceToken("IMAGE", position, image, source); |
| replaceToken("FORMAT", position, format, source); |
| replaceToken("IMAGE", position, image, source); |
| replaceToken("POINT", position, point_destination, source); |
| replaceToken("POINT", position, point_source, source); |
| |
| size_t temp_position = position; |
| replaceToken("COPY", position, copy, source); |
| position = temp_position; |
| |
| switch (m_test_case) |
| { |
| case RGBA32F: |
| case R32UI_MIPMAP: |
| replaceToken("TYPE", position, type, source); |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| |
| return source; |
| } |
| |
| /** Set textures as images |
| * |
| * @param id_destination Id of texture used as destination |
| * @param id_source Id of texture used as source |
| **/ |
| void ImageLoadStoreTest::setTextures(glw::GLuint id_destination, glw::GLuint id_source) |
| { |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| GLenum format = 0; |
| GLint level = 0; |
| |
| switch (m_test_case) |
| { |
| case RGBA32F: |
| format = GL_RGBA32F; |
| break; |
| case R32UI_MIPMAP: |
| format = GL_R32UI; |
| level = 1; |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| |
| gl.bindImageTexture(0 /* unit */, id_source, level, GL_FALSE /* layered */, 0 /* layer */, GL_READ_ONLY, format); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture"); |
| |
| gl.bindImageTexture(1 /* unit */, id_destination, level, GL_FALSE /* layered */, 0 /* layer */, GL_WRITE_ONLY, |
| format); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "BindImageTexture"); |
| } |
| |
| /** No verification because of undefined out-of-bound behavior in OpenGL ES |
| * |
| * @param texture_id Id of texture |
| * |
| * @return true |
| **/ |
| bool ImageLoadStoreTest::verifyInvalidResults(glw::GLuint texture_id) |
| { |
| (void)texture_id; |
| return true; |
| } |
| |
| /** Verifies that texutre is filled with increasing values |
| * |
| * @param texture_id Id of texture |
| * |
| * @return true when image is filled with increasing values, false otherwise |
| **/ |
| bool ImageLoadStoreTest::verifyValidResults(glw::GLuint texture_id) |
| { |
| static const GLuint height = 16; |
| static const GLuint width = 16; |
| static const GLuint n_pixels = height * width; |
| |
| const Functions& gl = m_context.getRenderContext().getFunctions(); |
| gl.memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "MemoryBarrier"); |
| |
| bool result = true; |
| |
| if (RGBA32F == m_test_case) |
| { |
| static const GLuint n_channels = 4; |
| |
| Texture::Bind(gl, texture_id, GL_TEXTURE_2D); |
| |
| std::vector<GLfloat> pixels; |
| pixels.resize(n_pixels * n_channels); |
| for (GLuint i = 0; i < n_pixels; ++i) |
| { |
| pixels[i * n_channels + 0] = (GLfloat)i / (GLfloat)n_pixels; |
| pixels[i * n_channels + 1] = (GLfloat)i / (GLfloat)n_pixels; |
| pixels[i * n_channels + 2] = (GLfloat)i / (GLfloat)n_pixels; |
| pixels[i * n_channels + 3] = (GLfloat)i / (GLfloat)n_pixels; |
| } |
| |
| Texture::GetData(gl, texture_id, 0 /* level */, width, height, GL_RGBA, GL_FLOAT, &pixels[0]); |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, GL_TEXTURE_2D); |
| |
| /* Verify */ |
| for (GLuint i = 0; i < n_pixels; ++i) |
| { |
| const GLfloat expected_red = (GLfloat)(i % 16) / 16.0f; |
| const GLfloat expected_green = (GLfloat)(i / 16) / 16.0f; |
| const GLfloat expected_blue = (GLfloat)i / 256.0f; |
| const GLfloat expected_alpha = 1.0f; |
| const GLfloat drawn_red = pixels[i * n_channels + 0]; |
| const GLfloat drawn_green = pixels[i * n_channels + 1]; |
| const GLfloat drawn_blue = pixels[i * n_channels + 2]; |
| const GLfloat drawn_alpha = pixels[i * n_channels + 3]; |
| |
| if ((expected_red != drawn_red) || (expected_green != drawn_green) || (expected_blue != drawn_blue) || |
| (expected_alpha != drawn_alpha)) |
| { |
| m_context.getTestContext().getLog() |
| << tcu::TestLog::Message << "Invalid value: " << drawn_red << ", " << drawn_green << ", " |
| << drawn_blue << ", " << drawn_alpha << ". Expected value: " << expected_red << ", " |
| << expected_green << ", " << expected_blue << ", " << expected_alpha << ". At offset: " << i |
| << tcu::TestLog::EndMessage; |
| |
| result = false; |
| break; |
| } |
| } |
| } |
| else if (R32UI_MIPMAP == m_test_case) |
| { |
| static const GLuint n_channels = 4; |
| |
| Texture::Bind(gl, texture_id, GL_TEXTURE_2D); |
| |
| std::vector<GLuint> pixels; |
| pixels.resize(n_pixels * n_channels); |
| for (GLuint i = 0; i < n_pixels * n_channels; ++i) |
| { |
| pixels[i] = 0; |
| } |
| |
| Texture::GetData(gl, texture_id, 1 /* level */, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]); |
| |
| /* Unbind */ |
| Texture::Bind(gl, 0, GL_TEXTURE_2D); |
| |
| /* Verify */ |
| for (GLuint i = 0; i < n_pixels; ++i) |
| { |
| const GLuint expected_red = i; |
| const GLuint drawn_red = pixels[i * n_channels]; |
| |
| if (expected_red != drawn_red) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid value: " << drawn_red |
| << ". Expected value: " << expected_red << " at offset: " << i |
| << tcu::TestLog::EndMessage; |
| |
| result = false; |
| break; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| StorageBufferTest::StorageBufferTest(deqp::Context& context) |
| : deqp::RobustBufferAccessBehavior::StorageBufferTest( |
| context, "storage_buffer", "Verifies that out-of-bound access to SSBO results with no error") |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult StorageBufferTest::iterate() |
| { |
| return deqp::RobustBufferAccessBehavior::StorageBufferTest::iterate(); |
| } |
| |
| /** Prepare shader for current test case |
| * |
| * @return Source |
| **/ |
| std::string StorageBufferTest::getComputeShader() |
| { |
| static const GLchar* cs = "#version 320 es\n" |
| "\n" |
| "layout (local_size_x = 4, local_size_y = 1, local_size_z = 1) in;\n" |
| "\n" |
| "layout (binding = 1) buffer Source {\n" |
| " float data[];\n" |
| "} source;\n" |
| "\n" |
| "layout (binding = 0) buffer Destination {\n" |
| " float data[];\n" |
| "} destination;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint index_destination = gl_LocalInvocationID.x + OFFSETU;\n" |
| " uint index_source = gl_LocalInvocationID.x + OFFSETU;\n" |
| "\n" |
| " destination.data[index_destination] = source.data[index_source];\n" |
| "}\n" |
| "\n"; |
| |
| const GLchar* destination_offset; |
| size_t position = 0; |
| std::string source = cs; |
| const GLchar* source_offset; |
| |
| switch (m_test_case) |
| { |
| case VALID: |
| destination_offset = "0"; |
| source_offset = "0"; |
| break; |
| case SOURCE_INVALID: |
| destination_offset = "0"; |
| source_offset = "16"; |
| break; |
| case DESTINATION_INVALID: |
| destination_offset = "16"; |
| source_offset = "0"; |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| |
| replaceToken("OFFSET", position, destination_offset, source); |
| replaceToken("OFFSET", position, source_offset, source); |
| |
| return source; |
| } |
| |
| /** Verify test case results |
| * |
| * @param buffer_data Buffer data to verify |
| * |
| * @return true if buffer_data is as expected, false othrewise |
| **/ |
| bool StorageBufferTest::verifyResults(GLfloat* buffer_data) |
| { |
| static const GLfloat expected_data_valid[4] = { 2.0f, 3.0f, 4.0f, 5.0f }; |
| static const GLfloat expected_data_invalid_destination[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; |
| static const GLfloat expected_data_invalid_source[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; |
| |
| int size = sizeof(GLfloat) * 4; |
| |
| /* Prepare expected data const for proper case*/ |
| const GLfloat* expected_data = 0; |
| const GLchar* name = 0; |
| switch (m_test_case) |
| { |
| case VALID: |
| expected_data = expected_data_valid; |
| name = "valid indices"; |
| break; |
| case SOURCE_INVALID: |
| expected_data = expected_data_invalid_source; |
| name = "invalid source indices"; |
| break; |
| case DESTINATION_INVALID: |
| expected_data = expected_data_invalid_destination; |
| name = "invalid destination indices"; |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| |
| /* Verify buffer data */ |
| if (m_test_case == VALID && memcmp(expected_data, buffer_data, size) != 0) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Test case: " << name << " failed" |
| << tcu::TestLog::EndMessage; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** Constructor |
| * |
| * @param context Test context |
| **/ |
| UniformBufferTest::UniformBufferTest(deqp::Context& context) |
| : deqp::RobustBufferAccessBehavior::UniformBufferTest( |
| context, "uniform_buffer", "Verifies that out-of-bound access to UBO resutls with no error") |
| { |
| /* Nothing to be done here */ |
| } |
| |
| /** Execute test |
| * |
| * @return tcu::TestNode::STOP |
| **/ |
| tcu::TestNode::IterateResult UniformBufferTest::iterate() |
| { |
| return deqp::RobustBufferAccessBehavior::UniformBufferTest::iterate(); |
| } |
| |
| /** Prepare shader for current test case |
| * |
| * @return Source |
| **/ |
| std::string UniformBufferTest::getComputeShader() |
| { |
| static const GLchar* cs = "#version 320 es\n" |
| "\n" |
| "layout (local_size_x = 4, local_size_y = 1, local_size_z = 1) in;\n" |
| "\n" |
| "layout (binding = 0) uniform Source {\n" |
| " float data[16];\n" |
| "} source;\n" |
| "\n" |
| "layout (binding = 0) buffer Destination {\n" |
| " float data[];\n" |
| "} destination;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " uint index_destination = gl_LocalInvocationID.x + OFFSETU;\n" |
| " uint index_source = gl_LocalInvocationID.x + OFFSETU;\n" |
| "\n" |
| " destination.data[index_destination] = source.data[index_source];\n" |
| "}\n" |
| "\n"; |
| |
| const GLchar* destination_offset; |
| size_t position = 0; |
| std::string source = cs; |
| const GLchar* source_offset; |
| |
| switch (m_test_case) |
| { |
| case VALID: |
| destination_offset = "0"; |
| source_offset = "0"; |
| break; |
| case SOURCE_INVALID: |
| destination_offset = "0"; |
| source_offset = "16"; |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| |
| replaceToken("OFFSET", position, destination_offset, source); |
| replaceToken("OFFSET", position, source_offset, source); |
| |
| return source; |
| } |
| |
| /** Verify test case results |
| * |
| * @param buffer_data Buffer data to verify |
| * |
| * @return true if buffer_data is as expected, false othrewise |
| **/ |
| bool UniformBufferTest::verifyResults(GLfloat* buffer_data) |
| { |
| static const GLfloat expected_data_valid[4] = { 2.0f, 3.0f, 4.0f, 5.0f }; |
| static const GLfloat expected_data_invalid_source[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; |
| |
| int size = sizeof(GLfloat) * 4; |
| |
| /* Prepare expected data const for proper case*/ |
| const GLfloat* expected_data = 0; |
| const GLchar* name = 0; |
| switch (m_test_case) |
| { |
| case VALID: |
| expected_data = expected_data_valid; |
| name = "valid indices"; |
| break; |
| case SOURCE_INVALID: |
| expected_data = expected_data_invalid_source; |
| name = "invalid source indices"; |
| break; |
| default: |
| TCU_FAIL("Invalid enum"); |
| } |
| |
| /* Verify buffer data */ |
| if (m_test_case == VALID && memcmp(expected_data, buffer_data, size) != 0) |
| { |
| m_context.getTestContext().getLog() << tcu::TestLog::Message << "Test case: " << name << " failed" |
| << tcu::TestLog::EndMessage; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } /* RobustBufferAccessBehavior */ |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| **/ |
| RobustBufferAccessBehaviorTests::RobustBufferAccessBehaviorTests(deqp::Context& context) |
| : deqp::RobustBufferAccessBehaviorTests(context) |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Initializes a multi_bind test group. |
| * |
| **/ |
| void RobustBufferAccessBehaviorTests::init(void) |
| { |
| addChild(new RobustBufferAccessBehavior::VertexBufferObjectsTest(m_context)); |
| addChild(new RobustBufferAccessBehavior::TexelFetchTest(m_context)); |
| addChild(new RobustBufferAccessBehavior::ImageLoadStoreTest(m_context)); |
| addChild(new RobustBufferAccessBehavior::StorageBufferTest(m_context)); |
| addChild(new RobustBufferAccessBehavior::UniformBufferTest(m_context)); |
| } |
| |
| } /* es32cts namespace */ |