blob: 2ecee0833bdf65afa72d9730f5122029c6104ce8 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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 <algorithm>
#include <iomanip>
#include <sstream>
#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");
}
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");
}
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");
}
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");
}
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");
}
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");
}
/* 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 <internalformat>
* @param format <format>
* @param type <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 <internalformat>
* @param format <format>
* @param type <type>
* @param level <level>
* @param width <width>
* @param height <height>
* @param depth <depth>
* @param pixels <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");
}
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, <target> 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, <target> 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 <typename T>
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 <typename T>
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 <typename T, unsigned int size_1, unsigned int size_2, unsigned int size_3, unsigned int off_1,
unsigned int off_2, unsigned int off_3>
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 <typename T, unsigned int size_1, unsigned int size_2, unsigned int size_3, unsigned int size_4,
unsigned int off_1, unsigned int off_2, unsigned int off_3, unsigned int off_4>
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<deUint32, 5, 6, 15, tcu::FLOAT_SUPPORT_DENORM> temp_val(bits);
out_value = temp_val.asDouble();
}
break;
case 1:
{
deUint32 bits = ((val >> 11) & 0x000007ff);
tcu::Float<deUint32, 5, 6, 15, tcu::FLOAT_SUPPORT_DENORM> temp_val(bits);
out_value = temp_val.asDouble();
}
break;
case 2:
{
deUint32 bits = ((val >> 22) & 0x000003ff);
tcu::Float<deUint32, 5, 5, 15, tcu::FLOAT_SUPPORT_DENORM> temp_val(bits);
out_value = temp_val.asDouble();
}
break;
default:
TCU_FAIL("Invalid channel");
}
}
/** 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 <typename T>
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 <typename T>
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 <typename T, unsigned int size_1, unsigned int size_2, unsigned int size_3, unsigned int off_1,
unsigned int off_2, unsigned int off_3>
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 <typename T, unsigned int size_1, unsigned int size_2, unsigned int size_3, unsigned int size_4,
unsigned int off_1, unsigned int off_2, unsigned int off_3, unsigned int off_4>
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<deUint32, 5, 6, 15, tcu::FLOAT_SUPPORT_DENORM> val(value);
deUint32 bits = val.bits();
*ptr |= bits;
}
break;
case 1:
{
tcu::Float<deUint32, 5, 6, 15, tcu::FLOAT_SUPPORT_DENORM> val(value);
deUint32 bits = val.bits();
*ptr |= (bits << 11);
}
break;
case 2:
{
tcu::Float<deUint32, 5, 5, 15, tcu::FLOAT_SUPPORT_DENORM> val(value);
deUint32 bits = val.bits();
*ptr |= (bits << 22);
}
break;
default:
TCU_FAIL("Invalid channel");
}
}
/** 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<GLubyte>(channel, pixel, value);
break;
case GL_UNSIGNED_SHORT:
readBaseTypeFromUnsignedChannel<GLushort>(channel, pixel, value);
break;
case GL_UNSIGNED_INT:
readBaseTypeFromUnsignedChannel<GLuint>(channel, pixel, value);
break;
case GL_BYTE:
readBaseTypeFromSignedChannel<GLbyte>(channel, pixel, value);
break;
case GL_SHORT:
readBaseTypeFromSignedChannel<GLshort>(channel, pixel, value);
break;
case GL_INT:
readBaseTypeFromSignedChannel<GLint>(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<GLubyte, 3, 3, 2, 5, 2, 0>(channel, pixel, value);
break;
case GL_UNSIGNED_SHORT_5_6_5:
read3Channel<GLushort, 5, 6, 5, 11, 5, 0>(channel, pixel, value);
break;
/* 4 channels */
case GL_UNSIGNED_SHORT_4_4_4_4:
read4Channel<GLushort, 4, 4, 4, 4, 12, 8, 4, 0>(channel, pixel, value);
break;
case GL_UNSIGNED_SHORT_5_5_5_1:
read4Channel<GLushort, 5, 5, 5, 1, 11, 6, 1, 0>(channel, pixel, value);
break;
case GL_UNSIGNED_INT_2_10_10_10_REV:
read4Channel<GLuint, 2, 10, 10, 10, 30, 20, 10, 0>(3 - channel, pixel, value);
break;
case GL_UNSIGNED_INT_5_9_9_9_REV:
read4Channel<GLuint, 5, 9, 9, 9, 27, 18, 9, 0>(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");
}
}
/** 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<GLubyte>(channel, value, pixel);
break;
case GL_UNSIGNED_SHORT:
writeBaseTypeToUnsignedChannel<GLushort>(channel, value, pixel);
break;
case GL_UNSIGNED_INT:
writeBaseTypeToUnsignedChannel<GLuint>(channel, value, pixel);
break;
case GL_BYTE:
writeBaseTypeToSignedChannel<GLbyte>(channel, value, pixel);
break;
case GL_SHORT:
writeBaseTypeToSignedChannel<GLshort>(channel, value, pixel);
break;
case GL_INT:
writeBaseTypeToSignedChannel<GLint>(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<GLubyte, 3, 3, 2, 5, 2, 0>(channel, value, pixel);
break;
case GL_UNSIGNED_SHORT_5_6_5:
write3Channel<GLushort, 5, 6, 5, 11, 5, 0>(channel, value, pixel);
break;
/* 4 channels */
case GL_UNSIGNED_SHORT_4_4_4_4:
write4Channel<GLushort, 4, 4, 4, 4, 12, 8, 4, 0>(channel, value, pixel);
break;
case GL_UNSIGNED_SHORT_5_5_5_1:
write4Channel<GLushort, 5, 5, 5, 1, 11, 6, 1, 0>(channel, value, pixel);
break;
case GL_UNSIGNED_INT_2_10_10_10_REV:
write4Channel<GLuint, 2, 10, 10, 10, 30, 20, 10, 0>(3 - channel, value, pixel);
break;
case GL_UNSIGNED_INT_5_9_9_9_REV:
write4Channel<GLuint, 5, 9, 9, 9, 27, 18, 9, 0>(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");
}
}
/** 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");
}
}
/** 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");
}
}
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<int>(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 <level>
* @param height Height of image at <level>
* @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