/*------------------------------------------------------------------------- * OpenGL Conformance Test Suite * ----------------------------- * * Copyright (c) 2014-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 esextcTextureCubeMapArraySubImage3D.cpp * \brief Texture Cube Map Array SubImage3D (Test 5) */ /*-------------------------------------------------------------------*/ #include "esextcTextureCubeMapArraySubImage3D.hpp" #include "gluContextInfo.hpp" #include "gluDefs.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuTestLog.hpp" #include namespace glcts { const glw::GLuint TextureCubeMapArraySubImage3D::m_n_components = 4; const glw::GLuint TextureCubeMapArraySubImage3D::m_n_dimensions = 3; const glw::GLuint TextureCubeMapArraySubImage3D::m_n_resolutions = 4; const glw::GLuint TextureCubeMapArraySubImage3D::m_n_storage_type = 2; /* Helper arrays for tests configuration */ /* Different texture resolutions */ const glw::GLuint resolutions[TextureCubeMapArraySubImage3D::m_n_resolutions] [TextureCubeMapArraySubImage3D::m_n_dimensions] = { /* Width , Height, Depth */ { 32, 32, 12 }, { 64, 64, 12 }, { 16, 16, 18 }, { 16, 16, 24 } }; /* Location of dimension in array with texture resolutions */ enum Dimensions_Location { DL_WIDTH = 0, DL_HEIGHT = 1, DL_DEPTH = 2 }; /** Constructor * * @param context Test context * @param name Test case's name * @param description Test case's description */ TextureCubeMapArraySubImage3D::TextureCubeMapArraySubImage3D(Context& context, const ExtParameters& extParams, const char* name, const char* description) : TestCaseBase(context, extParams, name, description) , m_read_fbo_id(0) , m_pixel_buffer_id(0) , m_tex_cube_map_array_id(0) , m_tex_2d_id(0) { /* Nothing to be done here */ } /** Initialize test case */ void TextureCubeMapArraySubImage3D::initTest(void) { /* Check if texture_cube_map_array extension is supported */ if (!m_is_texture_cube_map_array_supported) { throw tcu::NotSupportedError(TEXTURE_CUBE_MAP_ARRAY_EXTENSION_NOT_SUPPORTED, "", __FILE__, __LINE__); } /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.genFramebuffers(1, &m_read_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error generating frame buffer object!"); gl.bindFramebuffer(GL_READ_FRAMEBUFFER, m_read_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding frame buffer object!"); } /** Deinitialize test case */ void TextureCubeMapArraySubImage3D::deinit(void) { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Reset GLES configuration */ gl.bindFramebuffer(GL_READ_FRAMEBUFFER, 0); /* Delete GLES objects */ if (m_read_fbo_id != 0) { gl.deleteFramebuffers(1, &m_read_fbo_id); m_read_fbo_id = 0; } /* Delete pixel unpack buffer */ deletePixelUnpackBuffer(); /* Delete cube map array texture */ deleteCubeMapArrayTexture(); /* Delete 2D texture */ delete2DTexture(); /* Deinitialize base class */ TestCaseBase::deinit(); } /** Executes the test. * * Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise. * * Note the function throws exception should an error occur! * * @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again. */ tcu::TestCase::IterateResult TextureCubeMapArraySubImage3D::iterate() { initTest(); glw::GLboolean test_passed = true; /* Execute test throught all storage types */ for (glw::GLuint storage_index = 0; storage_index < m_n_storage_type; ++storage_index) { /* Execute test throught all texture resolutions */ for (glw::GLuint resolution_index = 0; resolution_index < m_n_resolutions; ++resolution_index) { glw::GLuint width = resolutions[resolution_index][DL_WIDTH]; glw::GLuint height = resolutions[resolution_index][DL_HEIGHT]; glw::GLuint depth = resolutions[resolution_index][DL_DEPTH]; configureCubeMapArrayTexture(width, height, depth, static_cast(storage_index), 0); /* A single whole layer-face at index 0 should be replaced (both functions) */ SubImage3DCopyParams copy_params; copy_params.init(0, 0, 0, width, height, 1); configureDataBuffer(width, height, depth, copy_params, 0); configurePixelUnpackBuffer(copy_params); configure2DTexture(copy_params); testTexSubImage3D(width, height, depth, copy_params, test_passed); testCopyTexSubImage3D(width, height, depth, copy_params, test_passed); deletePixelUnpackBuffer(); delete2DTexture(); /* A region of a layer-face at index 0 should be replaced (both functions) */ copy_params.init(width / 2, height / 2, 0, width / 2, height / 2, 1); configureDataBuffer(width, height, depth, copy_params, 0); configurePixelUnpackBuffer(copy_params); configure2DTexture(copy_params); testTexSubImage3D(width, height, depth, copy_params, test_passed); testCopyTexSubImage3D(width, height, depth, copy_params, test_passed); deletePixelUnpackBuffer(); delete2DTexture(); /* 6 layer-faces, making up a single layer, should be replaced (glTexSubImage3D() only) */ copy_params.init(0, 0, 0, width, height, 6); configureDataBuffer(width, height, depth, copy_params, 0); configurePixelUnpackBuffer(copy_params); testTexSubImage3D(width, height, depth, copy_params, test_passed); deletePixelUnpackBuffer(); /* 6 layer-faces, making up two different layers (for instance: three last layer-faces of layer 1 and three first layer-faces of layer 2) should be replaced (glTexSubImage3D() only) */ copy_params.init(0, 0, 3, width, height, 6); configureDataBuffer(width, height, depth, copy_params, 0); configurePixelUnpackBuffer(copy_params); testTexSubImage3D(width, height, depth, copy_params, test_passed); deletePixelUnpackBuffer(); /* 6 layer-faces, making up a single layer, should be replaced (glTexSubImage3D() only), but limited to a quad */ copy_params.init(width / 2, height / 2, 0, width / 2, height / 2, 6); configureDataBuffer(width, height, depth, copy_params, 0); configurePixelUnpackBuffer(copy_params); testTexSubImage3D(width, height, depth, copy_params, test_passed); deletePixelUnpackBuffer(); /* 6 layer-faces, making up two different layers (for instance: three last layer-faces of layer 1 and three first layer-faces of layer 2) should be replaced (glTexSubImage3D() only), but limited to a quad */ copy_params.init(width / 2, height / 2, 3, width / 2, height / 2, 6); configureDataBuffer(width, height, depth, copy_params, 0); configurePixelUnpackBuffer(copy_params); testTexSubImage3D(width, height, depth, copy_params, test_passed); deletePixelUnpackBuffer(); deleteCubeMapArrayTexture(); } } if (test_passed) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); return STOP; } /** Resizes data buffer and fills it with values * @param width - width of the texture * @param height - height of the texture * @param depth - depth of the texture * @param copy_params - data structure specifying which region of the data store to replace * @param clear_value - value with which to fill the data buffer outside of region specified by copy_params */ void TextureCubeMapArraySubImage3D::configureDataBuffer(glw::GLuint width, glw::GLuint height, glw::GLuint depth, const SubImage3DCopyParams& copy_params, glw::GLuint clear_value) { glw::GLuint index = 0; m_copy_data_buffer.assign(copy_params.m_width * copy_params.m_height * copy_params.m_depth * m_n_components, clear_value); for (glw::GLuint zoffset = copy_params.m_zoffset; zoffset < copy_params.m_zoffset + copy_params.m_depth; ++zoffset) { for (glw::GLuint yoffset = copy_params.m_yoffset; yoffset < copy_params.m_yoffset + copy_params.m_height; ++yoffset) { for (glw::GLuint xoffset = copy_params.m_xoffset; xoffset < copy_params.m_xoffset + copy_params.m_width; ++xoffset) { for (glw::GLuint component = 0; component < m_n_components; ++component) { m_copy_data_buffer[index++] = (zoffset * width * height + yoffset * width + xoffset) * m_n_components + component; } } } } m_expected_data_buffer.assign(width * height * depth * m_n_components, clear_value); for (glw::GLuint zoffset = copy_params.m_zoffset; zoffset < copy_params.m_zoffset + copy_params.m_depth; ++zoffset) { for (glw::GLuint yoffset = copy_params.m_yoffset; yoffset < copy_params.m_yoffset + copy_params.m_height; ++yoffset) { for (glw::GLuint xoffset = copy_params.m_xoffset; xoffset < copy_params.m_xoffset + copy_params.m_width; ++xoffset) { glw::GLuint* data_pointer = &m_expected_data_buffer[(zoffset * width * height + yoffset * width + xoffset) * m_n_components]; for (glw::GLuint component = 0; component < m_n_components; ++component) { data_pointer[component] = (zoffset * width * height + yoffset * width + xoffset) * m_n_components + component; } } } } } /** Creates a pixel unpack buffer that will be used as data source for filling a region of cube map array texture with data * @param copy_params - data structure specifying which region of the data store to replace */ void TextureCubeMapArraySubImage3D::configurePixelUnpackBuffer(const SubImage3DCopyParams& copy_params) { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* generate buffer for pixel unpack buffer */ gl.genBuffers(1, &m_pixel_buffer_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate buffer object!"); /* bind buffer to PIXEL_UNPACK_BUFFER binding point */ gl.bindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pixel_buffer_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not bind buffer object!"); /* fill buffer with data */ gl.bufferData(GL_PIXEL_UNPACK_BUFFER, copy_params.m_width * copy_params.m_height * copy_params.m_depth * m_n_components * sizeof(glw::GLuint), &m_copy_data_buffer[0], GL_STATIC_READ); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not fill buffer object's data store with data!"); gl.bindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not bind buffer object!"); } /** Creates cube map array texture and fills it with data * @param width - width of the texture * @param height - height of the texture * @param depth - depth of the texture * @param storType - mutable or immutable storage type * @param clear_value - value with which to initialize the texture's data store */ void TextureCubeMapArraySubImage3D::configureCubeMapArrayTexture(glw::GLuint width, glw::GLuint height, glw::GLuint depth, STORAGE_TYPE storType, glw::GLuint clear_value) { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.genTextures(1, &m_tex_cube_map_array_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error generating texture object!"); gl.bindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, m_tex_cube_map_array_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding texture object!"); /* used glTexImage3D() method if texture should be MUTABLE */ if (storType == ST_MUTABLE) { gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_BASE_LEVEL, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Error setting texture parameter."); gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAX_LEVEL, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Error setting texture parameter."); DataBufferVec data_buffer(width * height * depth * m_n_components, clear_value); gl.texImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, GL_RGBA32UI, width, height, depth, 0, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &data_buffer[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "Error allocating texture object's data store"); } /* used glTexStorage3D() method if texture should be IMMUTABLE */ else { gl.texStorage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 1, GL_RGBA32UI, width, height, depth); GLU_EXPECT_NO_ERROR(gl.getError(), "Error allocating texture object's data store"); clearCubeMapArrayTexture(width, height, depth, clear_value); } } /** Fills cube map array texture's data store with data * @param width - width of the texture * @param height - height of the texture * @param depth - depth of the texture * @param clear_value - value with which to fill the texture's data store */ void TextureCubeMapArraySubImage3D::clearCubeMapArrayTexture(glw::GLuint width, glw::GLuint height, glw::GLuint depth, glw::GLuint clear_value) { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); DataBufferVec data_buffer(width * height * depth * m_n_components, clear_value); gl.texSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, 0, 0, 0, width, height, depth, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &data_buffer[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "Error filling texture object's data store with data"); } /** Creates 2D texture that will be used as data source by the glCopyTexSubImage3D call * @param copy_params - data structure specifying which region of the data store to replace */ void TextureCubeMapArraySubImage3D::configure2DTexture(const SubImage3DCopyParams& copy_params) { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.genTextures(1, &m_tex_2d_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate texture object!"); gl.bindTexture(GL_TEXTURE_2D, m_tex_2d_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not bind texture object!"); gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32UI, copy_params.m_width, copy_params.m_height); GLU_EXPECT_NO_ERROR(gl.getError(), "Error allocating texture object's data store"); gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, copy_params.m_width, copy_params.m_height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &m_copy_data_buffer[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "Error filling texture object's data store with data"); } /** Replaces region of cube map array texture's data store using texSubImage3D function * @param copy_params - data structure specifying which region of the data store to replace * @param data_pointer - pointer to the data that should end up in the specified region of the data store */ void TextureCubeMapArraySubImage3D::texSubImage3D(const SubImage3DCopyParams& copy_params, const glw::GLuint* data_pointer) { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.texSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, copy_params.m_xoffset, copy_params.m_yoffset, copy_params.m_zoffset, copy_params.m_width, copy_params.m_height, copy_params.m_depth, GL_RGBA_INTEGER, GL_UNSIGNED_INT, data_pointer); GLU_EXPECT_NO_ERROR(gl.getError(), "Error filling texture object's data store with data"); } /** Replaces region of cube map array texture's data store using copyTexSubImage3D function * @param copy_params - data structure specifying which region of the data store to replace */ void TextureCubeMapArraySubImage3D::copyTexSubImage3D(const SubImage3DCopyParams& copy_params) { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_tex_2d_id, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not attach texture object to framebuffer's attachment"); checkFramebufferStatus(GL_READ_FRAMEBUFFER); gl.copyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, copy_params.m_xoffset, copy_params.m_yoffset, copy_params.m_zoffset, 0, 0, copy_params.m_width, copy_params.m_height); GLU_EXPECT_NO_ERROR(gl.getError(), "Error filling texture object's data store with data"); } /** Compares the region of data specified by copy_params taken from the cube map array texture's data store with * the reference data stored in m_data_buffer. * @param width - width of the texture * @param height - height of the texture * @param depth - depth of the texture * @return - true if the result of comparison is that the regions contain the same data, false otherwise */ bool TextureCubeMapArraySubImage3D::checkResults(glw::GLuint width, glw::GLuint height, glw::GLuint depth) { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLuint n_elements = width * height * depth * m_n_components; DataBufferVec result_data_buffer(n_elements, 0); for (glw::GLuint layer_nr = 0; layer_nr < depth; ++layer_nr) { /* attach one layer to framebuffer's attachment */ gl.framebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_tex_cube_map_array_id, 0, layer_nr); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not attach texture object to framebuffer's attachment"); /* check framebuffer status */ checkFramebufferStatus(GL_READ_FRAMEBUFFER); /* read data from the texture */ gl.readPixels(0, 0, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &result_data_buffer[layer_nr * width * height * m_n_components]); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not read pixels from framebuffer's attachment!"); } return memcmp(&result_data_buffer[0], &m_expected_data_buffer[0], n_elements * sizeof(glw::GLuint)) == 0; } /** Perform a full test of testTexSubImage3D function on cube map array texture, both with client pointer and pixel unpack buffer * @param width - width of the texture * @param height - height of the texture * @param depth - depth of the texture * @param copy_params - data structure specifying which region of the cube map array to test * @param test_passed - a boolean variable set to false if at any stage of the test we experience wrong result */ void TextureCubeMapArraySubImage3D::testTexSubImage3D(glw::GLuint width, glw::GLuint height, glw::GLuint depth, const SubImage3DCopyParams& copy_params, glw::GLboolean& test_passed) { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); clearCubeMapArrayTexture(width, height, depth, 0); texSubImage3D(copy_params, &m_copy_data_buffer[0]); if (!checkResults(width, height, depth)) { m_testCtx.getLog() << tcu::TestLog::Message << "glTexSubImage3D failed to copy data to texture cube map array's data store from client's memory\n" << "Texture Cube Map Array Dimensions (width, height, depth) " << "(" << width << "," << height << "," << depth << ")\n" << "Texture Cube Map Array Offsets (xoffset, yoffset, zoffset) " << "(" << copy_params.m_xoffset << "," << copy_params.m_yoffset << "," << copy_params.m_zoffset << ")\n" << "Texture Cube Map Array Copy Size (width, height, depth) " << "(" << copy_params.m_width << "," << copy_params.m_height << "," << copy_params.m_depth << ")\n" << tcu::TestLog::EndMessage; test_passed = false; } clearCubeMapArrayTexture(width, height, depth, 0); gl.bindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pixel_buffer_id); GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding buffer object!"); texSubImage3D(copy_params, 0); if (!checkResults(width, height, depth)) { m_testCtx.getLog() << tcu::TestLog::Message << "glTexSubImage3D failed to copy data to texture cube map " "array's data store from GL_PIXEL_UNPACK_BUFFER\n" << "Texture Cube Map Array Dimensions (width, height, depth) " << "(" << width << "," << height << "," << depth << ")\n" << "Texture Cube Map Array Offsets (xoffset, yoffset, zoffset) " << "(" << copy_params.m_xoffset << "," << copy_params.m_yoffset << "," << copy_params.m_zoffset << ")\n" << "Texture Cube Map Array Copy Size (width, height, depth) " << "(" << copy_params.m_width << "," << copy_params.m_height << "," << copy_params.m_depth << ")\n" << tcu::TestLog::EndMessage; test_passed = false; } gl.bindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding buffer object!"); } /** Perform a full test of copyTexSubImage3D function on cube map array texture * @param width - width of the texture * @param height - height of the texture * @param depth - depth of the texture * @param copy_params - data structure specifying which region of the cube map array to test * @param test_passed - a boolean variable set to false if at any stage of the test we experience wrong result */ void TextureCubeMapArraySubImage3D::testCopyTexSubImage3D(glw::GLuint width, glw::GLuint height, glw::GLuint depth, const SubImage3DCopyParams& copy_params, glw::GLboolean& test_passed) { clearCubeMapArrayTexture(width, height, depth, 0); copyTexSubImage3D(copy_params); if (!checkResults(width, height, depth)) { m_testCtx.getLog() << tcu::TestLog::Message << "glCopyTexSubImage3D failed to copy data to texture cube map array's data store\n" << "Texture Cube Map Array Dimensions (width, height, depth) " << "(" << width << "," << height << "," << depth << ")\n" << "Texture Cube Map Array Offsets (xoffset, yoffset, zoffset) " << "(" << copy_params.m_xoffset << "," << copy_params.m_yoffset << "," << copy_params.m_zoffset << ")\n" << "Texture Cube Map Array Copy Size (width, height, depth) " << "(" << copy_params.m_width << "," << copy_params.m_height << "," << copy_params.m_depth << ")\n" << tcu::TestLog::EndMessage; test_passed = false; } } /** Delete pixel unpack buffer */ void TextureCubeMapArraySubImage3D::deletePixelUnpackBuffer() { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Reset GLES configuration */ gl.bindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); /* Delete buffer object */ if (m_pixel_buffer_id != 0) { gl.deleteBuffers(1, &m_pixel_buffer_id); m_pixel_buffer_id = 0; } } /** Delete cube map array texture */ void TextureCubeMapArraySubImage3D::deleteCubeMapArrayTexture() { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Reset GLES configuration */ gl.bindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, 0); /* Delete texture object */ if (m_tex_cube_map_array_id != 0) { gl.deleteTextures(1, &m_tex_cube_map_array_id); m_tex_cube_map_array_id = 0; } } /* Delete 2D texture that had been used as data source by the glCopyTexSubImage3D call */ void TextureCubeMapArraySubImage3D::delete2DTexture() { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Reset GLES configuration */ gl.bindTexture(GL_TEXTURE_2D, 0); /* Delete texture object */ if (m_tex_2d_id != 0) { gl.deleteTextures(1, &m_tex_2d_id); m_tex_2d_id = 0; } } } // namespace glcts