/*------------------------------------------------------------------------- * OpenGL Conformance Test Suite * ----------------------------- * * Copyright (c) 2015-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 gl4cCopyImageTests.cpp * \brief Implements CopyImageSubData functional tests. */ /*-------------------------------------------------------------------*/ #include "gl4cCopyImageTests.hpp" #include "gluDefs.hpp" #include "gluStrUtil.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuFloat.hpp" #include "tcuTestLog.hpp" #include #include #include #include "deMath.h" /* There are far too much combinations specified for FunctionalTest. * * Following flags controls what is enabled. Set as 1 to enable * all test case from given category, 0 otherwise. * * By default everything is disabled - which still gives 14560 test cases. * * ALL_FORMAT - selects all internal formats, 61 x 61 * ALL_TARGETS - selects all valid targets, 10 x 10 * ALL_IMG_DIM - selects all image dimmensions, 9 x 9 * ALL_REG_DIM - selects all region dimmensions, 7 x 7 * ALL_REG_POS - selects all region positions, like left-top corner, 8 x 8 */ #define COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_FORMATS 0 #define COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_TARGETS 0 #define COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_IMG_DIM 0 #define COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_DIM 0 #define COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_POS 0 /* The following flags controls if workarounds are enabled */ #define COPY_IMAGE_WRKARD_FORMATS 0 using namespace glw; namespace gl4cts { namespace CopyImage { /** Various utilities used by all tests * **/ class Utils { public: /* Routines */ static bool areFormatsCompatible(glw::GLenum src, glw::GLenum dst); static bool comparePixels(glw::GLenum left_internal_format, const glw::GLdouble& left_red, const glw::GLdouble& left_green, const glw::GLdouble& left_blue, const glw::GLdouble& left_alpha, glw::GLenum right_internal_format, const glw::GLdouble& right_red, const glw::GLdouble& right_green, const glw::GLdouble& right_blue, const glw::GLdouble& right_alpha); static bool comparePixels(glw::GLuint left_pixel_size, const glw::GLubyte* left_pixel_data, glw::GLuint right_pixel_size, const glw::GLubyte* right_pixel_data); static void deleteTexture(deqp::Context& context, glw::GLenum target, glw::GLuint name); static bool isTargetMultilayer(glw::GLenum target); static bool isTargetMultilevel(glw::GLenum target); static bool isTargetMultisampled(glw::GLenum target); static glw::GLuint generateTexture(deqp::Context& context, glw::GLenum target); static void maskPixelForFormat(glw::GLenum internal_format, glw::GLubyte* pixel); static glw::GLdouble getEpsilon(glw::GLenum internal_format); static glw::GLuint getPixelSizeForFormat(glw::GLenum internal_format); static glw::GLenum getFormat(glw::GLenum internal_format); static glw::GLuint getNumberOfChannels(glw::GLenum internal_format); static std::string getPixelString(glw::GLenum internal_format, const glw::GLubyte* pixel); static glw::GLenum getType(glw::GLenum internal_format); static void makeTextureComplete(deqp::Context& context, glw::GLenum target, glw::GLuint id, glw::GLint base_level, glw::GLint max_level); static glw::GLuint prepareCompressedTex(deqp::Context& context, glw::GLenum target, glw::GLenum internal_format); static glw::GLuint prepareMultisampleTex(deqp::Context& context, glw::GLenum target, glw::GLsizei n_samples); static glw::GLuint prepareRenderBuffer(deqp::Context& context, glw::GLenum internal_format); static glw::GLuint prepareTex16x16x6(deqp::Context& context, glw::GLenum target, glw::GLenum internal_format, glw::GLenum format, glw::GLenum type, glw::GLuint& out_buf_id); static void prepareTexture(deqp::Context& context, glw::GLuint name, glw::GLenum target, glw::GLenum internal_format, glw::GLenum format, glw::GLenum type, glw::GLuint level, glw::GLuint width, glw::GLuint height, glw::GLuint depth, const glw::GLvoid* pixels, glw::GLuint& out_buf_id); static glw::GLenum transProxyToRealTarget(glw::GLenum target); static glw::GLenum transRealToBindTarget(glw::GLenum target); static void readChannel(glw::GLenum type, glw::GLuint channel, const glw::GLubyte* pixel, glw::GLdouble& out_value); static void writeChannel(glw::GLenum type, glw::GLuint channel, glw::GLdouble value, glw::GLubyte* pixel); static void packPixel(glw::GLenum internal_format, glw::GLenum type, glw::GLdouble red, glw::GLdouble green, glw::GLdouble blue, glw::GLdouble alpha, glw::GLubyte* out_pixel); static void unpackPixel(glw::GLenum format, glw::GLenum type, const glw::GLubyte* pixel, glw::GLdouble& out_red, glw::GLdouble& out_green, glw::GLdouble& out_blue, glw::GLdouble& out_alpha); static bool unpackAndComaprePixels(glw::GLenum left_format, glw::GLenum left_type, glw::GLenum left_internal_format, const glw::GLubyte* left_pixel, glw::GLenum right_format, glw::GLenum right_type, glw::GLenum right_internal_format, const glw::GLubyte* right_pixel); static inline bool roundComponent(glw::GLenum internal_format, glw::GLenum component, glw::GLdouble& value); }; /* Global constants */ static const GLenum s_internal_formats[] = { /* R8 */ GL_R8, GL_R8I, GL_R8UI, GL_R8_SNORM, /* R16 */ GL_R16, GL_R16F, GL_R16I, GL_R16UI, GL_R16_SNORM, /* R32 */ GL_R32F, GL_R32I, GL_R32UI, /* RG8 */ GL_RG8, GL_RG8I, GL_RG8UI, GL_RG8_SNORM, /* RG16 */ GL_RG16, GL_RG16F, GL_RG16I, GL_RG16UI, GL_RG16_SNORM, /* RG32 */ GL_RG32F, GL_RG32I, GL_RG32UI, /* RGB8 */ GL_RGB8, GL_RGB8I, GL_RGB8UI, GL_RGB8_SNORM, /* RGB16 */ GL_RGB16, GL_RGB16F, GL_RGB16I, GL_RGB16UI, GL_RGB16_SNORM, /* RGB32 */ GL_RGB32F, GL_RGB32I, GL_RGB32UI, /* RGBA8 */ GL_RGBA8, GL_RGBA8I, GL_RGBA8UI, GL_RGBA8_SNORM, /* RGBA16 */ GL_RGBA16, GL_RGBA16F, GL_RGBA16I, GL_RGBA16UI, GL_RGBA16_SNORM, /* RGBA32 */ GL_RGBA32F, GL_RGBA32I, GL_RGBA32UI, /* 8 */ GL_R3_G3_B2, GL_RGBA2, /* 12 */ GL_RGB4, /* 15 */ GL_RGB5, /* 16 */ GL_RGBA4, GL_RGB5_A1, /* 30 */ GL_RGB10, /* 32 */ GL_RGB10_A2, GL_RGB10_A2UI, GL_R11F_G11F_B10F, GL_RGB9_E5, /* 36 */ GL_RGB12, /* 48 */ GL_RGBA12, }; static const GLenum s_invalid_targets[] = { GL_TEXTURE_BUFFER, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_PROXY_TEXTURE_1D, GL_PROXY_TEXTURE_1D_ARRAY, GL_PROXY_TEXTURE_2D, GL_PROXY_TEXTURE_2D_ARRAY, GL_PROXY_TEXTURE_2D_MULTISAMPLE, GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_PROXY_TEXTURE_3D, GL_PROXY_TEXTURE_CUBE_MAP, GL_PROXY_TEXTURE_CUBE_MAP_ARRAY, GL_PROXY_TEXTURE_RECTANGLE, }; static const GLenum s_valid_targets[] = { GL_RENDERBUFFER, GL_TEXTURE_1D, GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_RECTANGLE, }; static const GLuint s_n_internal_formats = sizeof(s_internal_formats) / sizeof(s_internal_formats[0]); static const GLuint s_n_invalid_targets = sizeof(s_invalid_targets) / sizeof(s_invalid_targets[0]); static const GLuint s_n_valid_targets = sizeof(s_valid_targets) / sizeof(s_valid_targets[0]); /** * Pixel compatibility depends on pixel size. However value returned by getPixelSizeForFormat * needs some refinements * * @param internal_format Internal format of image * * @return Size of pixel for compatibility checks **/ GLuint getPixelSizeForCompatibilityVerification(GLenum internal_format) { GLuint size = Utils::getPixelSizeForFormat(internal_format); switch (internal_format) { case GL_RGBA2: size = 1; break; default: break; } return size; } #if COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_FORMATS == 0 /** Filters out formats that should not be tested by FunctionalTest * * @param format Internal format * * @return true if format should be tested, false otherwise **/ bool filterFormats(GLenum format) { bool result = true; switch (format) { /* R8 */ case GL_R8I: case GL_R8UI: case GL_R8_SNORM: /* R16 */ case GL_R16: case GL_R16F: case GL_R16I: case GL_R16UI: case GL_R16_SNORM: /* R32 */ case GL_R32F: case GL_R32I: case GL_R32UI: /* RG8 */ case GL_RG8: case GL_RG8I: case GL_RG8UI: case GL_RG8_SNORM: /* RG16 */ case GL_RG16: case GL_RG16F: case GL_RG16I: case GL_RG16UI: case GL_RG16_SNORM: /* RG32 */ case GL_RG32F: case GL_RG32I: case GL_RG32UI: /* RGB8 */ case GL_RGB8: case GL_RGB8I: case GL_RGB8UI: case GL_RGB8_SNORM: /* RGB16 */ case GL_RGB16: case GL_RGB16F: case GL_RGB16I: case GL_RGB16UI: case GL_RGB16_SNORM: /* RGB32 */ case GL_RGB32I: case GL_RGB32UI: /* RGBA8 */ case GL_RGBA8: case GL_RGBA8I: case GL_RGBA8UI: case GL_RGBA8_SNORM: /* RGBA16 */ case GL_RGBA16: case GL_RGBA16F: case GL_RGBA16I: case GL_RGBA16UI: case GL_RGBA16_SNORM: /* RGBA32 */ case GL_RGBA32F: case GL_RGBA32I: case GL_RGBA32UI: result = false; break; default: result = true; break; } return result; } #endif /* COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_FORMATS */ /** Checks if two internal_formats are compatible * * @param src Internal format of source image * @param dst Internal format of destination image * * @return true for compatible formats, false otherwise **/ bool Utils::areFormatsCompatible(glw::GLenum src, glw::GLenum dst) { const GLuint dst_size = getPixelSizeForCompatibilityVerification(dst); const GLuint src_size = getPixelSizeForCompatibilityVerification(src); if (dst_size != src_size) { return false; } #if COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_FORMATS == 0 if ((false == filterFormats(src)) || (false == filterFormats(dst))) { return false; } #endif /* COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_FORMATS */ if (src != dst) { if ((GL_R3_G3_B2 == dst) || (GL_R3_G3_B2 == src) || (GL_RGBA2 == dst) || (GL_RGBA2 == src) || (GL_RGBA4 == dst) || (GL_RGBA4 == src) || (GL_RGB5_A1 == dst) || (GL_RGB5_A1 == src) || (GL_RGB10 == dst) || (GL_RGB10 == src)) { return false; } } #if COPY_IMAGE_WRKARD_FORMATS if ((GL_RGB10_A2 == src) && (GL_R11F_G11F_B10F == dst) || (GL_RGB10_A2 == src) && (GL_RGB9_E5 == dst) || (GL_RGB10_A2UI == src) && (GL_R11F_G11F_B10F == dst) || (GL_RGB10_A2UI == src) && (GL_RGB9_E5 == dst) || (GL_RGB9_E5 == src) && (GL_RGB10_A2 == dst) || (GL_RGB9_E5 == src) && (GL_RGB10_A2UI == dst) || (GL_R11F_G11F_B10F == src) && (GL_RGB10_A2 == dst) || (GL_R11F_G11F_B10F == src) && (GL_RGB10_A2UI == dst)) { return false; } #endif /* COPY_IMAGE_WRKARD_FORMATS */ if (2 == dst_size) { if (src == dst) { return true; } if (((GL_RGB4 == src) && (GL_RGB4 != dst)) || ((GL_RGB4 != src) && (GL_RGB4 == dst)) || ((GL_RGB5 == src) && (GL_RGB5 != dst)) || ((GL_RGB5 != src) && (GL_RGB5 == dst))) { return false; } return true; } if (4 == dst_size) { if (src == dst) { return true; } return true; } return true; } /** Compare two pixels * * @param left_internal_format Internal format of left image * @param left_red Red channel of left image * @param left_green Green channel of left image * @param left_blue Blue channel of left image * @param left_alpha Alpha channel of left image * @param right_internal_format Internal format of right image * @param right_red Red channel of right image * @param right_green Green channel of right image * @param right_blue Blue channel of right image * @param right_alpha Alpha channel of right image * * @return true if pixels match, false otherwise **/ bool Utils::comparePixels(GLenum left_internal_format, const GLdouble& left_red, const GLdouble& left_green, const GLdouble& left_blue, const GLdouble& left_alpha, GLenum right_internal_format, const GLdouble& right_red, const GLdouble& right_green, const GLdouble& right_blue, const GLdouble& right_alpha) { const GLuint left_n_channels = getNumberOfChannels(left_internal_format); const GLuint right_n_channels = getNumberOfChannels(right_internal_format); const GLuint n_channels = (left_n_channels >= right_n_channels) ? right_n_channels : left_n_channels; const GLdouble left_channels[4] = { left_red, left_green, left_blue, left_alpha }; const GLdouble right_channels[4] = { right_red, right_green, right_blue, right_alpha }; for (GLuint i = 0; i < n_channels; ++i) { const GLdouble left = left_channels[i]; const GLdouble right = right_channels[i]; const GLdouble left_eps = getEpsilon(left_internal_format); const GLdouble right_eps = getEpsilon(right_internal_format); const GLdouble eps = fabs(std::max(left_eps, right_eps)); if (eps < fabs(left - right)) { return false; } } return true; } /** Compare two pixels with memcmp * * @param left_pixel_size Size of left pixel * @param left_pixel_data Data of left pixel * @param right_pixel_size Size of right pixel * @param right_pixel_data Data of right pixel * * @return true if memory match, false otherwise **/ bool Utils::comparePixels(GLuint left_pixel_size, const GLubyte* left_pixel_data, GLuint right_pixel_size, const GLubyte* right_pixel_data) { const GLuint pixel_size = (left_pixel_size >= right_pixel_size) ? left_pixel_size : right_pixel_size; return 0 == memcmp(left_pixel_data, right_pixel_data, pixel_size); } /** Delete texture or renderbuffer * * @param context Test context * @param target Image target * @param name Name of image **/ void Utils::deleteTexture(deqp::Context& context, GLenum target, GLuint name) { const Functions& gl = context.getRenderContext().getFunctions(); if (GL_RENDERBUFFER == target) { gl.deleteRenderbuffers(1, &name); } else { gl.deleteTextures(1, &name); } } /** Get epsilon for given internal_format * * @param internal_format Internal format of image * * @return Epsilon value **/ GLdouble Utils::getEpsilon(GLenum internal_format) { GLdouble epsilon; switch (internal_format) { case GL_R8: case GL_R8_SNORM: case GL_R16: case GL_R16F: case GL_R16_SNORM: case GL_R32F: case GL_R8I: case GL_R8UI: case GL_R16I: case GL_R16UI: case GL_R32I: case GL_R32UI: case GL_RG8: case GL_RG8_SNORM: case GL_RG16: case GL_RG16F: case GL_RG16_SNORM: case GL_RG32F: case GL_RG8I: case GL_RG8UI: case GL_RG16I: case GL_RG16UI: case GL_RG32I: case GL_RG32UI: case GL_R3_G3_B2: case GL_RGB4: case GL_RGB5: case GL_RGB8: case GL_RGB8_SNORM: case GL_R11F_G11F_B10F: case GL_RGB16: case GL_RGB16F: case GL_RGB16_SNORM: case GL_RGB32F: case GL_RGB8I: case GL_RGB8UI: case GL_RGB10: case GL_RGB16I: case GL_RGB16UI: case GL_RGB32I: case GL_RGB32UI: case GL_RGB9_E5: case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1: case GL_RGBA8: case GL_RGBA8_SNORM: case GL_RGB10_A2: case GL_RGBA16: case GL_RGBA16F: case GL_RGBA16_SNORM: case GL_RGBA32F: case GL_RGBA8I: case GL_RGBA8UI: case GL_RGB10_A2UI: case GL_RGBA16I: case GL_RGBA16UI: case GL_RGBA32I: case GL_RGBA32UI: epsilon = 0.0; break; case GL_RGB12: case GL_RGBA12: epsilon = 0.00390625; break; default: TCU_FAIL("Invalid enum"); break; } return epsilon; } /** Get format for given internal format * * @param internal_format Internal format * * @return Format **/ GLenum Utils::getFormat(GLenum internal_format) { GLenum format = 0; switch (internal_format) { /* R */ case GL_R8: case GL_R8_SNORM: case GL_R16: case GL_R16F: case GL_R16_SNORM: case GL_R32F: format = GL_RED; break; case GL_R8I: case GL_R8UI: case GL_R16I: case GL_R16UI: case GL_R32I: case GL_R32UI: format = GL_RED_INTEGER; break; /* RG */ case GL_RG8: case GL_RG8_SNORM: case GL_RG16: case GL_RG16F: case GL_RG16_SNORM: case GL_RG32F: format = GL_RG; break; case GL_RG8I: case GL_RG8UI: case GL_RG16I: case GL_RG16UI: case GL_RG32I: case GL_RG32UI: format = GL_RG_INTEGER; break; /* RGB */ case GL_R3_G3_B2: case GL_RGB4: case GL_RGB5: case GL_RGB8: case GL_RGB8_SNORM: case GL_R11F_G11F_B10F: case GL_RGB12: case GL_RGB16: case GL_RGB16F: case GL_RGB16_SNORM: case GL_RGB32F: case GL_RGB9_E5: format = GL_RGB; break; case GL_RGB8I: case GL_RGB8UI: case GL_RGB16I: case GL_RGB16UI: case GL_RGB32I: case GL_RGB32UI: format = GL_RGB_INTEGER; break; /* RGBA */ case GL_RGB10: case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1: case GL_RGBA8: case GL_RGBA8_SNORM: case GL_RGB10_A2: case GL_RGBA12: case GL_RGBA16: case GL_RGBA16F: case GL_RGBA16_SNORM: case GL_RGBA32F: format = GL_RGBA; break; case GL_RGBA8I: case GL_RGBA8UI: case GL_RGB10_A2UI: case GL_RGBA16I: case GL_RGBA16UI: case GL_RGBA32I: case GL_RGBA32UI: format = GL_RGBA_INTEGER; break; default: TCU_FAIL("Invalid enum"); break; } return format; } /** Get number of channels for given internal_format * * @param internal_format Internal format * * @return Number of channels **/ GLuint Utils::getNumberOfChannels(GLenum internal_format) { GLuint result = 0; switch (internal_format) { case GL_R8: case GL_R8_SNORM: case GL_R16: case GL_R16F: case GL_R16_SNORM: case GL_R32F: case GL_R8I: case GL_R8UI: case GL_R16I: case GL_R16UI: case GL_R32I: case GL_R32UI: result = 1; break; case GL_RG8: case GL_RG8_SNORM: case GL_RG16: case GL_RG16F: case GL_RG16_SNORM: case GL_RG32F: case GL_RG8I: case GL_RG8UI: case GL_RG16I: case GL_RG16UI: case GL_RG32I: case GL_RG32UI: result = 2; break; case GL_R3_G3_B2: case GL_RGB4: case GL_RGB5: case GL_RGB8: case GL_RGB8_SNORM: case GL_RGB10: case GL_R11F_G11F_B10F: case GL_RGB12: case GL_RGB16: case GL_RGB16F: case GL_RGB16_SNORM: case GL_RGB32F: case GL_RGB9_E5: case GL_RGB8I: case GL_RGB8UI: case GL_RGB16I: case GL_RGB16UI: case GL_RGB32I: case GL_RGB32UI: result = 3; break; case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1: case GL_RGBA8: case GL_RGBA8_SNORM: case GL_RGB10_A2: case GL_RGBA12: case GL_RGBA16: case GL_RGBA16F: case GL_RGBA16_SNORM: case GL_RGBA32F: case GL_RGBA8I: case GL_RGBA8UI: case GL_RGB10_A2UI: case GL_RGBA16I: case GL_RGBA16UI: case GL_RGBA32I: case GL_RGBA32UI: result = 4; break; default: TCU_FAIL("Invalid enum"); break; } return result; } /** Get type for given internal format * * @param internal_format Internal format * * @return Type **/ GLenum Utils::getType(GLenum internal_format) { GLenum type = 0; switch (internal_format) { case GL_R8: case GL_R8UI: case GL_RG8: case GL_RG8UI: case GL_RGB8: case GL_RGB8UI: case GL_RGBA8: case GL_RGBA8UI: type = GL_UNSIGNED_BYTE; break; case GL_R8_SNORM: case GL_R8I: case GL_RG8_SNORM: case GL_RG8I: case GL_RGB8_SNORM: case GL_RGB8I: case GL_RGBA8_SNORM: case GL_RGBA8I: type = GL_BYTE; break; case GL_R3_G3_B2: type = GL_UNSIGNED_BYTE_3_3_2; break; case GL_RGB4: case GL_RGB5: type = GL_UNSIGNED_SHORT_5_6_5; break; case GL_RGBA2: case GL_RGBA4: type = GL_UNSIGNED_SHORT_4_4_4_4; break; case GL_RGB5_A1: type = GL_UNSIGNED_SHORT_5_5_5_1; break; case GL_RGB10: case GL_RGB10_A2: case GL_RGB10_A2UI: type = GL_UNSIGNED_INT_2_10_10_10_REV; break; case GL_R16F: case GL_RG16F: case GL_RGB16F: case GL_RGBA16F: type = GL_HALF_FLOAT; break; case GL_R16: case GL_R16UI: case GL_RG16: case GL_RG16UI: case GL_RGB12: case GL_RGB16: case GL_RGB16UI: case GL_RGBA12: case GL_RGBA16: case GL_RGBA16UI: type = GL_UNSIGNED_SHORT; break; case GL_R16_SNORM: case GL_R16I: case GL_RG16_SNORM: case GL_RG16I: case GL_RGB16_SNORM: case GL_RGB16I: case GL_RGBA16_SNORM: case GL_RGBA16I: type = GL_SHORT; break; case GL_R32UI: case GL_RG32UI: case GL_RGB32UI: case GL_RGBA32UI: type = GL_UNSIGNED_INT; break; case GL_RGB9_E5: type = GL_UNSIGNED_INT_5_9_9_9_REV; break; case GL_R32I: case GL_RG32I: case GL_RGB32I: case GL_RGBA32I: type = GL_INT; break; case GL_R32F: case GL_RG32F: case GL_RGB32F: case GL_RGBA32F: type = GL_FLOAT; break; case GL_R11F_G11F_B10F: type = GL_UNSIGNED_INT_10F_11F_11F_REV; break; default: TCU_FAIL("Invalid enum"); break; } return type; } /** Returns mask that should be applied to pixel value * * @param internal_format Internal format of texture * @param pixel Pixel data * * @return Mask **/ void Utils::maskPixelForFormat(GLenum internal_format, GLubyte* pixel) { switch (internal_format) { case GL_RGB10: /* UINT_10_10_10_2 - ALPHA will be set to 3*/ pixel[0] |= 0x03; break; default: break; } } /** Get size of pixel for given internal format * * @param internal_format Internal format * * @return Number of bytes used by given format **/ GLuint Utils::getPixelSizeForFormat(GLenum internal_format) { GLuint size = 0; switch (internal_format) { /* 8 */ case GL_R8: case GL_R8I: case GL_R8UI: case GL_R8_SNORM: case GL_R3_G3_B2: size = 1; break; /* 8 */ case GL_RGBA2: size = 2; break; /* 12 */ case GL_RGB4: size = 2; break; /* 15 */ case GL_RGB5: size = 2; break; /* 16 */ case GL_RG8: case GL_RG8I: case GL_RG8UI: case GL_RG8_SNORM: case GL_R16: case GL_R16F: case GL_R16I: case GL_R16UI: case GL_R16_SNORM: case GL_RGBA4: case GL_RGB5_A1: size = 2; break; /* 24 */ case GL_RGB8: case GL_RGB8I: case GL_RGB8UI: case GL_RGB8_SNORM: size = 3; break; /* 30 */ case GL_RGB10: size = 4; break; /* 32 */ case GL_RGBA8: case GL_RGBA8I: case GL_RGBA8UI: case GL_RGBA8_SNORM: case GL_RG16: case GL_RG16F: case GL_RG16I: case GL_RG16UI: case GL_RG16_SNORM: case GL_R32F: case GL_R32I: case GL_R32UI: case GL_RGB10_A2: case GL_RGB10_A2UI: case GL_R11F_G11F_B10F: case GL_RGB9_E5: size = 4; break; /* 36 */ case GL_RGB12: size = 6; break; /* 48 */ case GL_RGB16: case GL_RGB16F: case GL_RGB16I: case GL_RGB16UI: case GL_RGB16_SNORM: size = 6; break; /* 64 */ case GL_RGBA12: case GL_RGBA16: case GL_RGBA16F: case GL_RGBA16I: case GL_RGBA16UI: case GL_RGBA16_SNORM: case GL_RG32F: case GL_RG32I: case GL_RG32UI: size = 8; break; /* 96 */ case GL_RGB32F: case GL_RGB32I: case GL_RGB32UI: size = 12; break; /* 128 */ case GL_RGBA32F: case GL_RGBA32I: case GL_RGBA32UI: size = 16; break; default: TCU_FAIL("Invalid enum"); break; } return size; } /** Prepare string that represents bytes of pixel * * @param internal_format Format * @param pixel Pixel data * * @return String **/ std::string Utils::getPixelString(GLenum internal_format, const GLubyte* pixel) { const GLuint pixel_size = Utils::getPixelSizeForFormat(internal_format); std::stringstream stream; stream << "0x"; for (GLint i = pixel_size - 1; i >= 0; --i) { stream << std::setbase(16) << std::setw(2) << std::setfill('0') << (GLuint)pixel[i]; } return stream.str(); } /** Check if target supports multiple layers * * @param target Texture target * * @return true if target is multilayered **/ bool Utils::isTargetMultilayer(GLenum target) { bool result = false; switch (target) { case GL_TEXTURE_1D_ARRAY: case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: case GL_TEXTURE_3D: case GL_TEXTURE_CUBE_MAP_ARRAY: result = true; break; default: break; } return result; } /** Check if target supports multiple level * * @param target Texture target * * @return true if target supports mipmaps **/ bool Utils::isTargetMultilevel(GLenum target) { bool result = true; switch (target) { case GL_TEXTURE_2D_MULTISAMPLE: case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: case GL_TEXTURE_RECTANGLE: case GL_RENDERBUFFER: result = false; break; default: break; } return result; } /** Check if target is multisampled * * @param target Texture target * * @return true when for multisampled formats, false otherwise **/ bool Utils::isTargetMultisampled(GLenum target) { bool result = false; switch (target) { case GL_TEXTURE_2D_MULTISAMPLE: case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: result = true; break; default: break; } return result; } /** Generate texture object * * @param context Test context * @param target Target of texture * * @return Generated name **/ glw::GLuint Utils::generateTexture(deqp::Context& context, GLenum target) { const Functions& gl = context.getRenderContext().getFunctions(); GLuint name = 0; switch (target) { case GL_RENDERBUFFER: gl.genRenderbuffers(1, &name); GLU_EXPECT_NO_ERROR(gl.getError(), "GenRenderbuffers"); break; default: gl.genTextures(1, &name); GLU_EXPECT_NO_ERROR(gl.getError(), "GenTextures"); break; } return name; } /** Sets base and max level parameters of texture to make it complete * * @param context Test context * @param target GLenum representing target of texture that should be created * @param id Id of texture * @param base_level Base level value, eg 0 * @param max_level Max level value, eg 0 **/ void Utils::makeTextureComplete(deqp::Context& context, GLenum target, GLuint id, GLint base_level, GLint max_level) { const Functions& gl = context.getRenderContext().getFunctions(); if (GL_RENDERBUFFER == target) { return; } /* Translate proxies into real targets */ target = transRealToBindTarget(transProxyToRealTarget(target)); gl.bindTexture(target, id); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); /* Set levels */ if (GL_TEXTURE_BUFFER != target) { gl.texParameteri(target, GL_TEXTURE_BASE_LEVEL, base_level); GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri"); gl.texParameteri(target, GL_TEXTURE_MAX_LEVEL, max_level); GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri"); /* Integer textures won't be complete with the default min filter * of GL_NEAREST_MIPMAP_LINEAR (or GL_LINEAR for rectangle textures) * and default mag filter of GL_LINEAR, so switch to nearest. */ if (GL_TEXTURE_2D_MULTISAMPLE != target && GL_TEXTURE_2D_MULTISAMPLE_ARRAY != target) { gl.texParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); if (GL_TEXTURE_RECTANGLE != target) { gl.texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri"); } else { gl.texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri"); } } } /* Clean binding point */ gl.bindTexture(target, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); } /** Generate and initialize texture for given target * * @param context Test context * @param target GLenum representing target of texture that should be created * @param n_samples Number of samples * * @return "name" of texture **/ GLuint Utils::prepareMultisampleTex(deqp::Context& context, GLenum target, GLsizei n_samples) { static const GLuint depth = 6; const Functions& gl = context.getRenderContext().getFunctions(); static const GLuint height = 16; static const GLenum internal_format = GL_RGBA8; GLuint name = 0; static const GLuint width = 16; gl.genTextures(1, &name); GLU_EXPECT_NO_ERROR(gl.getError(), "GenTextures"); /* Initialize */ switch (target) { case GL_TEXTURE_2D_MULTISAMPLE: gl.bindTexture(target, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.texImage2DMultisample(target, n_samples, internal_format, width, height, GL_FALSE /* fixedsamplelocation */); GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage2DMultisample"); break; case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: gl.bindTexture(target, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.texImage3DMultisample(target, n_samples, internal_format, width, height, depth, GL_FALSE /* fixedsamplelocation */); GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage3DMultisample"); break; default: TCU_FAIL("Invalid enum"); break; } /* Clean binding point */ gl.bindTexture(target, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); return name; } /** Generate and initialize texture for given target * * @param context Test context * @param internal_format Internal format of render buffer * * @return "name" of texture **/ GLuint Utils::prepareRenderBuffer(deqp::Context& context, GLenum internal_format) { const Functions& gl = context.getRenderContext().getFunctions(); static const GLuint height = 16; GLuint name = 0; static const GLuint width = 16; gl.genRenderbuffers(1, &name); GLU_EXPECT_NO_ERROR(gl.getError(), "GenRenderbuffers"); /* Initialize */ gl.bindRenderbuffer(GL_RENDERBUFFER, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindRenderbuffer"); gl.renderbufferStorage(GL_RENDERBUFFER, internal_format, width, height); GLU_EXPECT_NO_ERROR(gl.getError(), "RenderbufferStorage"); /* Clean binding point */ gl.bindRenderbuffer(GL_RENDERBUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "BindRenderbuffer"); return name; } /** Generate and initialize texture for given target * * @param context Test context * @param target GLenum representing target of texture that should be created * @param internal_format * @param format * @param type * @param out_buf_id ID of buffer that will be used for TEXTURE_BUFFER * * @return "name" of texture **/ GLuint Utils::prepareTex16x16x6(deqp::Context& context, GLenum target, GLenum internal_format, GLenum format, GLenum type, GLuint& out_buf_id) { static const GLuint depth = 6; static const GLuint height = 16; static const GLuint level = 0; GLuint name = 0; static const GLchar* pixels = 0; static const GLuint width = 16; name = generateTexture(context, target); prepareTexture(context, name, target, internal_format, format, type, level, width, height, depth, pixels, out_buf_id); return name; } /** Initialize texture * * @param context Test context * @param name Name of texture object * @param target GLenum representing target of texture that should be created * @param internal_format * @param format * @param type * @param level * @param width * @param height * @param depth * @param pixels * @param out_buf_id ID of buffer that will be used for TEXTURE_BUFFER * * @return "name" of texture **/ void Utils::prepareTexture(deqp::Context& context, GLuint name, GLenum target, GLenum internal_format, GLenum format, GLenum type, GLuint level, GLuint width, GLuint height, GLuint depth, const GLvoid* pixels, GLuint& out_buf_id) { static const GLint border = 0; GLenum error = 0; const GLchar* function_name = "unknown"; const Functions& gl = context.getRenderContext().getFunctions(); static const GLsizei samples = 1; /* Translate proxies into real targets */ target = transProxyToRealTarget(target); /* Initialize */ switch (target) { case GL_RENDERBUFFER: gl.bindRenderbuffer(target, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindRenderbuffer"); gl.renderbufferStorage(target, internal_format, width, height); GLU_EXPECT_NO_ERROR(gl.getError(), "RenderbufferStorage"); gl.bindRenderbuffer(target, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "BindRenderbuffer"); break; case GL_TEXTURE_1D: gl.bindTexture(target, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.texImage1D(target, level, internal_format, width, border, format, type, pixels); error = gl.getError(); function_name = "TexImage1D"; break; case GL_TEXTURE_1D_ARRAY: case GL_TEXTURE_2D: case GL_TEXTURE_RECTANGLE: gl.bindTexture(target, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.texImage2D(target, level, internal_format, width, height, border, format, type, pixels); error = gl.getError(); function_name = "TexImage2D"; break; case GL_TEXTURE_2D_MULTISAMPLE: gl.bindTexture(target, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.texImage2DMultisample(target, samples, internal_format, width, height, GL_FALSE /* fixedsamplelocation */); error = gl.getError(); function_name = "TexImage2DMultisample"; break; case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: gl.bindTexture(target, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.texImage3DMultisample(target, samples, internal_format, width, height, depth, GL_FALSE /* fixedsamplelocation */); error = gl.getError(); function_name = "TexImage3DMultisample"; break; case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_3D: case GL_TEXTURE_CUBE_MAP_ARRAY: gl.bindTexture(target, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.texImage3D(target, level, internal_format, width, height, depth, border, format, type, pixels); error = gl.getError(); function_name = "TexImage3D"; break; case GL_TEXTURE_BUFFER: gl.genBuffers(1, &out_buf_id); GLU_EXPECT_NO_ERROR(gl.getError(), "GenBuffers"); gl.bindBuffer(GL_TEXTURE_BUFFER, out_buf_id); GLU_EXPECT_NO_ERROR(gl.getError(), "BindBuffer"); { GLsizei size = 16; const GLvoid* data = 0; if (0 != pixels) { size = width; data = pixels; } gl.bufferData(GL_TEXTURE_BUFFER, size, data, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "BufferData"); } gl.bindTexture(GL_TEXTURE_BUFFER, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.texBuffer(GL_TEXTURE_BUFFER, internal_format, out_buf_id); GLU_EXPECT_NO_ERROR(gl.getError(), "TexBuffer"); break; case GL_TEXTURE_CUBE_MAP: case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: case GL_TEXTURE_CUBE_MAP_POSITIVE_X: case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: /* Change target to CUBE_MAP, it will be used later to change base and max level */ target = GL_TEXTURE_CUBE_MAP; gl.bindTexture(target, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, internal_format, width, height, border, format, type, pixels); gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, internal_format, width, height, border, format, type, pixels); gl.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, internal_format, width, height, border, format, type, pixels); gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, internal_format, width, height, border, format, type, pixels); gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, internal_format, width, height, border, format, type, pixels); gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, internal_format, width, height, border, format, type, pixels); error = gl.getError(); function_name = "TexImage2D"; break; default: TCU_FAIL("Invalid enum"); break; } if (GL_NO_ERROR != error) { context.getTestContext().getLog() << tcu::TestLog::Message << "Error: " << glu::getErrorStr(error) << ". Function: " << function_name << ". Target: " << glu::getTextureTargetStr(target) << ". Format: " << glu::getInternalFormatParameterStr(internal_format) << ", " << glu::getTextureFormatName(format) << ", " << glu::getTypeStr(type) << tcu::TestLog::EndMessage; TCU_FAIL("Failed to create texture"); } if (GL_RENDERBUFFER != target) { /* Clean binding point */ gl.bindTexture(target, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); } } /** Translate proxies into real targets * * @param target Target to be converted * * @return Converted target for proxies, otherwise **/ GLenum Utils::transProxyToRealTarget(GLenum target) { switch (target) { case GL_PROXY_TEXTURE_1D: target = GL_TEXTURE_1D; break; case GL_PROXY_TEXTURE_1D_ARRAY: target = GL_TEXTURE_1D_ARRAY; break; case GL_PROXY_TEXTURE_2D: target = GL_TEXTURE_2D; break; case GL_PROXY_TEXTURE_2D_ARRAY: target = GL_TEXTURE_2D_ARRAY; break; case GL_PROXY_TEXTURE_2D_MULTISAMPLE: target = GL_TEXTURE_2D_MULTISAMPLE; break; case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY: target = GL_TEXTURE_2D_MULTISAMPLE_ARRAY; break; case GL_PROXY_TEXTURE_3D: target = GL_TEXTURE_3D; break; case GL_PROXY_TEXTURE_CUBE_MAP: target = GL_TEXTURE_CUBE_MAP; break; case GL_PROXY_TEXTURE_CUBE_MAP_ARRAY: target = GL_TEXTURE_CUBE_MAP_ARRAY; break; case GL_PROXY_TEXTURE_RECTANGLE: target = GL_TEXTURE_RECTANGLE; break; default: break; } return target; } /** Translate real targets into binding targets * * @param target Target to be converted * * @return Converted target for cube map faces, otherwise **/ GLenum Utils::transRealToBindTarget(GLenum target) { switch (target) { case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: target = GL_TEXTURE_CUBE_MAP; break; case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: target = GL_TEXTURE_CUBE_MAP; break; case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: target = GL_TEXTURE_CUBE_MAP; break; case GL_TEXTURE_CUBE_MAP_POSITIVE_X: target = GL_TEXTURE_CUBE_MAP; break; case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: target = GL_TEXTURE_CUBE_MAP; break; case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: target = GL_TEXTURE_CUBE_MAP; break; default: break; } return target; } /** Read value of channel * * @tparam T Type used to store channel value * * @param channel Channel index * @param pixel Pixel data * @param out_value Read value **/ template void readBaseTypeFromUnsignedChannel(GLuint channel, const GLubyte* pixel, GLdouble& out_value) { static const T max = -1; const GLdouble d_max = (GLdouble)max; const T* ptr = (T*)pixel; const T t_value = ptr[channel]; const GLdouble d_value = (GLdouble)t_value; out_value = d_value / d_max; } /** Read value of channel * * @tparam T Type used to store channel value * * @param channel Channel index * @param pixel Pixel data * @param out_value Read value **/ template void readBaseTypeFromSignedChannel(GLuint channel, const GLubyte* pixel, GLdouble& out_value) { static const GLuint n_bytes = sizeof(T); static const GLuint n_bits = 8u * n_bytes; static const T max = (T)((1u << (n_bits - 1u)) - 1u); const GLdouble d_max = (GLdouble)max; const T* ptr = (T*)pixel; const T t_value = ptr[channel]; const GLdouble d_value = (GLdouble)t_value; out_value = d_value / d_max; } /** Read value of channel * * @tparam T Type used to store channel value * * @param channel Channel index * @param pixel Pixel data * @param out_value Read value **/ void readBaseTypeFromFloatChannel(GLuint channel, const GLubyte* pixel, GLdouble& out_value) { const GLfloat* ptr = (const GLfloat*)pixel; const GLfloat t_value = ptr[channel]; const GLdouble d_value = (GLdouble)t_value; out_value = d_value; } /** Read value of channel * * @tparam T Type used to store channel value * * @param channel Channel index * @param pixel Pixel data * @param out_value Read value **/ void readBaseTypeFromHalfFloatChannel(GLuint channel, const GLubyte* pixel, GLdouble& out_value) { const deUint16* ptr = (const deUint16*)pixel; const deUint16 bits = ptr[channel]; tcu::Float16 val(bits); const GLdouble d_value = val.asDouble(); out_value = d_value; } /** Read value of channel * * @tparam T Type used to store pixel * @tparam size_1 Size of channel in bits * @tparam size_2 Size of channel in bits * @tparam size_3 Size of channel in bits * @tparam off_1 Offset of channel in bits * @tparam off_2 Offset of channel in bits * @tparam off_3 Offset of channel in bits * * @param channel Channel index * @param pixel Pixel data * @param out_value Read value **/ template void read3Channel(GLuint channel, const GLubyte* pixel, GLdouble& out_value) { T mask = 0; T max = 0; T off = 0; const T* ptr = (T*)pixel; T result = 0; const T t_value = ptr[0]; static const T max_1 = (1 << size_1) - 1; static const T max_2 = (1 << size_2) - 1; static const T max_3 = (1 << size_3) - 1; switch (channel) { case 0: mask = max_1; max = max_1; off = off_1; break; case 1: mask = max_2; max = max_2; off = off_2; break; case 2: mask = max_3; max = max_3; off = off_3; break; default: TCU_FAIL("Invalid channel"); break; } result = (T)((t_value >> off) & mask); const GLdouble d_max = (GLdouble)max; const GLdouble d_value = (GLdouble)result; out_value = d_value / d_max; } /** Read value of channel * * @tparam T Type used to store pixel * @tparam size_1 Size of channel in bits * @tparam size_2 Size of channel in bits * @tparam size_3 Size of channel in bits * @tparam size_4 Size of channel in bits * @tparam off_1 Offset of channel in bits * @tparam off_2 Offset of channel in bits * @tparam off_3 Offset of channel in bits * @tparam off_4 Offset of channel in bits * * @param channel Channel index * @param pixel Pixel data * @param out_value Read value **/ template void read4Channel(GLuint channel, const GLubyte* pixel, GLdouble& out_value) { T mask = 0; T max = 0; T off = 0; const T* ptr = (T*)pixel; T result = 0; const T t_value = ptr[0]; static const T max_1 = (1 << size_1) - 1; static const T max_2 = (1 << size_2) - 1; static const T max_3 = (1 << size_3) - 1; static const T max_4 = (1 << size_4) - 1; switch (channel) { case 0: mask = max_1; max = max_1; off = off_1; break; case 1: mask = max_2; max = max_2; off = off_2; break; case 2: mask = max_3; max = max_3; off = off_3; break; case 3: mask = max_4; max = max_4; off = off_4; break; default: TCU_FAIL("Invalid channel"); break; } result = (T)((t_value >> off) & mask); const GLdouble d_max = (GLdouble)max; const GLdouble d_value = (GLdouble)result; out_value = d_value / d_max; } /** Read value of channel * * @param channel Channel index * @param pixel Pixel data * @param out_value Read value **/ void read11F_11F_10F_Channel(GLuint channel, const GLubyte* pixel, GLdouble& out_value) { const deUint32* ptr = (deUint32*)pixel; deUint32 val = *ptr; switch (channel) { case 0: { deUint32 bits = (val & 0x000007ff); tcu::Float temp_val(bits); out_value = temp_val.asDouble(); } break; case 1: { deUint32 bits = ((val >> 11) & 0x000007ff); tcu::Float temp_val(bits); out_value = temp_val.asDouble(); } break; case 2: { deUint32 bits = ((val >> 22) & 0x000003ff); tcu::Float temp_val(bits); out_value = temp_val.asDouble(); } break; default: TCU_FAIL("Invalid channel"); break; } } /** Write value of channel * * @tparam T Type used to store pixel * * @param channel Channel index * @param value Value to write * @param pixel Pixel data **/ template void writeBaseTypeToUnsignedChannel(GLuint channel, GLdouble value, GLubyte* pixel) { static const T max = -1; const GLdouble d_max = (GLdouble)max; const GLdouble d_value = value * d_max; const T t_value = (T)d_value; T* ptr = (T*)pixel; ptr[channel] = t_value; } /** Write value of channel * * @tparam T Type used to store pixel * * @param channel Channel index * @param value Value to write * @param pixel Pixel data **/ template void writeBaseTypeToSignedChannel(GLuint channel, GLdouble value, GLubyte* pixel) { static const GLuint n_bytes = sizeof(T); static const GLuint n_bits = 8u * n_bytes; static const T max = (T)((1u << (n_bits - 1u)) - 1u); const GLdouble d_max = (GLdouble)max; const GLdouble d_value = value * d_max; const T t_value = (T)d_value; T* ptr = (T*)pixel; ptr[channel] = t_value; } /** Write value of channel * * @param channel Channel index * @param value Value to write * @param pixel Pixel data **/ void writeBaseTypeToFloatChannel(GLuint channel, GLdouble value, GLubyte* pixel) { const GLfloat t_value = (GLfloat)value; GLfloat* ptr = (GLfloat*)pixel; ptr[channel] = t_value; } /** Write value of channel * * @param channel Channel index * @param value Value to write * @param pixel Pixel data **/ void writeBaseTypeToHalfFloatChannel(GLuint channel, GLdouble value, GLubyte* pixel) { deUint16* ptr = (deUint16*)pixel; tcu::Float16 val(value); ptr[channel] = val.bits(); } /** Write value of channel * * @tparam T Type used to store pixel * @tparam size_1 Size of channel in bits * @tparam size_2 Size of channel in bits * @tparam size_3 Size of channel in bits * @tparam off_1 Offset of channel in bits * @tparam off_2 Offset of channel in bits * @tparam off_3 Offset of channel in bits * * @param channel Channel index * @param value Value to write * @param pixel Pixel data **/ template void write3Channel(GLuint channel, GLdouble value, GLubyte* pixel) { T mask = 0; T max = 0; T off = 0; T* ptr = (T*)pixel; T result = 0; static const T max_1 = (1 << size_1) - 1; static const T max_2 = (1 << size_2) - 1; static const T max_3 = (1 << size_3) - 1; switch (channel) { case 0: mask = max_1; max = max_1; off = off_1; break; case 1: mask = max_2; max = max_2; off = off_2; break; case 2: mask = max_3; max = max_3; off = off_3; break; default: TCU_FAIL("Invalid channel"); break; } const GLdouble d_max = (GLdouble)max; const GLdouble d_value = value * d_max; const T t_value = (T)d_value; result = (T)((t_value & mask) << off); *ptr |= result; } /** Write value of channel * * @tparam T Type used to store pixel * @tparam size_1 Size of channel in bits * @tparam size_2 Size of channel in bits * @tparam size_3 Size of channel in bits * @tparam size_4 Size of channel in bits * @tparam off_1 Offset of channel in bits * @tparam off_2 Offset of channel in bits * @tparam off_3 Offset of channel in bits * @tparam off_4 Offset of channel in bits * * @param channel Channel index * @param value Value to write * @param pixel Pixel data **/ template void write4Channel(GLuint channel, GLdouble value, GLubyte* pixel) { T mask = 0; T max = 0; T off = 0; T* ptr = (T*)pixel; T result = 0; static const T max_1 = (1 << size_1) - 1; static const T max_2 = (1 << size_2) - 1; static const T max_3 = (1 << size_3) - 1; static const T max_4 = (1 << size_4) - 1; switch (channel) { case 0: mask = max_1; max = max_1; off = off_1; break; case 1: mask = max_2; max = max_2; off = off_2; break; case 2: mask = max_3; max = max_3; off = off_3; break; case 3: mask = max_4; max = max_4; off = off_4; break; default: TCU_FAIL("Invalid channel"); break; } const GLdouble d_max = (GLdouble)max; const GLdouble d_value = value * d_max; const T t_value = (T)d_value; result = (T)((t_value & mask) << off); *ptr |= result; } /** Write value of channel * * @param channel Channel index * @param value Value to write * @param pixel Pixel data **/ void write11F_11F_10F_Channel(GLuint channel, GLdouble value, GLubyte* pixel) { deUint32* ptr = (deUint32*)pixel; switch (channel) { case 0: { tcu::Float val(value); deUint32 bits = val.bits(); *ptr |= bits; } break; case 1: { tcu::Float val(value); deUint32 bits = val.bits(); *ptr |= (bits << 11); } break; case 2: { tcu::Float val(value); deUint32 bits = val.bits(); *ptr |= (bits << 22); } break; default: TCU_FAIL("Invalid channel"); break; } } /** Read value of channel * * @param type Type used by pixel * @param channel Channel index * @param pixel Pixel data * @param value Read value **/ void Utils::readChannel(GLenum type, GLuint channel, const GLubyte* pixel, GLdouble& value) { switch (type) { /* Base types */ case GL_UNSIGNED_BYTE: readBaseTypeFromUnsignedChannel(channel, pixel, value); break; case GL_UNSIGNED_SHORT: readBaseTypeFromUnsignedChannel(channel, pixel, value); break; case GL_UNSIGNED_INT: readBaseTypeFromUnsignedChannel(channel, pixel, value); break; case GL_BYTE: readBaseTypeFromSignedChannel(channel, pixel, value); break; case GL_SHORT: readBaseTypeFromSignedChannel(channel, pixel, value); break; case GL_INT: readBaseTypeFromSignedChannel(channel, pixel, value); break; case GL_HALF_FLOAT: readBaseTypeFromHalfFloatChannel(channel, pixel, value); break; case GL_FLOAT: readBaseTypeFromFloatChannel(channel, pixel, value); break; /* Complicated */ /* 3 channles */ case GL_UNSIGNED_BYTE_3_3_2: read3Channel(channel, pixel, value); break; case GL_UNSIGNED_SHORT_5_6_5: read3Channel(channel, pixel, value); break; /* 4 channels */ case GL_UNSIGNED_SHORT_4_4_4_4: read4Channel(channel, pixel, value); break; case GL_UNSIGNED_SHORT_5_5_5_1: read4Channel(channel, pixel, value); break; case GL_UNSIGNED_INT_2_10_10_10_REV: read4Channel(3 - channel, pixel, value); break; case GL_UNSIGNED_INT_5_9_9_9_REV: read4Channel(3 - channel, pixel, value); break; /* R11F_G11F_B10F - uber complicated */ case GL_UNSIGNED_INT_10F_11F_11F_REV: read11F_11F_10F_Channel(channel, pixel, value); break; default: TCU_FAIL("Invalid enum"); break; } } /** Write value of channel * * @param type Type used by pixel * @param channel Channel index * @param value Value to write * @param pixel Pixel data **/ void Utils::writeChannel(GLenum type, GLuint channel, GLdouble value, GLubyte* pixel) { switch (type) { /* Base types */ case GL_UNSIGNED_BYTE: writeBaseTypeToUnsignedChannel(channel, value, pixel); break; case GL_UNSIGNED_SHORT: writeBaseTypeToUnsignedChannel(channel, value, pixel); break; case GL_UNSIGNED_INT: writeBaseTypeToUnsignedChannel(channel, value, pixel); break; case GL_BYTE: writeBaseTypeToSignedChannel(channel, value, pixel); break; case GL_SHORT: writeBaseTypeToSignedChannel(channel, value, pixel); break; case GL_INT: writeBaseTypeToSignedChannel(channel, value, pixel); break; case GL_HALF_FLOAT: writeBaseTypeToHalfFloatChannel(channel, value, pixel); break; case GL_FLOAT: writeBaseTypeToFloatChannel(channel, value, pixel); break; /* Complicated */ /* 3 channles */ case GL_UNSIGNED_BYTE_3_3_2: write3Channel(channel, value, pixel); break; case GL_UNSIGNED_SHORT_5_6_5: write3Channel(channel, value, pixel); break; /* 4 channels */ case GL_UNSIGNED_SHORT_4_4_4_4: write4Channel(channel, value, pixel); break; case GL_UNSIGNED_SHORT_5_5_5_1: write4Channel(channel, value, pixel); break; case GL_UNSIGNED_INT_2_10_10_10_REV: write4Channel(3 - channel, value, pixel); break; case GL_UNSIGNED_INT_5_9_9_9_REV: write4Channel(3 - channel, value, pixel); break; /* R11F_G11F_B10F - uber complicated */ case GL_UNSIGNED_INT_10F_11F_11F_REV: write11F_11F_10F_Channel(channel, value, pixel); break; default: TCU_FAIL("Invalid enum"); break; } } /** Packs given channels to pixel * * @param internal_format Internal format of image * @param type Type used by image * @param red Red channel * @param green Green channel * @param blue Blue channel * @param alpha Alpha channel * @param out_pixel Pixel data **/ void Utils::packPixel(GLenum internal_format, GLenum type, GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha, GLubyte* out_pixel) { switch (internal_format) { case GL_R8: case GL_R8_SNORM: case GL_R16: case GL_R16F: case GL_R16_SNORM: case GL_R32F: case GL_R8I: case GL_R8UI: case GL_R16I: case GL_R16UI: case GL_R32I: case GL_R32UI: writeChannel(type, 0, red, out_pixel); break; case GL_RG8: case GL_RG8_SNORM: case GL_RG16: case GL_RG16F: case GL_RG16_SNORM: case GL_RG32F: case GL_RG8I: case GL_RG8UI: case GL_RG16I: case GL_RG16UI: case GL_RG32I: case GL_RG32UI: writeChannel(type, 0, red, out_pixel); writeChannel(type, 1, green, out_pixel); break; case GL_R3_G3_B2: case GL_RGB4: case GL_RGB5: case GL_RGB8: case GL_RGB8_SNORM: case GL_RGB10: case GL_R11F_G11F_B10F: case GL_RGB12: case GL_RGB16: case GL_RGB16F: case GL_RGB16_SNORM: case GL_RGB32F: case GL_RGB8I: case GL_RGB8UI: case GL_RGB16I: case GL_RGB16UI: case GL_RGB32I: case GL_RGB32UI: writeChannel(type, 0, red, out_pixel); writeChannel(type, 1, green, out_pixel); writeChannel(type, 2, blue, out_pixel); break; case GL_RGB9_E5: case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1: case GL_RGBA8: case GL_RGBA8_SNORM: case GL_RGB10_A2: case GL_RGBA12: case GL_RGBA16: case GL_RGBA16F: case GL_RGBA16_SNORM: case GL_RGBA32F: case GL_RGBA8I: case GL_RGBA8UI: case GL_RGB10_A2UI: case GL_RGBA16I: case GL_RGBA16UI: case GL_RGBA32I: case GL_RGBA32UI: writeChannel(type, 0, red, out_pixel); writeChannel(type, 1, green, out_pixel); writeChannel(type, 2, blue, out_pixel); writeChannel(type, 3, alpha, out_pixel); break; default: TCU_FAIL("Invalid enum"); break; } } /** Unpacks channels from pixel * * @param internal_format Internal format of image * @param type Type used by image * @param pixel Pixel data * @param red Red channel * @param green Green channel * @param blue Blue channel * @param alpha Alpha channel **/ void Utils::unpackPixel(GLenum format, GLenum type, const GLubyte* pixel, GLdouble& out_red, GLdouble& out_green, GLdouble& out_blue, GLdouble& out_alpha) { switch (format) { case GL_RED: case GL_RED_INTEGER: readChannel(type, 0, pixel, out_red); out_green = 1.0; out_blue = 1.0; out_alpha = 1.0; break; case GL_RG: case GL_RG_INTEGER: readChannel(type, 0, pixel, out_red); readChannel(type, 1, pixel, out_green); out_blue = 1.0; out_alpha = 1.0; break; case GL_RGB: case GL_RGB_INTEGER: switch (type) { case GL_UNSIGNED_INT_5_9_9_9_REV: readChannel(type, 0, pixel, out_red); readChannel(type, 1, pixel, out_green); readChannel(type, 2, pixel, out_blue); readChannel(type, 3, pixel, out_alpha); break; default: readChannel(type, 0, pixel, out_red); readChannel(type, 1, pixel, out_green); readChannel(type, 2, pixel, out_blue); out_alpha = 1.0; break; } break; case GL_RGBA: case GL_RGBA_INTEGER: readChannel(type, 0, pixel, out_red); readChannel(type, 1, pixel, out_green); readChannel(type, 2, pixel, out_blue); readChannel(type, 3, pixel, out_alpha); break; default: TCU_FAIL("Invalid enum"); break; } } inline bool Utils::roundComponent(GLenum internal_format, GLenum component, GLdouble& value) { int exponent = (internal_format == GL_RGB4 ? 4 : (internal_format == GL_RGB5 ? 5 : 0)); if (!exponent) return false; //Currently this only happens with GL_RGB4 and GL_RGB5 when stored as 565 type. int rounded_value = static_cast(floor((pow(2, exponent) - 1) * value + 0.5)); int multiplier = (component == GL_GREEN ? 2 : 1); if (internal_format == GL_RGB4) { multiplier *= 2; } value = rounded_value * multiplier; return true; } /** Unpacks pixels and compars them * * @param left_format Format of left image * @param left_type Type of left image * @param left_internal_format Internal format of left image * @param left_pixel Data of left pixel * @param right_format Format of right image * @param right_type Type of right image * @param right_internal_format Internal format of right image * @param right_pixel Data of right pixel * * @return true if pixels match, false otherwise **/ bool Utils::unpackAndComaprePixels(GLenum left_format, GLenum left_type, GLenum left_internal_format, const GLubyte* left_pixel, GLenum right_format, GLenum right_type, GLenum right_internal_format, const GLubyte* right_pixel) { GLdouble left_red; GLdouble left_green; GLdouble left_blue; GLdouble left_alpha; GLdouble right_red; GLdouble right_green; GLdouble right_blue; GLdouble right_alpha; unpackPixel(left_format, left_type, left_pixel, left_red, left_green, left_blue, left_alpha); unpackPixel(right_format, right_type, right_pixel, right_red, right_green, right_blue, right_alpha); roundComponent(left_internal_format, GL_RED, left_red); roundComponent(left_internal_format, GL_GREEN, left_green); roundComponent(left_internal_format, GL_BLUE, left_blue); roundComponent(right_internal_format, GL_RED, right_red); roundComponent(right_internal_format, GL_GREEN, right_green); roundComponent(right_internal_format, GL_BLUE, right_blue); return comparePixels(left_internal_format, left_red, left_green, left_blue, left_alpha, right_internal_format, right_red, right_green, right_blue, right_alpha); } /* FunctionalTest */ #define FUNCTIONAL_TEST_N_LAYERS 12 #define FUNCTIONAL_TEST_N_LEVELS 3 /** Constructor * * @param context Text context **/ FunctionalTest::FunctionalTest(deqp::Context& context) : TestCase(context, "functional", "Test verifies CopyImageSubData copy data as requested") , m_dst_buf_name(0) , m_dst_tex_name(0) , m_rb_name(0) , m_src_buf_name(0) , m_src_tex_name(0) , m_test_case_index(0) { for (GLuint src_tgt_id = 0; src_tgt_id < s_n_valid_targets; ++src_tgt_id) { const GLenum src_target = s_valid_targets[src_tgt_id]; #if COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_TARGETS == 0 if ((GL_TEXTURE_1D == src_target) || (GL_TEXTURE_1D_ARRAY == src_target) || (GL_TEXTURE_2D == src_target) || (GL_TEXTURE_CUBE_MAP == src_target) || (GL_TEXTURE_CUBE_MAP_ARRAY == src_target)) { continue; } #endif /* COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_TARGETS == 0 */ for (GLuint dst_tgt_id = 0; dst_tgt_id < s_n_valid_targets; ++dst_tgt_id) { const GLenum dst_target = s_valid_targets[dst_tgt_id]; #if COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_TARGETS == 0 if ((GL_TEXTURE_1D == dst_target) || (GL_TEXTURE_1D_ARRAY == dst_target) || (GL_TEXTURE_2D == dst_target) || (GL_TEXTURE_CUBE_MAP == dst_target) || (GL_TEXTURE_CUBE_MAP_ARRAY == dst_target)) { continue; } #endif /* COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_TARGETS == 0 */ /* Skip render buffer as destination */ if (GL_RENDERBUFFER == dst_target) { continue; } /* Skip multisampled */ if ((true == Utils::isTargetMultisampled(src_target)) || (true == Utils::isTargetMultisampled(dst_target))) { continue; } for (GLuint src_frmt_id = 0; src_frmt_id < s_n_internal_formats; ++src_frmt_id) { const GLenum src_format = s_internal_formats[src_frmt_id]; if (src_format == GL_RGB9_E5 && src_target == GL_RENDERBUFFER) { continue; } for (GLuint dst_frmt_id = 0; dst_frmt_id < s_n_internal_formats; ++dst_frmt_id) { const GLenum dst_format = s_internal_formats[dst_frmt_id]; /* Skip not compatible formats */ if (false == Utils::areFormatsCompatible(src_format, dst_format)) { continue; } prepareTestCases(dst_format, dst_target, src_format, src_target); } } } } } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult FunctionalTest::iterate() { GLubyte* dst_pixels[FUNCTIONAL_TEST_N_LEVELS] = { 0 }; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; GLubyte* src_pixels[FUNCTIONAL_TEST_N_LEVELS] = { 0 }; const testCase& test_case = m_test_cases[m_test_case_index]; gl.pixelStorei(GL_PACK_ALIGNMENT, 1); gl.pixelStorei(GL_UNPACK_ALIGNMENT, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "PixelStorei"); try { /* Prepare pixels */ prepareDstPxls(test_case.m_dst, dst_pixels); prepareSrcPxls(test_case.m_src, test_case.m_dst.m_internal_format, src_pixels); /* Prepare textures */ m_dst_tex_name = prepareTexture(test_case.m_dst, (const GLubyte**)dst_pixels, m_dst_buf_name); if (GL_RENDERBUFFER == test_case.m_src.m_target) { targetDesc desc = test_case.m_src; desc.m_target = GL_TEXTURE_2D; m_rb_name = prepareTexture(test_case.m_src, (const GLubyte**)src_pixels, m_src_buf_name); m_src_tex_name = prepareTexture(desc, (const GLubyte**)src_pixels, m_src_buf_name); } else { m_src_tex_name = prepareTexture(test_case.m_src, (const GLubyte**)src_pixels, m_src_buf_name); } /* Copy images and verify results */ result = copyAndVerify(test_case, (const GLubyte**)dst_pixels, (const GLubyte**)src_pixels); } catch (tcu::Exception& exc) { clean(); cleanPixels((GLubyte**)dst_pixels); cleanPixels((GLubyte**)src_pixels); throw exc; } /* Free resources */ clean(); cleanPixels((GLubyte**)dst_pixels); cleanPixels((GLubyte**)src_pixels); /* Set result */ if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. " << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Calculate dimmensions of all levels based on size of specific level * * @param target Target of image * @param level Level index * @param width Width of image at * @param height Height of image at * @param out_widths Calcualted widths, array of FUNCTIONAL_TEST_N_LEVELS'th elements * @param out_heights Calculated heights, array of FUNCTIONAL_TEST_N_LEVELS'th elements * @param out_depths Calculated dephts, array of FUNCTIONAL_TEST_N_LEVELS'th elements **/ void FunctionalTest::calculateDimmensions(GLenum target, GLuint level, GLuint width, GLuint height, GLuint* out_widths, GLuint* out_heights, GLuint* out_depths) const { GLuint divide = 100; GLuint factors[FUNCTIONAL_TEST_N_LEVELS]; GLuint factor = divide; const bool is_multi_layer = Utils::isTargetMultilayer(target); const GLuint n_layers = (true == is_multi_layer) ? FUNCTIONAL_TEST_N_LAYERS : 1; for (GLint i = (GLint)level; i >= 0; --i) { factors[i] = factor; factor *= 2; } factor = divide / 2; for (GLuint i = level + 1; i < FUNCTIONAL_TEST_N_LEVELS; ++i) { factors[i] = factor; factor /= 2; } for (GLuint i = 0; i < FUNCTIONAL_TEST_N_LEVELS; ++i) { out_widths[i] = width * factors[i] / divide; out_heights[i] = height * factors[i] / divide; if (GL_TEXTURE_3D == target) { out_depths[i] = FUNCTIONAL_TEST_N_LAYERS * factors[i] / divide; } else { out_depths[i] = n_layers; } } } /** Execute copyImageSubData for given test case and verify results * * @param test_case Test case * @param dst_pixels Data of destination image * @param src_pixels Data of source image * * @return true if there is no error and results match expectations, false otherwise **/ bool FunctionalTest::copyAndVerify(const testCase& test_case, const GLubyte** dst_pixels, const GLubyte** src_pixels) { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); GLuint region_depth = 1; GLuint dst_layer_step = 0; const bool is_dst_multi_layer = Utils::isTargetMultilayer(test_case.m_dst.m_target); const bool is_src_multi_layer = Utils::isTargetMultilayer(test_case.m_src.m_target); bool result = false; GLuint src_layer_step = 0; GLuint n_layers = 1; /* Configure layers */ if ((true == is_dst_multi_layer) || (true == is_src_multi_layer)) { if (is_src_multi_layer == is_dst_multi_layer) { /* Both objects are multilayered, copy all layers at once, verify at once */ region_depth = FUNCTIONAL_TEST_N_LAYERS; } else if (true == is_dst_multi_layer) { /* Destination is multilayered, copy each layer separetly, verify at once */ n_layers = FUNCTIONAL_TEST_N_LAYERS; dst_layer_step = 1; } else { /* Destination is multilayered, copy and verify each layer separetly */ n_layers = FUNCTIONAL_TEST_N_LAYERS; src_layer_step = 1; } } /* Copy and verification */ { GLuint dst_layer = 0; GLuint src_layer = 0; /* For each layer */ for (GLuint layer = 0; layer < n_layers; ++layer) { if (0 == m_rb_name) { gl.copyImageSubData(m_src_tex_name, test_case.m_src.m_target, test_case.m_src.m_level, test_case.m_src_x, test_case.m_src_y, src_layer, m_dst_tex_name, test_case.m_dst.m_target, test_case.m_dst.m_level, test_case.m_dst_x, test_case.m_dst_y, dst_layer, test_case.m_width, test_case.m_height, region_depth); } else /* Copy from src to rb and from rb to dst */ { /* Src and rb shares differs only on target */ gl.copyImageSubData(m_src_tex_name, GL_TEXTURE_2D, test_case.m_src.m_level, test_case.m_src_x, test_case.m_src_y, src_layer, m_rb_name, test_case.m_src.m_target, test_case.m_src.m_level, test_case.m_src_x, test_case.m_src_y, src_layer, test_case.m_width, test_case.m_height, region_depth); gl.copyImageSubData(m_rb_name, test_case.m_src.m_target, test_case.m_src.m_level, test_case.m_src_x, test_case.m_src_y, src_layer, m_dst_tex_name, test_case.m_dst.m_target, test_case.m_dst.m_level, test_case.m_dst_x, test_case.m_dst_y, dst_layer, test_case.m_width, test_case.m_height, region_depth); } /* Verify generated error */ error = gl.getError(); if (GL_NO_ERROR == error) { /* Verify copy results */ result = verify(test_case, dst_layer, dst_pixels, src_layer, src_pixels, region_depth); } if ((GL_NO_ERROR != error) || (false == result)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Targets src: " << glu::getTextureTargetStr(test_case.m_src.m_target) << ", dst: " << glu::getTextureTargetStr(test_case.m_dst.m_target) << ". Levels src: " << test_case.m_src.m_level << ", dst: " << test_case.m_dst.m_level << ". Dimmensions src [" << test_case.m_src.m_width << ", " << test_case.m_src.m_height << "], dst [" << test_case.m_dst.m_width << ", " << test_case.m_dst.m_height << "]. Region [" << test_case.m_width << " x " << test_case.m_height << " x " << region_depth << "] from [" << test_case.m_src_x << ", " << test_case.m_src_y << ", " << src_layer << "] to [" << test_case.m_dst_x << ", " << test_case.m_dst_y << ", " << dst_layer << "]. Format src: " << glu::getInternalFormatParameterStr(test_case.m_src.m_internal_format) << ", dst: " << glu::getInternalFormatParameterStr(test_case.m_dst.m_internal_format) << tcu::TestLog::EndMessage; if (GL_NO_ERROR != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failed due to error: " << glu::getErrorStr(error) << tcu::TestLog::EndMessage; TCU_FAIL("Copy operation failed"); } return false; } /* Step one layer */ dst_layer += dst_layer_step; src_layer += src_layer_step; } } return true; } /** Cleans resources * **/ void FunctionalTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); /* Clean textures and buffers. Errors ignored */ gl.deleteTextures(1, &m_dst_tex_name); gl.deleteTextures(1, &m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; if (0 != m_dst_buf_name) { gl.deleteBuffers(1, &m_dst_buf_name); m_dst_buf_name = 0; } if (0 != m_rb_name) { gl.deleteRenderbuffers(1, &m_rb_name); m_rb_name = 0; } if (0 != m_src_buf_name) { gl.deleteBuffers(1, &m_src_buf_name); m_src_buf_name = 0; } } /** Free memory allocated for images * * @param pixels Array of pointers to image data **/ void FunctionalTest::cleanPixels(GLubyte** pixels) const { for (GLuint i = 0; i < FUNCTIONAL_TEST_N_LEVELS; ++i) { if (0 != pixels[i]) { delete[] pixels[i]; pixels[i] = 0; } } } /** Compare two images * @param left_desc Descriptor of left image * @param left_data Data of left image * @param left_x X of left image * @param left_y Y of left image * @param left_layer Layer of left image * @param left_level Level of left image * @param right_desc Descriptor of right image * @param right_data Data of right image * @param right_x X of right image * @param right_y Y of right image * @param right_layer Layer of right image * @param right_level Level of right image * @param region_width Width of region to compare * @param region_height Height of region to compare * * @return true if images are considered idenctial, false otherwise **/ bool FunctionalTest::compareImages(const targetDesc& left_desc, const GLubyte* left_data, GLuint left_x, GLuint left_y, GLuint left_layer, GLuint left_level, const targetDesc& right_desc, const glw::GLubyte* right_data, GLuint right_x, GLuint right_y, GLuint right_layer, GLuint right_level, GLuint region_width, GLuint region_height) const { /* Get level dimmensions */ GLuint left_heights[FUNCTIONAL_TEST_N_LEVELS]; GLuint left_widths[FUNCTIONAL_TEST_N_LEVELS]; GLuint left_depths[FUNCTIONAL_TEST_N_LEVELS]; GLuint right_heights[FUNCTIONAL_TEST_N_LEVELS]; GLuint right_widths[FUNCTIONAL_TEST_N_LEVELS]; GLuint right_depths[FUNCTIONAL_TEST_N_LEVELS]; calculateDimmensions(left_desc.m_target, left_desc.m_level, left_desc.m_width, left_desc.m_height, left_widths, left_heights, left_depths); calculateDimmensions(right_desc.m_target, right_desc.m_level, right_desc.m_width, right_desc.m_height, right_widths, right_heights, right_depths); /* Constants */ /* Dimmensions */ const GLuint left_height = left_heights[left_level]; const GLuint left_width = left_widths[left_level]; const GLuint right_height = right_heights[right_level]; const GLuint right_width = right_widths[right_level]; /* Sizes */ const GLuint left_pixel_size = Utils::getPixelSizeForFormat(left_desc.m_internal_format); const GLuint left_line_size = left_pixel_size * left_width; const GLuint left_layer_size = left_line_size * left_height; const GLuint right_pixel_size = Utils::getPixelSizeForFormat(right_desc.m_internal_format); const GLuint right_line_size = right_pixel_size * right_width; const GLuint right_layer_size = right_line_size * right_height; /* Offsets */ const GLuint left_layer_offset = left_layer_size * left_layer; const GLuint left_reg_line_offset = left_line_size * left_y; const GLuint left_reg_pix_offset = left_pixel_size * left_x; const GLuint right_layer_offset = right_layer_size * right_layer; const GLuint right_reg_line_offset = right_line_size * right_y; const GLuint right_reg_pix_offset = right_pixel_size * right_x; /* Pointers */ const GLubyte* left_layer_data = left_data + left_layer_offset; const GLubyte* right_layer_data = right_data + right_layer_offset; /* For each line of region */ for (GLuint y = 0; y < region_height; ++y) { /* Offsets */ const GLuint left_line_offset = left_reg_line_offset + y * left_line_size; const GLuint right_line_offset = right_reg_line_offset + y * right_line_size; /* Pointers */ const GLubyte* left_line_data = left_layer_data + left_line_offset; const GLubyte* right_line_data = right_layer_data + right_line_offset; /* For each pixel of region */ for (GLuint x = 0; x < region_width; ++x) { /* Offsets */ const GLuint left_pixel_offset = left_reg_pix_offset + x * left_pixel_size; const GLuint right_pixel_offset = right_reg_pix_offset + x * right_pixel_size; /* Pointers */ const GLubyte* left_pixel_data = left_line_data + left_pixel_offset; const GLubyte* right_pixel_data = right_line_data + right_pixel_offset; /* Compare */ if (false == Utils::comparePixels(left_pixel_size, left_pixel_data, right_pixel_size, right_pixel_data)) { if (false == Utils::unpackAndComaprePixels(left_desc.m_format, left_desc.m_type, left_desc.m_internal_format, left_pixel_data, right_desc.m_format, right_desc.m_type, right_desc.m_internal_format, right_pixel_data)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Not matching pixels found. Left: [" << x + left_x << ", " << y + left_y << ", " << left_layer << "] lvl:" << left_level << ", off: " << left_pixel_data - left_data << ", data: " << Utils::getPixelString(left_desc.m_internal_format, left_pixel_data) << ". Right: [" << x + right_x << ", " << y + right_y << ", " << right_layer << "] lvl: " << right_level << ", off: " << right_pixel_data - right_data << ", data: " << Utils::getPixelString(right_desc.m_internal_format, right_pixel_data) << tcu::TestLog::EndMessage; return false; } } } } return true; } /** Prepare regions that should not be modified during test case * * @param test_case Test case descriptor * @param dst_level Level of destination image * @param out_regions Number of regions * @param out_n_regions Regions **/ void FunctionalTest::getCleanRegions(const testCase& test_case, GLuint dst_level, GLuint out_regions[4][4], GLuint& out_n_regions) const { GLuint dst_heights[FUNCTIONAL_TEST_N_LEVELS]; GLuint dst_widths[FUNCTIONAL_TEST_N_LEVELS]; GLuint dst_depths[FUNCTIONAL_TEST_N_LEVELS]; out_n_regions = 0; calculateDimmensions(test_case.m_dst.m_target, dst_level, test_case.m_dst.m_width, test_case.m_dst.m_height, dst_widths, dst_heights, dst_depths); /* Constants */ /* Copied region */ const GLuint reg_x = test_case.m_dst_x; const GLuint reg_y = test_case.m_dst_y; const GLuint reg_w = test_case.m_width; const GLuint reg_h = test_case.m_height; const GLuint reg_r = reg_x + reg_w; const GLuint reg_t = reg_y + reg_h; /* Image */ const GLuint img_w = dst_widths[dst_level]; const GLuint img_h = dst_heights[dst_level]; /* Bottom row */ if (0 != reg_y) { out_regions[out_n_regions][0] = 0; out_regions[out_n_regions][1] = 0; out_regions[out_n_regions][2] = img_w; out_regions[out_n_regions][3] = reg_y; out_n_regions += 1; } /* Left edge */ if (0 != reg_x) { out_regions[out_n_regions][0] = 0; out_regions[out_n_regions][1] = reg_y; out_regions[out_n_regions][2] = reg_x; out_regions[out_n_regions][3] = reg_h; out_n_regions += 1; } /* Right edge */ if (img_w != reg_r) { out_regions[out_n_regions][0] = reg_r; out_regions[out_n_regions][1] = reg_y; out_regions[out_n_regions][2] = img_w - reg_r; out_regions[out_n_regions][3] = reg_h; out_n_regions += 1; } /* Top row */ if (img_h != reg_t) { out_regions[out_n_regions][0] = 0; out_regions[out_n_regions][1] = reg_t; out_regions[out_n_regions][2] = img_w; out_regions[out_n_regions][3] = img_h - reg_t; out_n_regions += 1; } } /** Get pixel data for image * * @param name Name of image * @param desc Descriptor of image * @param level Level to capture * @param out_pixels Pixels **/ void FunctionalTest::getPixels(GLuint name, const targetDesc& desc, GLuint level, GLubyte* out_pixels) const { const Functions& gl = m_context.getRenderContext().getFunctions(); gl.bindTexture(desc.m_target, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.getTexImage(desc.m_target, level, desc.m_format, desc.m_type, out_pixels); GLU_EXPECT_NO_ERROR(gl.getError(), "GetTexImage"); gl.bindTexture(desc.m_target, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); } /** Prepare data for destination image * * @param desc Descriptor * @param out_pixels Array of pointer to image data **/ void FunctionalTest::prepareDstPxls(const FunctionalTest::targetDesc& desc, GLubyte** out_pixels) const { const GLenum internal_format = desc.m_internal_format; const bool is_multi_level = Utils::isTargetMultilevel(desc.m_target); GLuint n_levels = 1; const GLuint pixel_size = Utils::getPixelSizeForFormat(desc.m_internal_format); const GLenum type = desc.m_type; /* Configure levels */ if (true == is_multi_level) { n_levels = FUNCTIONAL_TEST_N_LEVELS; } /* Calculate dimmensions */ GLuint heights[FUNCTIONAL_TEST_N_LEVELS]; GLuint widths[FUNCTIONAL_TEST_N_LEVELS]; GLuint depths[FUNCTIONAL_TEST_N_LEVELS]; calculateDimmensions(desc.m_target, desc.m_level, desc.m_width, desc.m_height, widths, heights, depths); /* Prepare storage */ for (GLuint i = 0; i < n_levels; ++i) { const GLuint req_memory_per_layer = pixel_size * widths[i] * heights[i]; const GLuint req_memory_for_level = req_memory_per_layer * depths[i]; out_pixels[i] = new GLubyte[req_memory_for_level]; if (0 == out_pixels[i]) { TCU_FAIL("Memory allocation failed"); } memset(out_pixels[i], 0, req_memory_for_level); } /* Fill pixels */ for (GLuint i = 0; i < n_levels; ++i) { const GLuint n_layers = depths[i]; const GLuint n_pixels = widths[i] * heights[i]; GLubyte* ptr = (GLubyte*)out_pixels[i]; for (GLuint j = 0; j < n_pixels * n_layers; ++j) { GLubyte* pixel_data = ptr + j * pixel_size; Utils::packPixel(internal_format, type, 1.0, 1.0, 1.0, 1.0, pixel_data); } } } /** Prepare data for source image * * @param desc Descriptor * @param dst_internal_format Internal format of destination image * @param out_pixels Array of pointer to image data **/ void FunctionalTest::prepareSrcPxls(const FunctionalTest::targetDesc& desc, GLenum /* dst_internal_format */, GLubyte** out_pixels) const { const GLenum internal_format = desc.m_internal_format; const bool is_multi_level = Utils::isTargetMultilevel(desc.m_target); GLuint n_levels = 1; const GLuint pixel_size = Utils::getPixelSizeForFormat(desc.m_internal_format); const GLenum type = desc.m_type; /* Configure levels */ if (true == is_multi_level) { n_levels = FUNCTIONAL_TEST_N_LEVELS; } /* Calculate dimmensions */ GLuint heights[FUNCTIONAL_TEST_N_LEVELS]; GLuint widths[FUNCTIONAL_TEST_N_LEVELS]; GLuint depths[FUNCTIONAL_TEST_N_LEVELS]; calculateDimmensions(desc.m_target, desc.m_level, desc.m_width, desc.m_height, widths, heights, depths); /* Prepare storage */ for (GLuint i = 0; i < n_levels; ++i) { const GLuint req_memory_per_layer = pixel_size * widths[i] * heights[i]; const GLuint req_memory_for_level = req_memory_per_layer * depths[i]; out_pixels[i] = new GLubyte[req_memory_for_level]; if (0 == out_pixels[i]) { TCU_FAIL("Memory allocation failed"); } memset(out_pixels[i], 0, req_memory_for_level); } for (GLuint lvl = 0; lvl < n_levels; ++lvl) { const GLuint n_layers = depths[lvl]; const GLuint line_size = pixel_size * widths[lvl]; const GLuint req_memory_per_layer = line_size * heights[lvl]; GLubyte* level = (GLubyte*)out_pixels[lvl]; for (GLuint lay = 0; lay < n_layers; ++lay) { const GLuint layer_offset = lay * req_memory_per_layer; GLubyte* layer = ((GLubyte*)level) + layer_offset; for (GLuint y = 0; y < heights[lvl]; ++y) { const GLuint line_offset = line_size * y; GLubyte* line = layer + line_offset; for (GLuint x = 0; x < widths[lvl]; ++x) { const GLuint pixel_offset = x * pixel_size; GLubyte* pixel = line + pixel_offset; /* 255 is max ubyte. 1/15.9375 = 16/255 */ const GLdouble red = ((GLdouble)x) / 255.0 + (((GLdouble)y) / 15.9375); const GLdouble green = ((GLdouble)lay) / 255.0 + (((GLdouble)lvl) / 15.9375); const GLdouble blue = 0.125; const GLdouble alpha = 1.0; /* This value has special meaning for some internal_formats */ Utils::packPixel(internal_format, type, red, green, blue, alpha, pixel); } } } } } /** Prepare test cases for given targets and internal formats * * @param dst_internal_format Internal format of destination image * @param dst_target Target of destination image * @param src_internal_format Internal format of source image * @param src_target Target of source image **/ void FunctionalTest::prepareTestCases(GLenum dst_internal_format, GLenum dst_target, GLenum src_internal_format, GLenum src_target) { static const GLuint image_dimmensions[] = { 7, #if COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_IMG_DIM 8, 9, 10, 11, 12, 13, 14, #endif /* COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_IMG_DIM */ 15 }; static const GLuint region_dimmensions[] = { 1, #if COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_DIM 2, 3, 4, 5, 6, #endif /* COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_DIM */ 7 }; static const GLuint n_image_dimmensions = sizeof(image_dimmensions) / sizeof(image_dimmensions[0]); static const GLuint n_region_dimmensions = sizeof(region_dimmensions) / sizeof(region_dimmensions[0]); const bool is_dst_multi_level = Utils::isTargetMultilevel(dst_target); const bool is_src_multi_level = Utils::isTargetMultilevel(src_target); const GLenum dst_format = Utils::getFormat(dst_internal_format); const GLuint dst_n_levels = (true == is_dst_multi_level) ? FUNCTIONAL_TEST_N_LEVELS : 1; const GLenum dst_type = Utils::getType(dst_internal_format); const GLenum src_format = Utils::getFormat(src_internal_format); const GLuint src_n_levels = (true == is_src_multi_level) ? FUNCTIONAL_TEST_N_LEVELS : 1; const GLenum src_type = Utils::getType(src_internal_format); for (GLuint src_level = 0; src_level < src_n_levels; ++src_level) { for (GLuint dst_level = 0; dst_level < dst_n_levels; ++dst_level) { for (GLuint src_img_dim_id = 0; src_img_dim_id < n_image_dimmensions; ++src_img_dim_id) { const GLuint src_image_dimmension = image_dimmensions[src_img_dim_id]; for (GLuint dst_img_dim_id = 0; dst_img_dim_id < n_image_dimmensions; ++dst_img_dim_id) { const GLuint dst_image_dimmension = image_dimmensions[dst_img_dim_id]; for (GLuint reg_dim_id = 0; reg_dim_id < n_region_dimmensions; ++reg_dim_id) { const GLuint region_dimmension = region_dimmensions[reg_dim_id]; GLuint dst_coord[3] = { 0, 0, 0 }; const GLuint dst_dim_diff = dst_image_dimmension - region_dimmension; GLuint n_dst_coords = 1; #if COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_POS GLuint n_src_coords = 1; #endif /* COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_POS */ GLuint src_coord[3] = { 0, 0, 0 }; const GLuint src_dim_diff = src_image_dimmension - region_dimmension; /* Calculate coords */ if (1 == dst_dim_diff) { dst_coord[1] = 1; n_dst_coords = 2; } else if (1 < dst_dim_diff) { dst_coord[1] = dst_dim_diff / 2; dst_coord[2] = dst_dim_diff; n_dst_coords = 3; } if (1 == src_dim_diff) { src_coord[1] = 1; #if COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_POS n_src_coords = 2; #endif /* COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_POS */ } else if (1 < src_dim_diff) { src_coord[1] = src_dim_diff / 2; src_coord[2] = src_dim_diff; #if COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_POS n_src_coords = 3; #endif /* COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_POS */ } testCase test_case = { { /* m_dst */ dst_target, dst_image_dimmension, /* width */ dst_image_dimmension, /* height */ dst_level, dst_internal_format, dst_format, dst_type }, 0, /* dst_x */ 0, /* dst_y */ { /* m_src */ src_target, src_image_dimmension, /* width */ src_image_dimmension, /* height */ src_level, src_internal_format, src_format, src_type }, 0, /* src_x */ 0, /* src_y */ region_dimmension, /* width */ region_dimmension, /* height */ }; #if COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_POS for (GLuint src_x = 0; src_x < n_src_coords; ++src_x) { for (GLuint src_y = 0; src_y < n_src_coords; ++src_y) { for (GLuint dst_x = 0; dst_x < n_dst_coords; ++dst_x) { for (GLuint dst_y = 0; dst_y < n_dst_coords; ++dst_y) { test_case.m_dst_x = dst_coord[dst_x]; test_case.m_dst_y = dst_coord[dst_y]; test_case.m_src_x = src_coord[src_x]; test_case.m_src_y = src_coord[src_y]; m_test_cases.push_back(test_case); } } } } #else /* COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_POS */ test_case.m_dst_x = dst_coord[n_dst_coords - 1]; test_case.m_dst_y = dst_coord[n_dst_coords - 1]; test_case.m_src_x = src_coord[0]; test_case.m_src_y = src_coord[0]; m_test_cases.push_back(test_case); #endif /* COPY_IMAGE_FUNCTIONAL_TEST_ENABLE_ALL_REG_POS */ /* Whole image, for non 7x7 */ if ((dst_image_dimmension == src_image_dimmension) && (image_dimmensions[0] != dst_image_dimmension)) { test_case.m_dst_x = 0; test_case.m_dst_y = 0; test_case.m_src_x = 0; test_case.m_src_y = 0; test_case.m_width = dst_image_dimmension; test_case.m_height = dst_image_dimmension; m_test_cases.push_back(test_case); } } } } } } } /** Prepare texture * * @param desc Descriptor * @param pixels Image data * @param out_buf_id Id of buffer used by texture buffer * * @return Name of iamge **/ GLuint FunctionalTest::prepareTexture(const targetDesc& desc, const GLubyte** pixels, GLuint& out_buf_id) { GLuint name = Utils::generateTexture(m_context, desc.m_target); if (false == Utils::isTargetMultilevel(desc.m_target)) { Utils::prepareTexture(m_context, name, desc.m_target, desc.m_internal_format, desc.m_format, desc.m_type, 0 /* level */, desc.m_width, desc.m_height, FUNCTIONAL_TEST_N_LAYERS /* depth - 12 for multilayered, 1D and 2D will ignore that */, pixels[0], out_buf_id); Utils::makeTextureComplete(m_context, desc.m_target, name, 0 /* base */, 0 /* max */); } else { /* Calculate dimmensions */ GLuint heights[FUNCTIONAL_TEST_N_LEVELS]; GLuint widths[FUNCTIONAL_TEST_N_LEVELS]; GLuint depths[FUNCTIONAL_TEST_N_LEVELS]; calculateDimmensions(desc.m_target, desc.m_level, desc.m_width, desc.m_height, widths, heights, depths); for (GLuint level = 0; level < FUNCTIONAL_TEST_N_LEVELS; ++level) { Utils::prepareTexture(m_context, name, desc.m_target, desc.m_internal_format, desc.m_format, desc.m_type, level, widths[level], heights[level], depths[level], pixels[level], out_buf_id); Utils::makeTextureComplete(m_context, desc.m_target, name, 0 /* base */, 2 /* max */); } } return name; } /** Verify copy operation * * @param test_case Test case * @param dst_layer First layer modified by copy * @param dst_pixels Origiranl data of destination image * @param src_layer First layer read by copy * @param src_pixels Original data of source image * @param depth Number of copied layers * * @return true if everything is as expected, false otherwise **/ bool FunctionalTest::verify(const testCase& test_case, GLuint dst_layer, const GLubyte** dst_pixels, GLuint src_layer, const GLubyte** src_pixels, GLuint depth) { const bool is_dst_multi_level = Utils::isTargetMultilevel(test_case.m_dst.m_target); const bool is_src_multi_level = Utils::isTargetMultilevel(test_case.m_src.m_target); const GLuint dst_level = test_case.m_dst.m_level; std::vector dst_level_data; const GLuint dst_pixel_size = Utils::getPixelSizeForFormat(test_case.m_dst.m_internal_format); targetDesc src_desc = test_case.m_src; const GLuint src_level = src_desc.m_level; std::vector src_level_data; const GLuint src_pixel_size = Utils::getPixelSizeForFormat(src_desc.m_internal_format); if (0 != m_rb_name) { src_desc.m_target = GL_TEXTURE_2D; } /* Calculate storage requirements */ GLuint dst_req_mem_per_layer[FUNCTIONAL_TEST_N_LEVELS]; GLuint dst_heights[FUNCTIONAL_TEST_N_LEVELS]; GLuint dst_widths[FUNCTIONAL_TEST_N_LEVELS]; GLuint dst_depths[FUNCTIONAL_TEST_N_LEVELS]; GLuint src_req_mem_per_layer[FUNCTIONAL_TEST_N_LEVELS]; GLuint src_heights[FUNCTIONAL_TEST_N_LEVELS]; GLuint src_widths[FUNCTIONAL_TEST_N_LEVELS]; GLuint src_depths[FUNCTIONAL_TEST_N_LEVELS]; calculateDimmensions(test_case.m_dst.m_target, dst_level, test_case.m_dst.m_width, test_case.m_dst.m_height, dst_widths, dst_heights, dst_depths); calculateDimmensions(src_desc.m_target, src_level, src_desc.m_width, src_desc.m_height, src_widths, src_heights, src_depths); for (GLuint i = 0; i < FUNCTIONAL_TEST_N_LEVELS; ++i) { dst_req_mem_per_layer[i] = dst_widths[i] * dst_heights[i] * dst_pixel_size; src_req_mem_per_layer[i] = src_widths[i] * src_heights[i] * src_pixel_size; } /* Prepare storage, use 0 level as it is the biggest one */ dst_level_data.resize(dst_req_mem_per_layer[0] * dst_depths[0]); src_level_data.resize(src_req_mem_per_layer[0] * src_depths[0]); /* Verification of contents * - source image - expect no modification * - destination image, mipmap before and after dst_level - expect no modification * - destination image, mipmap at dst_level: * * layers after dst_layer + depth - expect no modification * * layers <0, dst_layer + depth> - expect that contents at selected region were copied */ /* Check if source image was not modified */ { const GLuint n_levels = (true == is_src_multi_level) ? FUNCTIONAL_TEST_N_LEVELS : 1; for (GLuint level = 0; level < n_levels; ++level) { getPixels(m_src_tex_name, src_desc, level, &src_level_data[0]); for (GLuint layer = 0; layer < src_depths[level]; ++layer) { if (false == compareImages(src_desc, src_pixels[level], 0, 0, layer, level, src_desc, &src_level_data[0], 0, 0, layer, level, src_widths[level], src_heights[level])) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "CopyImageSubData modified contents of source image. Original data: left." << tcu::TestLog::EndMessage; return false; } } } } /* Check if contents of destination at levels != dst_level were not modified */ { const GLuint n_levels = (true == is_dst_multi_level) ? FUNCTIONAL_TEST_N_LEVELS : 1; for (GLuint level = 0; level < n_levels; ++level) { if (dst_level == level) { continue; } getPixels(m_dst_tex_name, test_case.m_dst, level, &dst_level_data[0]); for (GLuint layer = 0; layer < dst_depths[level]; ++layer) { if (false == compareImages(test_case.m_dst, dst_pixels[level], 0, 0, layer, level, test_case.m_dst, &dst_level_data[0], 0, 0, layer, level, dst_widths[level], dst_heights[level])) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "CopyImageSubData modified contents of wrong mipmap level. Original data: left." << tcu::TestLog::EndMessage; return false; } } } } /* Check contents of modified level */ { getPixels(m_dst_tex_name, test_case.m_dst, dst_level, &dst_level_data[0]); /* Check anything after dst_layer + depth */ { for (GLuint layer = dst_layer + depth; layer < dst_depths[dst_level]; ++layer) { if (false == compareImages(test_case.m_dst, dst_pixels[dst_level], 0, 0, layer, dst_level, test_case.m_dst, &dst_level_data[0], 0, 0, layer, dst_level, dst_widths[dst_level], dst_heights[dst_level])) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "CopyImageSubData modified contents of wrong layer. Original data: left." << tcu::TestLog::EndMessage; return false; } } } /* Check modified layers */ for (GLuint layer = 0; layer < depth; ++layer) { /* Check contents outside of copied region */ { GLuint n_regions = 0; GLuint regions[4][4] = { { 0 } }; getCleanRegions(test_case, dst_level, regions, n_regions); for (GLuint region = 0; region < n_regions; ++region) { const GLuint x = regions[region][0]; const GLuint y = regions[region][1]; const GLuint w = regions[region][2]; const GLuint h = regions[region][3]; if (false == compareImages(test_case.m_dst, dst_pixels[dst_level], x, y, layer + dst_layer, dst_level, test_case.m_dst, &dst_level_data[0], x, y, layer + dst_layer, dst_level, w, h)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "CopyImageSubData modified contents outside of copied region. Original data: left." << tcu::TestLog::EndMessage; return false; } } } /* Check contents of copied region */ if (false == compareImages(test_case.m_dst, &dst_level_data[0], test_case.m_dst_x, test_case.m_dst_y, layer + dst_layer, dst_level, src_desc, src_pixels[src_level], test_case.m_src_x, test_case.m_src_y, layer + src_layer, src_level, test_case.m_width, test_case.m_height)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "CopyImageSubData stored invalid data in copied region. Destination data: left." << tcu::TestLog::EndMessage; return false; } } } return true; } /* SmokeTest */ /* Constants */ const GLuint SmokeTest::m_width = 16; const GLuint SmokeTest::m_height = 16; const GLuint SmokeTest::m_depth = 1; /** Constructor * * @param context Text context **/ SmokeTest::SmokeTest(deqp::Context& context) : TestCase(context, "smoke_test", "Test tries all formats and targets") , m_dst_buf_name(0) , m_dst_tex_name(0) , m_rb_name(0) , m_src_buf_name(0) , m_src_tex_name(0) , m_test_case_index(0) { /* Iterate over valid targets */ for (GLuint tgt_id = 0; tgt_id < s_n_valid_targets; ++tgt_id) { const GLenum target = s_valid_targets[tgt_id]; if (true == Utils::isTargetMultisampled(target)) { continue; } const testCase test_case = { target, GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT }; m_test_cases.push_back(test_case); } /* Iterate over internal formats */ for (GLuint fmt_id = 0; fmt_id < s_n_internal_formats; ++fmt_id) { const GLenum internal_format = s_internal_formats[fmt_id]; const GLenum format = Utils::getFormat(internal_format); const GLenum type = Utils::getType(internal_format); const testCase test_case = { GL_TEXTURE_2D, internal_format, format, type }; m_test_cases.push_back(test_case); } } /** Cleans resources * **/ void SmokeTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); /* Clean textures and buffers. Errors ignored */ gl.deleteTextures(1, &m_dst_tex_name); gl.deleteTextures(1, &m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; if (0 != m_dst_buf_name) { gl.deleteBuffers(1, &m_dst_buf_name); m_dst_buf_name = 0; } if (0 != m_rb_name) { gl.deleteRenderbuffers(1, &m_rb_name); m_rb_name = 0; } if (0 != m_src_buf_name) { gl.deleteBuffers(1, &m_src_buf_name); m_src_buf_name = 0; } } /** Free memory allocated for images * * @param pixels Pointers to image data **/ void SmokeTest::cleanPixels(GLubyte*& pixels) const { if (0 == pixels) { return; } delete[] pixels; pixels = 0; } /** Compare two images * @param test_case Test case descriptor * @param left_data Data of left image * @param right_data Data of right image * * @return true if images are considered idenctial, false otherwise **/ bool SmokeTest::compareImages(const testCase& test_case, const GLubyte* left_data, const GLubyte* right_data) const { /* Constants */ /* Sizes */ const GLuint pixel_size = Utils::getPixelSizeForFormat(test_case.m_internal_format); const GLuint line_size = pixel_size * m_width; GLuint height = m_height; if ((GL_TEXTURE_1D == test_case.m_target) || (GL_TEXTURE_1D_ARRAY == test_case.m_target)) { height = 1; } /* For each line */ for (GLuint y = 0; y < height; ++y) { /* Offsets */ const GLuint line_offset = y * line_size; /* Pointers */ const GLubyte* left_line_data = left_data + line_offset; const GLubyte* right_line_data = right_data + line_offset; /* For each pixel of region */ for (GLuint x = 0; x < m_width; ++x) { /* Offsets */ const GLuint pixel_offset = x * pixel_size; /* Pointers */ const GLubyte* left_pixel_data = left_line_data + pixel_offset; const GLubyte* right_pixel_data = right_line_data + pixel_offset; /* Compare */ if (false == Utils::comparePixels(pixel_size, left_pixel_data, pixel_size, right_pixel_data)) { if (false == Utils::unpackAndComaprePixels( test_case.m_format, test_case.m_type, test_case.m_internal_format, left_pixel_data, test_case.m_format, test_case.m_type, test_case.m_internal_format, right_pixel_data)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Not matching pixels found. " << "[" << x << ", " << y << "], off: " << left_pixel_data - left_data << ". Data left: " << Utils::getPixelString(test_case.m_internal_format, left_pixel_data) << ", right: " << Utils::getPixelString(test_case.m_internal_format, right_pixel_data) << tcu::TestLog::EndMessage; return false; } } } } return true; } /** Execute copyImageSubData for given test case and verify results * * @param test_case Test case * @param src_pixels Data of source image * * @return true if there is no error and results match expectations, false otherwise **/ bool SmokeTest::copyAndVerify(const testCase& test_case, const GLubyte* src_pixels) { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); bool result = false; /* Copy and verification */ { if (0 == m_rb_name) { GLuint height = m_height; if ((GL_TEXTURE_1D == test_case.m_target) || (GL_TEXTURE_1D_ARRAY == test_case.m_target)) { height = 1; } gl.copyImageSubData(m_src_tex_name, test_case.m_target, 0 /* srcLevel */, 0 /* srcX */, 0 /* srcY */, 0 /* srcZ */, m_dst_tex_name, test_case.m_target, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, m_width, height, m_depth); } else /* Copy from src to rb and from rb to dst */ { /* Src and rb shares differs only on target */ gl.copyImageSubData(m_src_tex_name, GL_TEXTURE_2D, 0 /* srcLevel */, 0 /* srcX */, 0 /* srcY */, 0 /* srcZ */, m_rb_name, test_case.m_target, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, m_width, m_height, m_depth); gl.copyImageSubData(m_rb_name, test_case.m_target, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, m_dst_tex_name, GL_TEXTURE_2D, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, m_width, m_height, m_depth); } /* Verify generated error */ error = gl.getError(); if (GL_NO_ERROR == error) { /* Verify copy results */ result = verify(test_case, src_pixels); } if ((GL_NO_ERROR != error) || (false == result)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Target: " << glu::getTextureTargetStr(test_case.m_target) << ". Format: " << glu::getInternalFormatParameterStr(test_case.m_internal_format) << tcu::TestLog::EndMessage; if (GL_NO_ERROR != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failed due to error: " << glu::getErrorStr(error) << tcu::TestLog::EndMessage; TCU_FAIL("Copy operation failed"); } return false; } } return true; } /** Get pixel data for image * * @param name Name of image * @param test_case Test case descriptor * @param out_pixels Pixels **/ void SmokeTest::getPixels(GLuint name, const SmokeTest::testCase& test_case, GLubyte* out_pixels) const { const Functions& gl = m_context.getRenderContext().getFunctions(); GLenum tgt_bind = test_case.m_target; GLenum tgt_get = test_case.m_target; if (GL_RENDERBUFFER == test_case.m_target) { tgt_bind = GL_TEXTURE_2D; tgt_get = GL_TEXTURE_2D; } else if (GL_TEXTURE_CUBE_MAP == test_case.m_target) { tgt_get = GL_TEXTURE_CUBE_MAP_POSITIVE_X; } gl.bindTexture(tgt_bind, name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.getTexImage(tgt_get, 0 /* level */, test_case.m_format, test_case.m_type, out_pixels); GLU_EXPECT_NO_ERROR(gl.getError(), "GetTexImage"); gl.bindTexture(tgt_bind, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult SmokeTest::iterate() { GLubyte* dst_pixels = 0; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; GLubyte* src_pixels = 0; const testCase& test_case = m_test_cases[m_test_case_index]; gl.pixelStorei(GL_PACK_ALIGNMENT, 1); gl.pixelStorei(GL_UNPACK_ALIGNMENT, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "PixelStorei"); try { /* Prepare pixels */ prepareDstPxls(test_case, dst_pixels); prepareSrcPxls(test_case, src_pixels); /* Prepare textures */ if (GL_RENDERBUFFER == test_case.m_target) { testCase desc = test_case; GLuint ignored = 0; desc.m_target = GL_TEXTURE_2D; m_rb_name = prepareTexture(test_case, 0 /* pixels */, ignored /* buffer name */); m_dst_tex_name = prepareTexture(desc, dst_pixels, m_dst_buf_name); m_src_tex_name = prepareTexture(desc, src_pixels, m_src_buf_name); } else { m_dst_tex_name = prepareTexture(test_case, dst_pixels, m_dst_buf_name); m_src_tex_name = prepareTexture(test_case, src_pixels, m_src_buf_name); } /* Copy images and verify results */ result = copyAndVerify(test_case, src_pixels); } catch (tcu::Exception& exc) { clean(); cleanPixels(dst_pixels); cleanPixels(src_pixels); throw exc; } /* Free resources */ clean(); cleanPixels(dst_pixels); cleanPixels(src_pixels); /* Set result */ if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. " << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Prepare data for destination image * * @param test_case Test case descriptor * @param out_pixels Pointer to image data **/ void SmokeTest::prepareDstPxls(const SmokeTest::testCase& test_case, GLubyte*& out_pixels) const { static const GLuint n_pixels_per_layer = m_width * m_height; const GLenum internal_format = test_case.m_internal_format; const GLuint n_layers = (GL_TEXTURE_CUBE_MAP_ARRAY != test_case.m_target) ? m_depth : 6; const GLuint n_pixels = n_pixels_per_layer * n_layers; const GLuint pixel_size = Utils::getPixelSizeForFormat(internal_format); const GLuint req_memory = pixel_size * n_pixels; const GLenum type = test_case.m_type; /* Prepare storage */ out_pixels = new GLubyte[req_memory]; if (0 == out_pixels) { TCU_FAIL("Memory allocation failed"); } memset(out_pixels, 0, req_memory); /* Fill pixels */ for (GLuint j = 0; j < n_pixels; ++j) { GLubyte* pixel_data = out_pixels + j * pixel_size; Utils::packPixel(internal_format, type, 1.0, 1.0, 1.0, 1.0, pixel_data); } } /** Prepare data for source image * * @param test_case Test case descriptor * @param out_pixels Pointer to image data **/ void SmokeTest::prepareSrcPxls(const SmokeTest::testCase& test_case, GLubyte*& out_pixels) const { static const GLuint n_pixels_per_layer = m_width * m_height; const GLenum internal_format = test_case.m_internal_format; const GLuint n_layers = (GL_TEXTURE_CUBE_MAP_ARRAY != test_case.m_target) ? m_depth : 6; const GLuint n_pixels = n_pixels_per_layer * n_layers; const GLuint pixel_size = Utils::getPixelSizeForFormat(internal_format); const GLuint layer_size = pixel_size * n_pixels_per_layer; const GLuint line_size = pixel_size * m_width; const GLuint req_memory = pixel_size * n_pixels; const GLenum type = test_case.m_type; /* Prepare storage */ out_pixels = new GLubyte[req_memory]; if (0 == out_pixels) { TCU_FAIL("Memory allocation failed"); } memset(out_pixels, 0, req_memory); /* Fill pixels */ for (GLuint layer = 0; layer < n_layers; ++layer) { const GLuint layer_offset = layer * layer_size; GLubyte* layer_data = out_pixels + layer_offset; for (GLuint y = 0; y < m_height; ++y) { const GLuint line_offset = line_size * y; GLubyte* line_data = layer_data + line_offset; for (GLuint x = 0; x < m_width; ++x) { const GLuint pixel_offset = x * pixel_size; GLubyte* pixel_data = line_data + pixel_offset; /* 255 is max ubyte. 1/15.9375 = 16/255 */ const GLdouble red = ((GLdouble)x) / 255.0 + (((GLdouble)y) / 15.9375); const GLdouble green = ((GLdouble)layer) / 255.0 + (1.0 / 15.9375); const GLdouble blue = 0.125; const GLdouble alpha = 1.0; /* This value has special meaning for some internal_formats */ Utils::packPixel(internal_format, type, red, green, blue, alpha, pixel_data); } } } } /** Prepare texture * * @param desc Descriptor * @param pixels Image data * @param out_buf_id Id of buffer used by texture buffer * * @return Name of iamge **/ GLuint SmokeTest::prepareTexture(const testCase& test_case, const GLubyte* pixels, GLuint& out_buf_id) { const GLuint n_layers = (GL_TEXTURE_CUBE_MAP_ARRAY != test_case.m_target) ? m_depth : 6; GLuint name = Utils::generateTexture(m_context, test_case.m_target); Utils::prepareTexture(m_context, name, test_case.m_target, test_case.m_internal_format, test_case.m_format, test_case.m_type, 0 /* level */, m_width, m_height, n_layers, pixels, out_buf_id); Utils::makeTextureComplete(m_context, test_case.m_target, name, 0 /* base */, 0 /* max */); return name; } /** Verify copy operation * * @param test_case Test case * @param src_pixels Original data of source image * * @return true if everything is as expected, false otherwise **/ bool SmokeTest::verify(const testCase& test_case, const GLubyte* src_pixels) { std::vector dst_data; const GLuint n_layers = (GL_TEXTURE_CUBE_MAP_ARRAY != test_case.m_target) ? m_depth : 6; const GLuint pixel_size = Utils::getPixelSizeForFormat(test_case.m_internal_format); const GLuint line_size = pixel_size * m_width; const GLuint req_memory = line_size * m_height * n_layers; std::vector src_data; /* Prepare storage */ dst_data.resize(req_memory); src_data.resize(req_memory); /* Check if source image was not modified */ { getPixels(m_src_tex_name, test_case, &src_data[0]); if (false == compareImages(test_case, src_pixels, &src_data[0])) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "CopyImageSubData modified contents of source image. Original data: left." << tcu::TestLog::EndMessage; return false; } } /* Check contents of destination image */ { getPixels(m_dst_tex_name, test_case, &dst_data[0]); if (false == compareImages(test_case, src_pixels, &dst_data[0])) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "CopyImageSubData stored invalid contents in destination image. Source data: left." << tcu::TestLog::EndMessage; return false; } } return true; } /* InvalidTargetTest */ /** Constructor * * @param context Text context **/ InvalidTargetTest::InvalidTargetTest(deqp::Context& context) : TestCase(context, "invalid_target", "Test verifies if INVALID_ENUM is generated when invalid target is provided to CopyImageSubData") , m_dst_buf_name(0) , m_dst_tex_name(0) , m_src_buf_name(0) , m_src_tex_name(0) , m_test_case_index(0) { /* Valid source, valid dst */ for (GLuint src = 0; src < s_n_valid_targets; ++src) { for (GLuint dst = 0; dst < s_n_valid_targets; ++dst) { const GLenum src_target = s_valid_targets[src]; const GLenum dst_target = s_valid_targets[dst]; testCase test_case = { src_target, dst_target, GL_NO_ERROR }; if (Utils::isTargetMultisampled(dst_target) != Utils::isTargetMultisampled(src_target)) { test_case.m_expected_result = GL_INVALID_OPERATION; } m_test_cases.push_back(test_case); } } /* Invalid source, invalid dst */ for (GLuint src = 0; src < s_n_invalid_targets; ++src) { for (GLuint dst = 0; dst < s_n_invalid_targets; ++dst) { const GLenum src_target = s_invalid_targets[src]; const GLenum dst_target = s_invalid_targets[dst]; const testCase test_case = { src_target, dst_target, GL_INVALID_ENUM }; m_test_cases.push_back(test_case); } } /* Invalid source, valid dst */ for (GLuint src = 0; src < s_n_invalid_targets; ++src) { for (GLuint dst = 0; dst < s_n_valid_targets; ++dst) { const GLenum src_target = s_invalid_targets[src]; const GLenum dst_target = s_valid_targets[dst]; const testCase test_case = { src_target, dst_target, GL_INVALID_ENUM }; m_test_cases.push_back(test_case); } } /* Valid source, invalid dst */ for (GLuint src = 0; src < s_n_valid_targets; ++src) { for (GLuint dst = 0; dst < s_n_invalid_targets; ++dst) { const GLenum src_target = s_valid_targets[src]; const GLenum dst_target = s_invalid_targets[dst]; const testCase test_case = { src_target, dst_target, GL_INVALID_ENUM }; m_test_cases.push_back(test_case); } } } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult InvalidTargetTest::iterate() { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; const testCase& test_case = m_test_cases[m_test_case_index]; try { /* Prepare textures */ m_dst_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_dst_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, m_dst_buf_name); m_src_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_src_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, m_src_buf_name); /* Make textures complete */ Utils::makeTextureComplete(m_context, test_case.m_dst_target, m_dst_tex_name, 0 /* base */, 0 /* max */); Utils::makeTextureComplete(m_context, test_case.m_src_target, m_src_tex_name, 0 /* base */, 0 /* max */); } catch (tcu::Exception& exc) { clean(); throw exc; } /* Execute CopyImageSubData */ gl.copyImageSubData(m_src_tex_name, test_case.m_src_target, 0 /* srcLevel */, 0 /* srcX */, 0 /* srcY */, 0 /* srcZ */, m_dst_tex_name, test_case.m_dst_target, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, 1 /* srcWidth */, 1 /* srcHeight */, 1 /* srcDepth */); /* Verify generated error */ error = gl.getError(); result = (test_case.m_expected_result == error); /* Free resources */ clean(); /* Set result */ if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Expected result: " << glu::getErrorStr(test_case.m_expected_result) << " got: " << glu::getErrorStr(error) << ". Source target: " << glu::getTextureTargetStr(test_case.m_src_target) << ", destination target: " << glu::getTextureTargetStr(test_case.m_dst_target) << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Cleans resources * **/ void InvalidTargetTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); const testCase& test_case = m_test_cases[m_test_case_index]; /* Clean textures and buffers. Errors ignored */ Utils::deleteTexture(m_context, test_case.m_dst_target, m_dst_tex_name); Utils::deleteTexture(m_context, test_case.m_src_target, m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; if (0 != m_dst_buf_name) { gl.deleteBuffers(1, &m_dst_buf_name); m_dst_buf_name = 0; } if (0 != m_src_buf_name) { gl.deleteBuffers(1, &m_src_buf_name); m_src_buf_name = 0; } } /* TargetMismatchTest */ /** Constructor * * @param context Text context **/ TargetMismatchTest::TargetMismatchTest(deqp::Context& context) : TestCase( context, "target_miss_match", "Test verifies if INVALID_ENUM is generated when target provided to CopyImageSubData does not match texture") , m_dst_buf_name(0) , m_dst_tex_name(0) , m_src_buf_name(0) , m_src_tex_name(0) , m_test_case_index(0) { /* Wrong dst target */ for (GLuint target = 0; target < s_n_valid_targets; ++target) { for (GLuint dst = 0; dst < s_n_valid_targets; ++dst) { const GLenum dst_target = s_valid_targets[dst]; const GLenum src_target = s_valid_targets[target]; const GLenum tex_target = s_valid_targets[target]; testCase test_case = { tex_target, src_target, dst_target, GL_INVALID_ENUM }; /* Skip renderbuffers */ if ((GL_RENDERBUFFER == tex_target) || (GL_RENDERBUFFER == dst_target) || (GL_RENDERBUFFER == src_target)) { continue; } /* Valid case */ if (dst_target == tex_target) { test_case.m_expected_result = GL_NO_ERROR; } /* Skip cases with multisampling conflict */ if (Utils::isTargetMultisampled(dst_target) != Utils::isTargetMultisampled(src_target)) { continue; } m_test_cases.push_back(test_case); } } /* Wrong src target */ for (GLuint target = 0; target < s_n_valid_targets; ++target) { for (GLuint src = 0; src < s_n_valid_targets; ++src) { const GLenum dst_target = s_valid_targets[target]; const GLenum src_target = s_valid_targets[src]; const GLenum tex_target = s_valid_targets[target]; testCase test_case = { tex_target, src_target, dst_target, GL_INVALID_ENUM }; /* Skip renderbuffers */ if ((GL_RENDERBUFFER == tex_target) || (GL_RENDERBUFFER == dst_target) || (GL_RENDERBUFFER == src_target)) { continue; } /* Valid case */ if (src_target == tex_target) { test_case.m_expected_result = GL_NO_ERROR; } /* Skip cases with multisampling conflict */ if (Utils::isTargetMultisampled(dst_target) != Utils::isTargetMultisampled(src_target)) { continue; } m_test_cases.push_back(test_case); } } } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult TargetMismatchTest::iterate() { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; const testCase& test_case = m_test_cases[m_test_case_index]; try { /* Prepare textures */ m_dst_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, m_dst_buf_name); m_src_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, m_src_buf_name); /* Make textures complete */ Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_dst_tex_name, 0 /* base */, 0 /* max */); Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_src_tex_name, 0 /* base */, 0 /* max */); } catch (tcu::Exception& exc) { clean(); throw exc; } /* Execute CopyImageSubData */ gl.copyImageSubData(m_src_tex_name, test_case.m_src_target, 0 /* srcLevel */, 0 /* srcX */, 0 /* srcY */, 0 /* srcZ */, m_dst_tex_name, test_case.m_dst_target, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, 1 /* srcWidth */, 1 /* srcHeight */, 1 /* srcDepth */); /* Verify generated error */ error = gl.getError(); result = (test_case.m_expected_result == error); /* Remove resources */ clean(); if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Expected result: " << glu::getErrorStr(test_case.m_expected_result) << " got: " << glu::getErrorStr(error) << ". Texture target: " << glu::getTextureTargetStr(test_case.m_tex_target) << ". Source target: " << glu::getTextureTargetStr(test_case.m_src_target) << ", destination target: " << glu::getTextureTargetStr(test_case.m_dst_target) << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Cleans resources * **/ void TargetMismatchTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); /* Clean textures and buffers. Errors ignored */ gl.deleteTextures(1, &m_dst_tex_name); gl.deleteTextures(1, &m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; if (0 != m_dst_buf_name) { gl.deleteBuffers(1, &m_dst_buf_name); m_dst_buf_name = 0; } if (0 != m_src_buf_name) { gl.deleteBuffers(1, &m_src_buf_name); m_src_buf_name = 0; } } /* TargetMismatchTest */ /** Constructor * * @param context Text context **/ IncompleteTexTest::IncompleteTexTest(deqp::Context& context) : TestCase( context, "incomplete_tex", "Test verifies if INVALID_OPERATION is generated when texture provided to CopyImageSubData is incomplete") , m_dst_buf_name(0) , m_dst_tex_name(0) , m_src_buf_name(0) , m_src_tex_name(0) , m_test_case_index(0) { for (GLuint target = 0; target < s_n_valid_targets; ++target) { const GLenum tex_target = s_valid_targets[target]; testCase test_case = { tex_target, false, false, GL_INVALID_OPERATION }; /* Skip targets that are not multi level */ if (false == Utils::isTargetMultilevel(tex_target)) { continue; } m_test_cases.push_back(test_case); test_case.m_is_dst_complete = true; test_case.m_is_src_complete = false; m_test_cases.push_back(test_case); test_case.m_is_dst_complete = false; test_case.m_is_src_complete = true; m_test_cases.push_back(test_case); test_case.m_is_dst_complete = true; test_case.m_is_src_complete = true; test_case.m_expected_result = GL_NO_ERROR; m_test_cases.push_back(test_case); } } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult IncompleteTexTest::iterate() { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; const testCase& test_case = m_test_cases[m_test_case_index]; try { /* Prepare textures */ m_dst_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, m_dst_buf_name); m_src_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, m_src_buf_name); /* Make textures complete */ if (true == test_case.m_is_dst_complete) { Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_dst_tex_name, 0 /* base */, 0 /* max */); } if (true == test_case.m_is_src_complete) { Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_src_tex_name, 0 /* base */, 0 /* max */); } } catch (tcu::Exception& exc) { clean(); throw exc; } /* Execute CopyImageSubData */ gl.copyImageSubData(m_src_tex_name, test_case.m_tex_target, 0 /* srcLevel */, 0 /* srcX */, 0 /* srcY */, 0 /* srcZ */, m_dst_tex_name, test_case.m_tex_target, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, 1 /* srcWidth */, 1 /* srcHeight */, 1 /* srcDepth */); /* Verify generated error */ error = gl.getError(); result = (test_case.m_expected_result == error); /* Remove resources */ clean(); if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Expected result: " << glu::getErrorStr(test_case.m_expected_result) << " got: " << glu::getErrorStr(error) << ". Texture target: " << glu::getTextureTargetStr(test_case.m_tex_target) << ". Is source complete: " << test_case.m_is_src_complete << ", is destination complete: " << test_case.m_is_dst_complete << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Cleans resources * **/ void IncompleteTexTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); /* Clean textures and buffers. Errors ignored */ gl.deleteTextures(1, &m_dst_tex_name); gl.deleteTextures(1, &m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; if (0 != m_dst_buf_name) { gl.deleteBuffers(1, &m_dst_buf_name); m_dst_buf_name = 0; } if (0 != m_src_buf_name) { gl.deleteBuffers(1, &m_src_buf_name); m_src_buf_name = 0; } } /* IncompatibleFormatsTest */ /** Constructor * * @param context Text context **/ IncompatibleFormatsTest::IncompatibleFormatsTest(deqp::Context& context) : TestCase( context, "incompatible_formats", "Test verifies if INVALID_OPERATION is generated when textures provided to CopyImageSubData are incompatible") , m_dst_buf_name(0) , m_dst_tex_name(0) , m_src_buf_name(0) , m_src_tex_name(0) , m_test_case_index(0) { /* RGBA8UI vs RGBA16UI */ for (GLuint target = 0; target < s_n_valid_targets; ++target) { const GLenum tex_target = s_valid_targets[target]; testCase test_case = { tex_target, GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, GL_INVALID_OPERATION }; /* Skip multisampled and rectangle targets */ if (true == Utils::isTargetMultisampled(tex_target)) { continue; } m_test_cases.push_back(test_case); } /* RGBA8UI vs RGBA32UI */ for (GLuint target = 0; target < s_n_valid_targets; ++target) { const GLenum tex_target = s_valid_targets[target]; testCase test_case = { tex_target, GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, GL_INVALID_OPERATION }; /* Skip multisampled and rectangle targets */ if (true == Utils::isTargetMultisampled(tex_target)) { continue; } m_test_cases.push_back(test_case); } /* RGBA16UI vs RG16UI */ for (GLuint target = 0; target < s_n_valid_targets; ++target) { const GLenum tex_target = s_valid_targets[target]; testCase test_case = { tex_target, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, GL_INVALID_OPERATION }; /* Skip multisampled and rectangle targets */ if (true == Utils::isTargetMultisampled(tex_target)) { continue; } m_test_cases.push_back(test_case); } /* RGBA32UI vs RGBA32F */ for (GLuint target = 0; target < s_n_valid_targets; ++target) { const GLenum tex_target = s_valid_targets[target]; testCase test_case = { tex_target, GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, GL_RGBA32F, GL_RGBA, GL_FLOAT, GL_NO_ERROR }; /* Skip multisampled and rectangle targets */ if (true == Utils::isTargetMultisampled(tex_target)) { continue; } m_test_cases.push_back(test_case); } /* RGBA8 vs RGBA32F */ for (GLuint target = 0; target < s_n_valid_targets; ++target) { const GLenum tex_target = s_valid_targets[target]; testCase test_case = { tex_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, GL_RGBA32F, GL_RGBA, GL_FLOAT, GL_INVALID_OPERATION }; /* Skip multisampled and rectangle targets */ if (true == Utils::isTargetMultisampled(tex_target)) { continue; } m_test_cases.push_back(test_case); } } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult IncompatibleFormatsTest::iterate() { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; const testCase& test_case = m_test_cases[m_test_case_index]; try { /* Prepare textures */ m_dst_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, test_case.m_dst_internal_format, test_case.m_dst_format, test_case.m_dst_type, m_dst_buf_name); m_src_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, test_case.m_src_internal_format, test_case.m_src_format, test_case.m_src_type, m_src_buf_name); /* Make textures complete */ Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_dst_tex_name, 0 /* base */, 0 /* max */); Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_src_tex_name, 0 /* base */, 0 /* max */); } catch (tcu::Exception& exc) { clean(); throw exc; } /* Execute CopyImageSubData */ gl.copyImageSubData(m_src_tex_name, test_case.m_tex_target, 0 /* srcLevel */, 0 /* srcX */, 0 /* srcY */, 0 /* srcZ */, m_dst_tex_name, test_case.m_tex_target, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, 1 /* srcWidth */, 1 /* srcHeight */, 1 /* srcDepth */); /* Verify generated error */ error = gl.getError(); result = (test_case.m_expected_result == error); /* Remove resources */ clean(); if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Expected result: " << glu::getErrorStr(test_case.m_expected_result) << " got: " << glu::getErrorStr(error) << ". Texture target: " << glu::getTextureTargetStr(test_case.m_tex_target) << ". Source format: " << glu::getInternalFormatParameterStr(test_case.m_src_internal_format) << ". Destination format: " << glu::getInternalFormatParameterStr(test_case.m_dst_internal_format) << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Cleans resources * **/ void IncompatibleFormatsTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); const testCase& test_case = m_test_cases[m_test_case_index]; /* Clean textures and buffers. Errors ignored */ Utils::deleteTexture(m_context, test_case.m_tex_target, m_dst_tex_name); Utils::deleteTexture(m_context, test_case.m_tex_target, m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; if (0 != m_dst_buf_name) { gl.deleteBuffers(1, &m_dst_buf_name); m_dst_buf_name = 0; } if (0 != m_src_buf_name) { gl.deleteBuffers(1, &m_src_buf_name); m_src_buf_name = 0; } } /* InvalidTargetTest */ /** Constructor * * @param context Text context **/ SamplesMismatchTest::SamplesMismatchTest(deqp::Context& context) : TestCase(context, "samples_mismatch", "Test verifies if INVALID_OPERATION is generated when textures provided " "to CopyImageSubData have different number of samples") , m_dst_tex_name(0) , m_src_tex_name(0) , m_test_case_index(0) { testCase test_case; static const GLsizei n_samples[2] = { 1, 4 }; static const GLenum targets[2] = { GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_2D_MULTISAMPLE_ARRAY }; for (GLuint src_sample = 0; src_sample < 2; ++src_sample) { for (GLuint dst_sample = 0; dst_sample < 2; ++dst_sample) { for (GLuint src_target = 0; src_target < 2; ++src_target) { for (GLuint dst_target = 0; dst_target < 2; ++dst_target) { test_case.m_src_target = targets[src_target]; test_case.m_src_n_samples = n_samples[src_sample]; test_case.m_dst_target = targets[dst_target]; test_case.m_dst_n_samples = n_samples[dst_sample]; if (test_case.m_src_n_samples == test_case.m_dst_n_samples) { test_case.m_expected_result = GL_NO_ERROR; } else { test_case.m_expected_result = GL_INVALID_OPERATION; } m_test_cases.push_back(test_case); } } } } } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult SamplesMismatchTest::iterate() { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; const testCase& test_case = m_test_cases[m_test_case_index]; try { /* Prepare textures */ m_dst_tex_name = Utils::prepareMultisampleTex(m_context, test_case.m_dst_target, test_case.m_dst_n_samples); m_src_tex_name = Utils::prepareMultisampleTex(m_context, test_case.m_src_target, test_case.m_src_n_samples); } catch (tcu::Exception& exc) { clean(); throw exc; } GLenum expected_result = test_case.m_expected_result; if (test_case.m_dst_n_samples > 0 && test_case.m_src_n_samples > 0) { /* Implementations are allowed to use more samples than requested, so we need * to verify the actual sample counts allocated by the hardware and adjust * the expected result if they are different from what we requested. */ GLint num_src_samples; GLint num_dst_samples; gl.bindTexture(test_case.m_dst_target, m_dst_tex_name); gl.getTexLevelParameteriv(test_case.m_dst_target, 0, GL_TEXTURE_SAMPLES, &num_dst_samples); gl.bindTexture(test_case.m_src_target, m_src_tex_name); gl.getTexLevelParameteriv(test_case.m_src_target, 0, GL_TEXTURE_SAMPLES, &num_src_samples); if (num_dst_samples != test_case.m_dst_n_samples || num_src_samples != test_case.m_src_n_samples) { /* The hardware allocated a different number of samples, check if this affects the expected * result of the test. This can happen if we requested different sample counts but the * hardware ended up allocating the same or viceversa. */ if (test_case.m_dst_n_samples != test_case.m_src_n_samples && num_dst_samples == num_src_samples) { expected_result = GL_NO_ERROR; } else if (test_case.m_dst_n_samples == test_case.m_src_n_samples && num_dst_samples != num_src_samples) { expected_result = GL_INVALID_OPERATION; } } } /* Execute CopyImageSubData */ gl.copyImageSubData(m_src_tex_name, test_case.m_src_target, 0 /* srcLevel */, 0 /* srcX */, 0 /* srcY */, 0 /* srcZ */, m_dst_tex_name, test_case.m_dst_target, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, 1 /* srcWidth */, 1 /* srcHeight */, 1 /* srcDepth */); /* Verify generated error */ error = gl.getError(); result = (expected_result == error); /* Free resources */ clean(); /* Set result */ if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Expected result: " << glu::getErrorStr(expected_result) << " got: " << glu::getErrorStr(error) << ". Source target: " << glu::getTextureTargetStr(test_case.m_src_target) << " samples: " << test_case.m_src_n_samples << ", destination target: " << glu::getTextureTargetStr(test_case.m_dst_target) << " samples: " << test_case.m_dst_n_samples << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Cleans resources * **/ void SamplesMismatchTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); /* Clean textures . Errors ignored */ gl.deleteTextures(1, &m_dst_tex_name); gl.deleteTextures(1, &m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; } /* IncompatibleFormatsCompressionTest */ /** Constructor * * @param context Text context **/ IncompatibleFormatsCompressionTest::IncompatibleFormatsCompressionTest(deqp::Context& context) : TestCase(context, "incompatible_formats_compression", "Test verifies if INVALID_OPERATION is generated when " "textures provided to CopyImageSubData are incompatible, " "one of formats is compressed") , m_dst_tex_name(0) , m_src_tex_name(0) , m_test_case_index(0) { for (GLuint target = 0; target < s_n_valid_targets; ++target) { const GLenum tex_target = s_valid_targets[target]; /* Skip 1D targets, not supported */ if ((GL_TEXTURE_1D == tex_target) || (GL_TEXTURE_1D_ARRAY == tex_target) || (GL_TEXTURE_3D == tex_target) || (GL_TEXTURE_RECTANGLE == tex_target) || (GL_RENDERBUFFER == tex_target) || (GL_TEXTURE_CUBE_MAP_ARRAY == tex_target)) { continue; } /* Skip multisampled and rectangle targets */ if (true == Utils::isTargetMultisampled(tex_target)) { continue; } /* Compressed 128bit vs RGBA32UI */ { testCase test_case = { tex_target, GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_BYTE, GL_NO_ERROR }; m_test_cases.push_back(test_case); } /* Compressed 128bit vs RGBA16UI */ { testCase test_case = { tex_target, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_BYTE, GL_INVALID_OPERATION }; m_test_cases.push_back(test_case); } } } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult IncompatibleFormatsCompressionTest::iterate() { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; GLuint temp = 0; const testCase& test_case = m_test_cases[m_test_case_index]; try { /* Prepare textures */ m_dst_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, test_case.m_dst_internal_format, test_case.m_dst_format, test_case.m_dst_type, temp); m_src_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, test_case.m_src_internal_format, test_case.m_src_format, test_case.m_src_type, temp); /* Make textures complete */ Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_dst_tex_name, 0 /* base */, 0 /* max */); Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_src_tex_name, 0 /* base */, 0 /* max */); } catch (tcu::Exception& exc) { clean(); throw exc; } /* Execute CopyImageSubData */ gl.copyImageSubData(m_src_tex_name, test_case.m_tex_target, 0 /* srcLevel */, 0 /* srcX */, 0 /* srcY */, 0 /* srcZ */, m_dst_tex_name, test_case.m_tex_target, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, 4 /* srcWidth */, 4 /* srcHeight */, 1 /* srcDepth */); /* Verify generated error */ error = gl.getError(); result = (test_case.m_expected_result == error); /* Remove resources */ clean(); if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Expected result: " << glu::getErrorStr(test_case.m_expected_result) << " got: " << glu::getErrorStr(error) << ". Texture target: " << glu::getTextureTargetStr(test_case.m_tex_target) << ". Source format: " << glu::getInternalFormatParameterStr(test_case.m_src_internal_format) << ". Destination format: " << glu::getInternalFormatParameterStr(test_case.m_dst_internal_format) << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Cleans resources * **/ void IncompatibleFormatsCompressionTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); /* Clean textures and buffers. Errors ignored */ gl.deleteTextures(1, &m_dst_tex_name); gl.deleteTextures(1, &m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; } /* InvalidObjectTest */ /** Constructor * * @param context Text context **/ InvalidObjectTest::InvalidObjectTest(deqp::Context& context) : TestCase( context, "invalid_object", "Test verifies if INVALID_VALUE is generated when object & target provided to CopyImageSubData do not match") , m_dst_name(0) , m_src_name(0) , m_test_case_index(0) { static glw::GLenum arg_types[] = { GL_TEXTURE_2D, GL_RENDERBUFFER }; static const GLuint n_arg_types = sizeof(arg_types) / sizeof(arg_types[0]); for (GLuint dst = 0; dst < n_arg_types; dst++) { for (GLuint src = 0; src < n_arg_types; src++) { for (GLuint dst_valid = 0; dst_valid < 2; dst_valid++) { for (GLuint src_valid = 0; src_valid < 2; src_valid++) { glw::GLenum expected_error = GL_INVALID_VALUE; if (!!src_valid && !!dst_valid) { expected_error = GL_NO_ERROR; } const testCase test_case = { arg_types[dst], !!dst_valid, arg_types[src], !!src_valid, expected_error }; m_test_cases.push_back(test_case); } } } } } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult InvalidObjectTest::iterate() { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; GLuint temp = 0; const testCase& test_case = m_test_cases[m_test_case_index]; try { /* Prepare objects */ if (GL_RENDERBUFFER == test_case.m_dst_target) { m_dst_name = Utils::prepareRenderBuffer(m_context, GL_RGBA8); } else { m_dst_name = Utils::prepareTex16x16x6(m_context, test_case.m_dst_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, temp); /* Make textures complete */ Utils::makeTextureComplete(m_context, test_case.m_dst_target, m_dst_name, 0 /* base */, 0 /* max */); } if (GL_RENDERBUFFER == test_case.m_src_target) { m_src_name = Utils::prepareRenderBuffer(m_context, GL_RGBA8); } else { m_src_name = Utils::prepareTex16x16x6(m_context, test_case.m_src_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, temp); /* Make texture complete */ Utils::makeTextureComplete(m_context, test_case.m_src_target, m_src_name, 0 /* base */, 0 /* max */); } /* If an object is invalid, free it before use to make it invalid */ if (!test_case.m_dst_valid) { Utils::deleteTexture(m_context, test_case.m_dst_target, m_dst_name); } if (!test_case.m_src_valid) { Utils::deleteTexture(m_context, test_case.m_src_target, m_src_name); } } catch (tcu::Exception& exc) { clean(); throw exc; } /* Execute CopyImageSubData */ gl.copyImageSubData(m_src_name, test_case.m_src_target, 0 /* srcLevel */, 0 /* srcX */, 0 /* srcY */, 0 /* srcZ */, m_dst_name, test_case.m_dst_target, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, 1 /* srcWidth */, 1 /* srcHeight */, 1 /* srcDepth */); /* Verify generated error */ error = gl.getError(); result = (test_case.m_expected_result == error); /* Remove resources */ clean(); if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Expected result: " << glu::getErrorStr(test_case.m_expected_result) << " got: " << glu::getErrorStr(error) << ". Destination target: " << glu::getTextureTargetStr(test_case.m_dst_target) << ". Destination valid: " << (test_case.m_src_valid ? "true" : "false") << ". Source target: " << glu::getTextureTargetStr(test_case.m_dst_target) << ". Source valid: " << (test_case.m_dst_valid ? "true" : "false") << "." << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Cleans resources * **/ void InvalidObjectTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); const testCase& test_case = m_test_cases[m_test_case_index]; /* Clean textures or renderbuffers. Errors ignored */ if (test_case.m_dst_valid) { if (GL_RENDERBUFFER == test_case.m_dst_target) { gl.deleteRenderbuffers(1, &m_dst_name); } else { gl.deleteTextures(1, &m_dst_name); } } if (test_case.m_src_valid) { if (GL_RENDERBUFFER == test_case.m_src_target) { gl.deleteRenderbuffers(1, &m_src_name); } else { gl.deleteTextures(1, &m_src_name); } } m_dst_name = 0; m_src_name = 0; } /* NonExistentMipMapTest */ /** Constructor * * @param context Text context **/ NonExistentMipMapTest::NonExistentMipMapTest(deqp::Context& context) : TestCase(context, "non_existent_mipmap", "Test verifies if INVALID_VALUE is generated when CopyImageSubData is " "executed for mipmap that does not exist") , m_dst_tex_name(0) , m_src_tex_name(0) , m_test_case_index(0) { for (GLuint target = 0; target < s_n_valid_targets; ++target) { const GLenum tex_target = s_valid_targets[target]; testCase test_case = { tex_target, 0, 0, GL_NO_ERROR }; if (GL_RENDERBUFFER == tex_target) { continue; } m_test_cases.push_back(test_case); /* Rest of cases is invalid */ test_case.m_expected_result = GL_INVALID_VALUE; test_case.m_dst_level = 1; test_case.m_src_level = 0; m_test_cases.push_back(test_case); test_case.m_dst_level = 0; test_case.m_src_level = 1; m_test_cases.push_back(test_case); test_case.m_dst_level = 1; test_case.m_src_level = 1; m_test_cases.push_back(test_case); } } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult NonExistentMipMapTest::iterate() { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; GLuint temp = 0; const testCase& test_case = m_test_cases[m_test_case_index]; try { /* Prepare textures */ m_dst_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, temp); m_src_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, temp); /* Make textures complete */ Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_dst_tex_name, 0 /* base */, 0 /* max */); Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_src_tex_name, 0 /* base */, 0 /* max */); } catch (tcu::Exception& exc) { clean(); throw exc; } /* Execute CopyImageSubData */ gl.copyImageSubData(m_src_tex_name, test_case.m_tex_target, test_case.m_src_level, 0 /* srcX */, 0 /* srcY */, 0 /* srcZ */, m_dst_tex_name, test_case.m_tex_target, test_case.m_dst_level, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, 1 /* srcWidth */, 1 /* srcHeight */, 1 /* srcDepth */); /* Verify generated error */ error = gl.getError(); result = (test_case.m_expected_result == error); /* Free resources */ clean(); /* Set result */ if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Expected result: " << glu::getErrorStr(test_case.m_expected_result) << " got: " << glu::getErrorStr(error) << ". Texture target: " << glu::getTextureTargetStr(test_case.m_tex_target) << ", source level: " << test_case.m_src_level << ", destination level: " << test_case.m_dst_level << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Cleans resources * **/ void NonExistentMipMapTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); /* Clean textures and buffers. Errors ignored */ gl.deleteTextures(1, &m_dst_tex_name); gl.deleteTextures(1, &m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; } /* ExceedingBoundariesTest */ const glw::GLuint ExceedingBoundariesTest::m_region_depth = 4; const glw::GLuint ExceedingBoundariesTest::m_region_height = 4; const glw::GLuint ExceedingBoundariesTest::m_region_width = 4; /** Constructor * * @param context Text context **/ ExceedingBoundariesTest::ExceedingBoundariesTest(deqp::Context& context) : TestCase(context, "exceeding_boundaries", "Test verifies if INVALID_VALUE is generated when CopyImageSubData is " "executed for regions exceeding image boundaries") , m_dst_tex_name(0) , m_src_tex_name(0) , m_test_case_index(0) { /* 16x16x6 are values used by prepareTex16x16x6 */ static const GLuint invalid_x = 16 - (m_region_width / 2); static const GLuint invalid_y = 16 - (m_region_height / 2); static const GLuint invalid_z = 6 - (m_region_depth / 2); static const GLuint x_vals[] = { 0, invalid_x }; static const GLuint y_vals[] = { 0, invalid_y }; static const GLuint z_vals[] = { 0, invalid_z }; static const GLuint n_x_vals = sizeof(x_vals) / sizeof(x_vals[0]); static const GLuint n_y_vals = sizeof(y_vals) / sizeof(y_vals[0]); static const GLuint n_z_vals = sizeof(z_vals) / sizeof(z_vals[0]); for (GLuint target = 0; target < s_n_valid_targets; ++target) { const GLenum tex_target = s_valid_targets[target]; GLuint height = m_region_height; if (GL_TEXTURE_BUFFER == tex_target) { continue; } if ((GL_TEXTURE_1D == tex_target) || (GL_TEXTURE_1D_ARRAY == tex_target)) { height = 1; } for (GLuint x = 0; x < n_x_vals; ++x) { for (GLuint y = 0; y < n_y_vals; ++y) { for (GLuint z = 0; z < n_z_vals; ++z) { const GLuint x_val = x_vals[x]; const GLuint y_val = y_vals[y]; const GLuint z_val = z_vals[z]; const GLenum res = ((0 == x_val) && (0 == y_val) && (0 == z_val)) ? GL_NO_ERROR : GL_INVALID_VALUE; GLuint depth = 1; if (0 != z_val) { if ((GL_TEXTURE_2D_ARRAY != tex_target) || (GL_TEXTURE_2D_MULTISAMPLE_ARRAY != tex_target) || (GL_TEXTURE_3D != tex_target) || (GL_TEXTURE_CUBE_MAP_ARRAY != tex_target)) { /* Skip z != 0 for 2d textures */ continue; } else { /* Set depth so as to exceed boundary */ depth = m_region_depth; } } testCase src_test_case = { tex_target, depth, height, x_val, y_val, z_val, 0, 0, 0, res }; testCase dst_test_case = { tex_target, depth, height, 0, 0, 0, x_val, y_val, z_val, res }; m_test_cases.push_back(src_test_case); m_test_cases.push_back(dst_test_case); } } } } } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult ExceedingBoundariesTest::iterate() { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; GLuint temp = 0; const testCase& test_case = m_test_cases[m_test_case_index]; try { /* Prepare textures */ m_dst_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, temp); m_src_tex_name = Utils::prepareTex16x16x6(m_context, test_case.m_tex_target, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, temp); /* Make textures complete */ Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_dst_tex_name, 0 /* base */, 0 /* max */); Utils::makeTextureComplete(m_context, test_case.m_tex_target, m_src_tex_name, 0 /* base */, 0 /* max */); } catch (tcu::Exception& exc) { clean(); throw exc; } /* Execute CopyImageSubData */ gl.copyImageSubData(m_src_tex_name, test_case.m_tex_target, 0 /* level */, test_case.m_src_x /* srcX */, test_case.m_src_y /* srcY */, test_case.m_src_z /* srcZ */, m_dst_tex_name, test_case.m_tex_target, 0 /* level */, test_case.m_dst_x /* dstX */, test_case.m_dst_y /* dstY */, test_case.m_dst_z /* dstZ */, m_region_width /* srcWidth */, test_case.m_height /* srcHeight */, test_case.m_depth /* srcDepth */); /* Verify generated error */ error = gl.getError(); result = (test_case.m_expected_result == error); /* Free resources */ clean(); /* Set result */ if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Expected result: " << glu::getErrorStr(test_case.m_expected_result) << " got: " << glu::getErrorStr(error) << ". Texture target: " << glu::getTextureTargetStr(test_case.m_tex_target) << ", source: [" << test_case.m_src_x << ", " << test_case.m_src_y << ", " << test_case.m_src_z << "], destination: [" << test_case.m_src_x << ", " << test_case.m_src_y << ", " << test_case.m_src_z << "], depth: " << test_case.m_depth << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Cleans resources * **/ void ExceedingBoundariesTest::clean() { const testCase& test_case = m_test_cases[m_test_case_index]; /* Clean textures and buffers. Errors ignored */ Utils::deleteTexture(m_context, test_case.m_tex_target, m_dst_tex_name); Utils::deleteTexture(m_context, test_case.m_tex_target, m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; } /* InvalidAlignmentTest */ /** Constructor * * @param context Text context **/ InvalidAlignmentTest::InvalidAlignmentTest(deqp::Context& context) : TestCase(context, "invalid_alignment", "Test verifies if INVALID_VALUE is generated when CopyImageSubData is " "executed for regions with invalid alignment") , m_dst_tex_name(0) , m_src_tex_name(0) , m_test_case_index(0) { /* 16x16x6 are values used by prepareTex16x16x6 */ static const GLuint invalid_h = 2; static const GLuint invalid_w = 2; static const GLuint invalid_x = 2; static const GLuint invalid_y = 2; static const GLuint valid_h = 4; static const GLuint valid_w = 4; static const GLuint h_vals[] = { valid_h, invalid_h }; static const GLuint w_vals[] = { valid_w, invalid_w }; static const GLuint x_vals[] = { 0, invalid_x }; static const GLuint y_vals[] = { 0, invalid_y }; static const GLuint n_h_vals = sizeof(h_vals) / sizeof(h_vals[0]); static const GLuint n_w_vals = sizeof(w_vals) / sizeof(w_vals[0]); static const GLuint n_x_vals = sizeof(x_vals) / sizeof(x_vals[0]); static const GLuint n_y_vals = sizeof(y_vals) / sizeof(y_vals[0]); for (GLuint x = 0; x < n_x_vals; ++x) { for (GLuint y = 0; y < n_y_vals; ++y) { for (GLuint h = 0; h < n_h_vals; ++h) { for (GLuint w = 0; w < n_w_vals; ++w) { const GLuint h_val = h_vals[h]; const GLuint w_val = w_vals[w]; const GLuint x_val = x_vals[x]; const GLuint y_val = y_vals[y]; const GLenum res = ((valid_h == h_val) && (valid_w == w_val) && (0 == x_val) && (0 == y_val)) ? GL_NO_ERROR : GL_INVALID_VALUE; testCase dst_test_case = { h_val, w_val, 0, 0, x_val, y_val, res }; testCase src_test_case = { h_val, w_val, x_val, y_val, 0, 0, res }; m_test_cases.push_back(dst_test_case); m_test_cases.push_back(src_test_case); } } } } } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult InvalidAlignmentTest::iterate() { GLenum error = GL_NO_ERROR; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; bool result = false; GLuint temp = 0; const testCase& test_case = m_test_cases[m_test_case_index]; try { /* Prepare textures */ m_dst_tex_name = Utils::prepareTex16x16x6(m_context, GL_TEXTURE_2D, GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_BYTE, temp); m_src_tex_name = Utils::prepareTex16x16x6(m_context, GL_TEXTURE_2D, GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_BYTE, temp); /* Make textures complete */ Utils::makeTextureComplete(m_context, GL_TEXTURE_2D, m_dst_tex_name, 0 /* base */, 0 /* max */); Utils::makeTextureComplete(m_context, GL_TEXTURE_2D, m_src_tex_name, 0 /* base */, 0 /* max */); } catch (tcu::Exception& exc) { clean(); throw exc; } /* Execute CopyImageSubData */ gl.copyImageSubData(m_src_tex_name, GL_TEXTURE_2D, 0 /* level */, test_case.m_src_x /* srcX */, test_case.m_src_y /* srcY */, 0 /* srcZ */, m_dst_tex_name, GL_TEXTURE_2D, 0 /* level */, test_case.m_dst_x /* dstX */, test_case.m_dst_y /* dstY */, 0 /* dstZ */, test_case.m_width /* srcWidth */, test_case.m_height /* srcHeight */, 1 /* srcDepth */); /* Verify generated error */ error = gl.getError(); result = (test_case.m_expected_result == error); /* Free resources */ clean(); /* Set result */ if (true == result) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ m_test_case_index += 1; /* Are there any test cases left */ if (m_test_cases.size() > m_test_case_index) { it_result = tcu::TestNode::CONTINUE; } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Expected result: " << glu::getErrorStr(test_case.m_expected_result) << " got: " << glu::getErrorStr(error) << ". source: [" << test_case.m_src_x << ", " << test_case.m_src_y << "], destination: [" << test_case.m_src_x << ", " << test_case.m_src_y << "], size: " << test_case.m_width << " x " << test_case.m_height << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Done */ return it_result; } /** Cleans resources * **/ void InvalidAlignmentTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); /* Clean textures and buffers. Errors ignored */ gl.deleteTextures(1, &m_dst_tex_name); gl.deleteTextures(1, &m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; } /** Constructor * * @param context Text context **/ IntegerTexTest::IntegerTexTest(deqp::Context& context) : TestCase( context, "integer_tex", "Test verifies if INVALID_OPERATION is generated when texture provided to CopySubImageData is incomplete") , m_dst_buf_name(0) , m_dst_tex_name(0) , m_src_buf_name(0) , m_src_tex_name(0) , m_test_case_index(0) { } /** Execute test * * @return CONTINUE as long there are more test case, STOP otherwise **/ tcu::TestNode::IterateResult IntegerTexTest::iterate() { testCase testCases[] = { { GL_R32I, GL_INT }, { GL_R32UI, GL_UNSIGNED_INT } }; const unsigned int width = 16; const unsigned int height = 16; const Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestNode::IterateResult it_result = tcu::TestNode::STOP; const testCase& test_case = testCases[m_test_case_index]; std::vector data_buf(width * height, 1); m_dst_tex_name = createTexture(width, height, test_case.m_internal_format, test_case.m_type, &data_buf[0], GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR); std::fill(data_buf.begin(), data_buf.end(), 2); m_src_tex_name = createTexture(width, height, test_case.m_internal_format, test_case.m_type, &data_buf[0], GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR); /* Execute CopyImageSubData */ gl.copyImageSubData(m_src_tex_name, GL_TEXTURE_2D, 0 /* srcLevel */, 0 /* srcX */, 0 /* srcY */, 0 /* srcZ */, m_dst_tex_name, GL_TEXTURE_2D, 0 /* dstLevel */, 0 /* dstX */, 0 /* dstY */, 0 /* dstZ */, 1 /* srcWidth */, 1 /* srcHeight */, 1 /* srcDepth */); /* Check generated error */ GLenum error = gl.getError(); if (error == GL_NO_ERROR) { /* Verify result */ std::fill(data_buf.begin(), data_buf.end(), 3); gl.bindTexture(GL_TEXTURE_2D, m_dst_tex_name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.getTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, &data_buf[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "GetTexImage"); if ((data_buf[0] == 2) && (std::count(data_buf.begin(), data_buf.end(), 1) == (width * height - 1))) { m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); /* Increase index */ ++m_test_case_index; /* Are there any test cases left */ if (DE_LENGTH_OF_ARRAY(testCases) > m_test_case_index) it_result = tcu::TestNode::CONTINUE; } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Image data is not valid." << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failure. Expected no error, got: " << glu::getErrorStr(error) << ". Texture internal format: " << glu::getTextureFormatStr(test_case.m_internal_format) << tcu::TestLog::EndMessage; m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } /* Remove resources */ clean(); /* Done */ return it_result; } /** Create texture * **/ unsigned int IntegerTexTest::createTexture(int width, int height, GLint internalFormat, GLuint type, const void* data, int minFilter, int magFilter) { const Functions& gl = m_context.getRenderContext().getFunctions(); GLuint tex_name; gl.genTextures(1, &tex_name); GLU_EXPECT_NO_ERROR(gl.getError(), "GenTextures"); gl.bindTexture(GL_TEXTURE_2D, tex_name); GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); gl.texImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, GL_RED_INTEGER, type, data); GLU_EXPECT_NO_ERROR(gl.getError(), "TexImage2D"); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri"); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri"); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri"); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); GLU_EXPECT_NO_ERROR(gl.getError(), "TexParameteri"); gl.bindTexture(GL_TEXTURE_2D, 0); return tex_name; } /** Cleans resources * **/ void IntegerTexTest::clean() { const Functions& gl = m_context.getRenderContext().getFunctions(); /* Clean textures and buffers. Errors ignored */ gl.deleteTextures(1, &m_dst_tex_name); gl.deleteTextures(1, &m_src_tex_name); m_dst_tex_name = 0; m_src_tex_name = 0; if (0 != m_dst_buf_name) { gl.deleteBuffers(1, &m_dst_buf_name); m_dst_buf_name = 0; } if (0 != m_src_buf_name) { gl.deleteBuffers(1, &m_src_buf_name); m_src_buf_name = 0; } } } /* namespace CopyImage */ CopyImageTests::CopyImageTests(deqp::Context& context) : TestCaseGroup(context, "copy_image", "") { } CopyImageTests::~CopyImageTests(void) { } void CopyImageTests::init() { addChild(new CopyImage::FunctionalTest(m_context)); addChild(new CopyImage::IncompleteTexTest(m_context)); addChild(new CopyImage::InvalidObjectTest(m_context)); addChild(new CopyImage::SmokeTest(m_context)); addChild(new CopyImage::InvalidTargetTest(m_context)); addChild(new CopyImage::TargetMismatchTest(m_context)); addChild(new CopyImage::IncompatibleFormatsTest(m_context)); addChild(new CopyImage::SamplesMismatchTest(m_context)); addChild(new CopyImage::IncompatibleFormatsCompressionTest(m_context)); addChild(new CopyImage::NonExistentMipMapTest(m_context)); addChild(new CopyImage::ExceedingBoundariesTest(m_context)); addChild(new CopyImage::InvalidAlignmentTest(m_context)); addChild(new CopyImage::IntegerTexTest(m_context)); } } /* namespace gl4cts */