/*-------------------------------------------------------------------------
* drawElements Quality Program Tester Core
* ----------------------------------------
*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Texture utilities.
*//*--------------------------------------------------------------------*/
#include "tcuTextureUtil.hpp"
#include "tcuVectorUtil.hpp"
#include "deRandom.hpp"
#include "deMath.h"
#include "deMemory.h"
#include <limits>
namespace tcu
{
static inline float sRGBChannelToLinear (float cs)
{
if (cs <= 0.04045)
return cs / 12.92f;
else
return deFloatPow((cs + 0.055f) / 1.055f, 2.4f);
}
static const deUint32 s_srgb8Lut[256] =
{
#include "tcuSRGB8Lut.inl"
};
static inline float sRGB8ChannelToLinear (deUint32 cs)
{
DE_ASSERT(cs < 256);
// \note This triggers UB, but in practice it doesn't cause any problems
return ((const float*)s_srgb8Lut)[cs];
}
static inline float linearChannelToSRGB (float cl)
{
if (cl <= 0.0f)
return 0.0f;
else if (cl < 0.0031308f)
return 12.92f*cl;
else if (cl < 1.0f)
return 1.055f*deFloatPow(cl, 0.41666f) - 0.055f;
else
return 1.0f;
}
//! Convert sRGB to linear colorspace
Vec4 sRGBToLinear (const Vec4& cs)
{
return Vec4(sRGBChannelToLinear(cs[0]),
sRGBChannelToLinear(cs[1]),
sRGBChannelToLinear(cs[2]),
cs[3]);
}
Vec4 sRGB8ToLinear (const UVec4& cs)
{
return Vec4(sRGB8ChannelToLinear(cs[0]),
sRGB8ChannelToLinear(cs[1]),
sRGB8ChannelToLinear(cs[2]),
1.0f);
}
Vec4 sRGBA8ToLinear (const UVec4& cs)
{
return Vec4(sRGB8ChannelToLinear(cs[0]),
sRGB8ChannelToLinear(cs[1]),
sRGB8ChannelToLinear(cs[2]),
(float)cs[3] / 255.0f);
}
//! Convert from linear to sRGB colorspace
Vec4 linearToSRGB (const Vec4& cl)
{
return Vec4(linearChannelToSRGB(cl[0]),
linearChannelToSRGB(cl[1]),
linearChannelToSRGB(cl[2]),
cl[3]);
}
bool isSRGB (TextureFormat format)
{
// make sure to update this if type table is updated
DE_STATIC_ASSERT(TextureFormat::CHANNELORDER_LAST == 21);
return format.order == TextureFormat::sR ||
format.order == TextureFormat::sRG ||
format.order == TextureFormat::sRGB ||
format.order == TextureFormat::sRGBA ||
format.order == TextureFormat::sBGR ||
format.order == TextureFormat::sBGRA;
}
tcu::Vec4 linearToSRGBIfNeeded (const TextureFormat& format, const tcu::Vec4& color)
{
return isSRGB(format) ? linearToSRGB(color) : color;
}
bool isCombinedDepthStencilType (TextureFormat::ChannelType type)
{
// make sure to update this if type table is updated
DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 40);
return type == TextureFormat::UNSIGNED_INT_16_8_8 ||
type == TextureFormat::UNSIGNED_INT_24_8 ||
type == TextureFormat::UNSIGNED_INT_24_8_REV ||
type == TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV;
}
bool hasStencilComponent (TextureFormat::ChannelOrder order)
{
DE_STATIC_ASSERT(TextureFormat::CHANNELORDER_LAST == 21);
switch (order)
{
case TextureFormat::S:
case TextureFormat::DS:
return true;
default:
return false;
}
}
bool hasDepthComponent (TextureFormat::ChannelOrder order)
{
DE_STATIC_ASSERT(TextureFormat::CHANNELORDER_LAST == 21);
switch (order)
{
case TextureFormat::D:
case TextureFormat::DS:
return true;
default:
return false;
}
}
//! Get texture channel class for format
TextureChannelClass getTextureChannelClass (TextureFormat::ChannelType channelType)
{
// make sure this table is updated if format table is updated
DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 40);
switch (channelType)
{
case TextureFormat::SNORM_INT8: return TEXTURECHANNELCLASS_SIGNED_FIXED_POINT;
case TextureFormat::SNORM_INT16: return TEXTURECHANNELCLASS_SIGNED_FIXED_POINT;
case TextureFormat::SNORM_INT32: return TEXTURECHANNELCLASS_SIGNED_FIXED_POINT;
case TextureFormat::UNORM_INT8: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::UNORM_INT16: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::UNORM_INT24: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::UNORM_INT32: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::UNORM_BYTE_44: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::UNORM_SHORT_565: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::UNORM_SHORT_555: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::UNORM_SHORT_4444: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::UNORM_SHORT_5551: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::UNORM_SHORT_1555: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::UNSIGNED_BYTE_44: return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
case TextureFormat::UNSIGNED_SHORT_565: return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
case TextureFormat::UNSIGNED_SHORT_4444: return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
case TextureFormat::UNSIGNED_SHORT_5551: return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
case TextureFormat::UNORM_INT_101010: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::SNORM_INT_1010102_REV: return TEXTURECHANNELCLASS_SIGNED_FIXED_POINT;
case TextureFormat::UNORM_INT_1010102_REV: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::SIGNED_INT_1010102_REV: return TEXTURECHANNELCLASS_SIGNED_INTEGER;
case TextureFormat::UNSIGNED_INT_1010102_REV: return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV: return TEXTURECHANNELCLASS_FLOATING_POINT;
case TextureFormat::UNSIGNED_INT_999_E5_REV: return TEXTURECHANNELCLASS_FLOATING_POINT;
case TextureFormat::UNSIGNED_INT_16_8_8: return TEXTURECHANNELCLASS_LAST; //!< packed unorm16-x8-uint8
case TextureFormat::UNSIGNED_INT_24_8: return TEXTURECHANNELCLASS_LAST; //!< packed unorm24-uint8
case TextureFormat::UNSIGNED_INT_24_8_REV: return TEXTURECHANNELCLASS_LAST; //!< packed unorm24-uint8
case TextureFormat::SIGNED_INT8: return TEXTURECHANNELCLASS_SIGNED_INTEGER;
case TextureFormat::SIGNED_INT16: return TEXTURECHANNELCLASS_SIGNED_INTEGER;
case TextureFormat::SIGNED_INT32: return TEXTURECHANNELCLASS_SIGNED_INTEGER;
case TextureFormat::UNSIGNED_INT8: return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
case TextureFormat::UNSIGNED_INT16: return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
case TextureFormat::UNSIGNED_INT24: return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
case TextureFormat::UNSIGNED_INT32: return TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
case TextureFormat::HALF_FLOAT: return TEXTURECHANNELCLASS_FLOATING_POINT;
case TextureFormat::FLOAT: return TEXTURECHANNELCLASS_FLOATING_POINT;
case TextureFormat::FLOAT64: return TEXTURECHANNELCLASS_FLOATING_POINT;
case TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV: return TEXTURECHANNELCLASS_LAST; //!< packed float32-pad24-uint8
case TextureFormat::UNORM_SHORT_10: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
case TextureFormat::UNORM_SHORT_12: return TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
default:
DE_FATAL("Unknown channel type");
return TEXTURECHANNELCLASS_LAST;
}
}
bool isAccessValid (TextureFormat format, TextureAccessType type)
{
DE_ASSERT(isValid(format));
if (format.order == TextureFormat::DS)
{
// It is never allowed to access combined depth-stencil format with getPixel().
// Instead either getPixDepth() or getPixStencil(), or effective depth- or stencil-
// access must be used.
return false;
}
else if (format.order == TextureFormat::D)
return type == TEXTUREACCESSTYPE_FLOAT;
else if (format.order == TextureFormat::S)
return type == TEXTUREACCESSTYPE_UNSIGNED_INT;
else
{
// A few packed color formats have access type restrictions
if (format.type == TextureFormat::UNSIGNED_INT_11F_11F_10F_REV ||
format.type == TextureFormat::UNSIGNED_INT_999_E5_REV)
return type == TEXTUREACCESSTYPE_FLOAT;
else
return true;
}
}
/*--------------------------------------------------------------------*//*!
* \brief Get access to subregion of pixel buffer
* \param access Parent access object
* \param x X offset
* \param y Y offset
* \param z Z offset
* \param width Width
* \param height Height
* \param depth Depth
* \return Access object that targets given subregion of parent access object
*//*--------------------------------------------------------------------*/
ConstPixelBufferAccess getSubregion (const ConstPixelBufferAccess& access, int x, int y, int z, int width, int height, int depth)
{
DE_ASSERT(de::inBounds(x, 0, access.getWidth()));
DE_ASSERT(de::inRange(x+width, x+1, access.getWidth()));
DE_ASSERT(de::inBounds(y, 0, access.getHeight()));
DE_ASSERT(de::inRange(y+height, y+1, access.getHeight()));
DE_ASSERT(de::inBounds(z, 0, access.getDepth()));
DE_ASSERT(de::inRange(z+depth, z+1, access.getDepth()));
return ConstPixelBufferAccess(access.getFormat(), tcu::IVec3(width, height, depth), access.getPitch(),
(const deUint8*)access.getDataPtr() + access.getPixelPitch()*x + access.getRowPitch()*y + access.getSlicePitch()*z);
}
/*--------------------------------------------------------------------*//*!
* \brief Get access to subregion of pixel buffer
* \param access Parent access object
* \param x X offset
* \param y Y offset
* \param z Z offset
* \param width Width
* \param height Height
* \param depth Depth
* \return Access object that targets given subregion of parent access object
*//*--------------------------------------------------------------------*/
PixelBufferAccess getSubregion (const PixelBufferAccess& access, int x, int y, int z, int width, int height, int depth)
{
DE_ASSERT(de::inBounds(x, 0, access.getWidth()));
DE_ASSERT(de::inRange(x+width, x+1, access.getWidth()));
DE_ASSERT(de::inBounds(y, 0, access.getHeight()));
DE_ASSERT(de::inRange(y+height, y+1, access.getHeight()));
DE_ASSERT(de::inBounds(z, 0, access.getDepth()));
DE_ASSERT(de::inRange(z+depth, z+1, access.getDepth()));
return PixelBufferAccess(access.getFormat(), tcu::IVec3(width, height, depth), access.getPitch(),
(deUint8*)access.getDataPtr() + access.getPixelPitch()*x + access.getRowPitch()*y + access.getSlicePitch()*z);
}
/*--------------------------------------------------------------------*//*!
* \brief Get access to subregion of pixel buffer
* \param access Parent access object
* \param x X offset
* \param y Y offset
* \param width Width
* \param height Height
* \return Access object that targets given subregion of parent access object
*//*--------------------------------------------------------------------*/
PixelBufferAccess getSubregion (const PixelBufferAccess& access, int x, int y, int width, int height)
{
return getSubregion(access, x, y, 0, width, height, 1);
}
/*--------------------------------------------------------------------*//*!
* \brief Get access to subregion of pixel buffer
* \param access Parent access object
* \param x X offset
* \param y Y offset
* \param width Width
* \param height Height
* \return Access object that targets given subregion of parent access object
*//*--------------------------------------------------------------------*/
ConstPixelBufferAccess getSubregion (const ConstPixelBufferAccess& access, int x, int y, int width, int height)
{
return getSubregion(access, x, y, 0, width, height, 1);
}
/*--------------------------------------------------------------------*//*!
* \brief Flip rows in Y direction
* \param access Access object
* \return Modified access object where Y coordinates are reversed
*//*--------------------------------------------------------------------*/
PixelBufferAccess flipYAccess (const PixelBufferAccess& access)
{
const int rowPitch = access.getRowPitch();
const int offsetToLast = rowPitch*(access.getHeight()-1);
const tcu::IVec3 pitch (access.getPixelPitch(), -rowPitch, access.getSlicePitch());
return PixelBufferAccess(access.getFormat(), access.getSize(), pitch, (deUint8*)access.getDataPtr() + offsetToLast);
}
/*--------------------------------------------------------------------*//*!
* \brief Flip rows in Y direction
* \param access Access object
* \return Modified access object where Y coordinates are reversed
*//*--------------------------------------------------------------------*/
ConstPixelBufferAccess flipYAccess (const ConstPixelBufferAccess& access)
{
const int rowPitch = access.getRowPitch();
const int offsetToLast = rowPitch*(access.getHeight()-1);
const tcu::IVec3 pitch (access.getPixelPitch(), -rowPitch, access.getSlicePitch());
return ConstPixelBufferAccess(access.getFormat(), access.getSize(), pitch, (deUint8*)access.getDataPtr() + offsetToLast);
}
static Vec2 getFloatChannelValueRange (TextureFormat::ChannelType channelType)
{
// make sure this table is updated if format table is updated
DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 40);
float cMin = 0.0f;
float cMax = 0.0f;
switch (channelType)
{
// Signed normalized formats.
case TextureFormat::SNORM_INT8:
case TextureFormat::SNORM_INT16:
case TextureFormat::SNORM_INT32:
case TextureFormat::SNORM_INT_1010102_REV: cMin = -1.0f; cMax = 1.0f; break;
// Unsigned normalized formats.
case TextureFormat::UNORM_INT8:
case TextureFormat::UNORM_INT16:
case TextureFormat::UNORM_INT24:
case TextureFormat::UNORM_INT32:
case TextureFormat::UNORM_BYTE_44:
case TextureFormat::UNORM_SHORT_565:
case TextureFormat::UNORM_SHORT_555:
case TextureFormat::UNORM_SHORT_4444:
case TextureFormat::UNORM_SHORT_5551:
case TextureFormat::UNORM_SHORT_1555:
case TextureFormat::UNORM_INT_101010:
case TextureFormat::UNORM_INT_1010102_REV:
case TextureFormat::UNORM_SHORT_10:
case TextureFormat::UNORM_SHORT_12: cMin = 0.0f; cMax = 1.0f; break;
// Misc formats.
case TextureFormat::SIGNED_INT8: cMin = -128.0f; cMax = 127.0f; break;
case TextureFormat::SIGNED_INT16: cMin = -32768.0f; cMax = 32767.0f; break;
case TextureFormat::SIGNED_INT32: cMin = -2147483520.0f; cMax = 2147483520.0f; break; // Maximum exactly representable 31-bit integer: (2^24 - 1) * 2^7
case TextureFormat::UNSIGNED_INT8: cMin = 0.0f; cMax = 255.0f; break;
case TextureFormat::UNSIGNED_INT16: cMin = 0.0f; cMax = 65535.0f; break;
case TextureFormat::UNSIGNED_INT24: cMin = 0.0f; cMax = 16777215.0f; break;
case TextureFormat::UNSIGNED_INT32: cMin = 0.0f; cMax = 4294967040.f; break; // Maximum exactly representable 32-bit integer: (2^24 - 1) * 2^8
case TextureFormat::HALF_FLOAT: cMin = -1e3f; cMax = 1e3f; break;
case TextureFormat::FLOAT: cMin = -1e5f; cMax = 1e5f; break;
case TextureFormat::FLOAT64: cMin = -1e5f; cMax = 1e5f; break;
case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV: cMin = 0.0f; cMax = 1e4f; break;
case TextureFormat::UNSIGNED_INT_999_E5_REV: cMin = 0.0f; cMax = 1e5f; break;
case TextureFormat::UNSIGNED_BYTE_44: cMin = 0.0f; cMax = 15.f; break;
case TextureFormat::UNSIGNED_SHORT_4444: cMin = 0.0f; cMax = 15.f; break;
default:
DE_ASSERT(false);
}
return Vec2(cMin, cMax);
}
/*--------------------------------------------------------------------*//*!
* \brief Get standard parameters for testing texture format
*
* Returns TextureFormatInfo that describes good parameters for exercising
* given TextureFormat. Parameters include value ranges per channel and
* suitable lookup scaling and bias in order to reduce result back to
* 0..1 range.
*//*--------------------------------------------------------------------*/
TextureFormatInfo getTextureFormatInfo (const TextureFormat& format)
{
// Special cases.
if (format.type == TextureFormat::UNSIGNED_INT_1010102_REV)
return TextureFormatInfo(Vec4( 0.0f, 0.0f, 0.0f, 0.0f),
Vec4( 1023.0f, 1023.0f, 1023.0f, 3.0f),
Vec4(1.0f/1023.f, 1.0f/1023.0f, 1.0f/1023.0f, 1.0f/3.0f),
Vec4( 0.0f, 0.0f, 0.0f, 0.0f));
if (format.type == TextureFormat::SIGNED_INT_1010102_REV)
return TextureFormatInfo(Vec4( -512.0f, -512.0f, -512.0f, -2.0f),
Vec4( 511.0f, 511.0f, 511.0f, 1.0f),
Vec4(1.0f/1023.f, 1.0f/1023.0f, 1.0f/1023.0f, 1.0f/3.0f),
Vec4( 0.5f, 0.5f, 0.5f, 0.5f));
else if (format.order == TextureFormat::D || format.order == TextureFormat::DS)
return TextureFormatInfo(Vec4(0.0f, 0.0f, 0.0f, 0.0f),
Vec4(1.0f, 1.0f, 1.0f, 0.0f),
Vec4(1.0f, 1.0f, 1.0f, 1.0f),
Vec4(0.0f, 0.0f, 0.0f, 0.0f)); // Depth / stencil formats.
else if (format == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_5551))
return TextureFormatInfo(Vec4(0.0f, 0.0f, 0.0f, 0.5f),
Vec4(1.0f, 1.0f, 1.0f, 1.5f),
Vec4(1.0f, 1.0f, 1.0f, 1.0f),
Vec4(0.0f, 0.0f, 0.0f, 0.0f));
else if (format.type == TextureFormat::UNSIGNED_SHORT_5551)
return TextureFormatInfo(Vec4( 0.0f, 0.0f, 0.0f, 0.0f),
Vec4( 31.0f, 31.0f, 31.0f, 1.0f),
Vec4(1.0f/31.f, 1.0f/31.0f, 1.0f/31.0f, 1.0f),
Vec4( 0.0f, 0.0f, 0.0f, 0.0f));
else if (format.type == TextureFormat::UNSIGNED_SHORT_565)
return TextureFormatInfo(Vec4( 0.0f, 0.0f, 0.0f, 0.0f),
Vec4( 31.0f, 63.0f, 31.0f, 0.0f),
Vec4(1.0f/31.f, 1.0f/63.0f, 1.0f/31.0f, 1.0f),
Vec4( 0.0f, 0.0f, 0.0f, 0.0f));
const Vec2 cRange = getFloatChannelValueRange(format.type);
const TextureSwizzle::Channel* map = getChannelReadSwizzle(format.order).components;
const BVec4 chnMask = BVec4(deInRange32(map[0], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[1], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[2], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[3], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE);
const float scale = 1.0f / (cRange[1] - cRange[0]);
const float bias = -cRange[0] * scale;
return TextureFormatInfo(select(cRange[0], 0.0f, chnMask),
select(cRange[1], 0.0f, chnMask),
select(scale, 1.0f, chnMask),
select(bias, 0.0f, chnMask));
}
IVec4 getFormatMinIntValue (const TextureFormat& format)
{
DE_ASSERT(getTextureChannelClass(format.type) == TEXTURECHANNELCLASS_SIGNED_INTEGER);
switch (format.type)
{
case TextureFormat::SIGNED_INT8: return IVec4(std::numeric_limits<deInt8>::min());
case TextureFormat::SIGNED_INT16: return IVec4(std::numeric_limits<deInt16>::min());
case TextureFormat::SIGNED_INT32: return IVec4(std::numeric_limits<deInt32>::min());
default:
DE_FATAL("Invalid channel type");
return IVec4(0);
}
}
IVec4 getFormatMaxIntValue (const TextureFormat& format)
{
DE_ASSERT(getTextureChannelClass(format.type) == TEXTURECHANNELCLASS_SIGNED_INTEGER);
if (format == TextureFormat(TextureFormat::RGBA, TextureFormat::SIGNED_INT_1010102_REV) ||
format == TextureFormat(TextureFormat::BGRA, TextureFormat::SIGNED_INT_1010102_REV))
return IVec4(511, 511, 511, 1);
switch (format.type)
{
case TextureFormat::SIGNED_INT8: return IVec4(std::numeric_limits<deInt8>::max());
case TextureFormat::SIGNED_INT16: return IVec4(std::numeric_limits<deInt16>::max());
case TextureFormat::SIGNED_INT32: return IVec4(std::numeric_limits<deInt32>::max());
default:
DE_FATAL("Invalid channel type");
return IVec4(0);
}
}
UVec4 getFormatMaxUintValue (const TextureFormat& format)
{
DE_ASSERT(getTextureChannelClass(format.type) == TEXTURECHANNELCLASS_UNSIGNED_INTEGER);
if (format == TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT_1010102_REV) ||
format == TextureFormat(TextureFormat::BGRA, TextureFormat::UNSIGNED_INT_1010102_REV))
return UVec4(1023u, 1023u, 1023u, 3u);
switch (format.type)
{
case TextureFormat::UNSIGNED_INT8: return UVec4(std::numeric_limits<deUint8>::max());
case TextureFormat::UNSIGNED_INT16: return UVec4(std::numeric_limits<deUint16>::max());
case TextureFormat::UNSIGNED_INT24: return UVec4(0xffffffu);
case TextureFormat::UNSIGNED_INT32: return UVec4(std::numeric_limits<deUint32>::max());
default:
DE_FATAL("Invalid channel type");
return UVec4(0);
}
}
static IVec4 getChannelBitDepth (TextureFormat::ChannelType channelType)
{
// make sure this table is updated if format table is updated
DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 40);
switch (channelType)
{
case TextureFormat::SNORM_INT8: return IVec4(8);
case TextureFormat::SNORM_INT16: return IVec4(16);
case TextureFormat::SNORM_INT32: return IVec4(32);
case TextureFormat::UNORM_INT8: return IVec4(8);
case TextureFormat::UNORM_INT16: return IVec4(16);
case TextureFormat::UNORM_INT24: return IVec4(24);
case TextureFormat::UNORM_INT32: return IVec4(32);
case TextureFormat::UNORM_BYTE_44: return IVec4(4,4,0,0);
case TextureFormat::UNORM_SHORT_565: return IVec4(5,6,5,0);
case TextureFormat::UNORM_SHORT_4444: return IVec4(4);
case TextureFormat::UNORM_SHORT_555: return IVec4(5,5,5,0);
case TextureFormat::UNORM_SHORT_5551: return IVec4(5,5,5,1);
case TextureFormat::UNORM_SHORT_1555: return IVec4(1,5,5,5);
case TextureFormat::UNSIGNED_BYTE_44: return IVec4(4,4,0,0);
case TextureFormat::UNSIGNED_SHORT_565: return IVec4(5,6,5,0);
case TextureFormat::UNSIGNED_SHORT_4444: return IVec4(4);
case TextureFormat::UNSIGNED_SHORT_5551: return IVec4(5,5,5,1);
case TextureFormat::UNORM_INT_101010: return IVec4(10,10,10,0);
case TextureFormat::SNORM_INT_1010102_REV: return IVec4(10,10,10,2);
case TextureFormat::UNORM_INT_1010102_REV: return IVec4(10,10,10,2);
case TextureFormat::SIGNED_INT8: return IVec4(8);
case TextureFormat::SIGNED_INT16: return IVec4(16);
case TextureFormat::SIGNED_INT32: return IVec4(32);
case TextureFormat::UNSIGNED_INT8: return IVec4(8);
case TextureFormat::UNSIGNED_INT16: return IVec4(16);
case TextureFormat::UNSIGNED_INT24: return IVec4(24);
case TextureFormat::UNSIGNED_INT32: return IVec4(32);
case TextureFormat::SIGNED_INT_1010102_REV: return IVec4(10,10,10,2);
case TextureFormat::UNSIGNED_INT_1010102_REV: return IVec4(10,10,10,2);
case TextureFormat::UNSIGNED_INT_16_8_8: return IVec4(16,8,0,0);
case TextureFormat::UNSIGNED_INT_24_8: return IVec4(24,8,0,0);
case TextureFormat::UNSIGNED_INT_24_8_REV: return IVec4(24,8,0,0);
case TextureFormat::HALF_FLOAT: return IVec4(16);
case TextureFormat::FLOAT: return IVec4(32);
case TextureFormat::FLOAT64: return IVec4(64);
case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV: return IVec4(11,11,10,0);
case TextureFormat::UNSIGNED_INT_999_E5_REV: return IVec4(9,9,9,0);
case TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV: return IVec4(32,8,0,0);
case TextureFormat::UNORM_SHORT_10: return IVec4(10);
case TextureFormat::UNORM_SHORT_12: return IVec4(12);
default:
DE_ASSERT(false);
return IVec4(0);
}
}
IVec4 getTextureFormatBitDepth (const TextureFormat& format)
{
const IVec4 chnBits = getChannelBitDepth(format.type);
const TextureSwizzle::Channel* map = getChannelReadSwizzle(format.order).components;
const BVec4 chnMask = BVec4(deInRange32(map[0], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[1], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[2], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[3], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE);
const IVec4 chnSwz = IVec4((chnMask[0]) ? ((int)map[0]) : (0),
(chnMask[1]) ? ((int)map[1]) : (0),
(chnMask[2]) ? ((int)map[2]) : (0),
(chnMask[3]) ? ((int)map[3]) : (0));
return select(chnBits.swizzle(chnSwz.x(), chnSwz.y(), chnSwz.z(), chnSwz.w()), IVec4(0), chnMask);
}
static IVec4 getChannelMantissaBitDepth (TextureFormat::ChannelType channelType)
{
// make sure this table is updated if format table is updated
DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 40);
switch (channelType)
{
case TextureFormat::SNORM_INT8:
case TextureFormat::SNORM_INT16:
case TextureFormat::SNORM_INT32:
case TextureFormat::UNORM_INT8:
case TextureFormat::UNORM_INT16:
case TextureFormat::UNORM_INT24:
case TextureFormat::UNORM_INT32:
case TextureFormat::UNORM_BYTE_44:
case TextureFormat::UNORM_SHORT_565:
case TextureFormat::UNORM_SHORT_4444:
case TextureFormat::UNORM_SHORT_555:
case TextureFormat::UNORM_SHORT_5551:
case TextureFormat::UNORM_SHORT_1555:
case TextureFormat::UNSIGNED_BYTE_44:
case TextureFormat::UNSIGNED_SHORT_565:
case TextureFormat::UNSIGNED_SHORT_4444:
case TextureFormat::UNSIGNED_SHORT_5551:
case TextureFormat::UNORM_INT_101010:
case TextureFormat::SNORM_INT_1010102_REV:
case TextureFormat::UNORM_INT_1010102_REV:
case TextureFormat::SIGNED_INT8:
case TextureFormat::SIGNED_INT16:
case TextureFormat::SIGNED_INT32:
case TextureFormat::UNSIGNED_INT8:
case TextureFormat::UNSIGNED_INT16:
case TextureFormat::UNSIGNED_INT24:
case TextureFormat::UNSIGNED_INT32:
case TextureFormat::SIGNED_INT_1010102_REV:
case TextureFormat::UNSIGNED_INT_1010102_REV:
case TextureFormat::UNSIGNED_INT_16_8_8:
case TextureFormat::UNSIGNED_INT_24_8:
case TextureFormat::UNSIGNED_INT_24_8_REV:
case TextureFormat::UNSIGNED_INT_999_E5_REV:
case TextureFormat::UNORM_SHORT_10:
case TextureFormat::UNORM_SHORT_12:
return getChannelBitDepth(channelType);
case TextureFormat::HALF_FLOAT: return IVec4(10);
case TextureFormat::FLOAT: return IVec4(23);
case TextureFormat::FLOAT64: return IVec4(52);
case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV: return IVec4(6,6,5,0);
case TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV: return IVec4(23,8,0,0);
default:
DE_ASSERT(false);
return IVec4(0);
}
}
IVec4 getTextureFormatMantissaBitDepth (const TextureFormat& format)
{
const IVec4 chnBits = getChannelMantissaBitDepth(format.type);
const TextureSwizzle::Channel* map = getChannelReadSwizzle(format.order).components;
const BVec4 chnMask = BVec4(deInRange32(map[0], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[1], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[2], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[3], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE);
const IVec4 chnSwz = IVec4((chnMask[0]) ? ((int)map[0]) : (0),
(chnMask[1]) ? ((int)map[1]) : (0),
(chnMask[2]) ? ((int)map[2]) : (0),
(chnMask[3]) ? ((int)map[3]) : (0));
return select(chnBits.swizzle(chnSwz.x(), chnSwz.y(), chnSwz.z(), chnSwz.w()), IVec4(0), chnMask);
}
BVec4 getTextureFormatChannelMask (const TextureFormat& format)
{
const TextureSwizzle::Channel* const map = getChannelReadSwizzle(format.order).components;
return BVec4(deInRange32(map[0], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[1], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[2], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE,
deInRange32(map[3], TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE);
}
static inline float linearInterpolate (float t, float minVal, float maxVal)
{
return minVal + (maxVal - minVal) * t;
}
static inline Vec4 linearInterpolate (float t, const Vec4& a, const Vec4& b)
{
return a + (b - a) * t;
}
enum
{
CLEAR_OPTIMIZE_THRESHOLD = 128,
CLEAR_OPTIMIZE_MAX_PIXEL_SIZE = 8
};
inline void fillRow (const PixelBufferAccess& dst, int y, int z, int pixelSize, const deUint8* pixel)
{
DE_ASSERT(dst.getPixelPitch() == pixelSize); // only tightly packed
deUint8* dstPtr = (deUint8*)dst.getPixelPtr(0, y, z);
int width = dst.getWidth();
if (pixelSize == 8 && deIsAlignedPtr(dstPtr, pixelSize))
{
deUint64 val;
memcpy(&val, pixel, sizeof(val));
for (int i = 0; i < width; i++)
((deUint64*)dstPtr)[i] = val;
}
else if (pixelSize == 4 && deIsAlignedPtr(dstPtr, pixelSize))
{
deUint32 val;
memcpy(&val, pixel, sizeof(val));
for (int i = 0; i < width; i++)
((deUint32*)dstPtr)[i] = val;
}
else
{
for (int i = 0; i < width; i++)
for (int j = 0; j < pixelSize; j++)
dstPtr[i*pixelSize+j] = pixel[j];
}
}
void clear (const PixelBufferAccess& access, const Vec4& color)
{
const int pixelSize = access.getFormat().getPixelSize();
const int pixelPitch = access.getPixelPitch();
const bool rowPixelsTightlyPacked = (pixelSize == pixelPitch);
if (access.getWidth()*access.getHeight()*access.getDepth() >= CLEAR_OPTIMIZE_THRESHOLD &&
pixelSize < CLEAR_OPTIMIZE_MAX_PIXEL_SIZE && rowPixelsTightlyPacked)
{
// Convert to destination format.
union
{
deUint8 u8[CLEAR_OPTIMIZE_MAX_PIXEL_SIZE];
deUint64 u64; // Forces 64-bit alignment.
} pixel;
DE_STATIC_ASSERT(sizeof(pixel) == CLEAR_OPTIMIZE_MAX_PIXEL_SIZE);
PixelBufferAccess(access.getFormat(), 1, 1, 1, 0, 0, &pixel.u8[0]).setPixel(color, 0, 0);
for (int z = 0; z < access.getDepth(); z++)
for (int y = 0; y < access.getHeight(); y++)
fillRow(access, y, z, pixelSize, &pixel.u8[0]);
}
else
{
for (int z = 0; z < access.getDepth(); z++)
for (int y = 0; y < access.getHeight(); y++)
for (int x = 0; x < access.getWidth(); x++)
access.setPixel(color, x, y, z);
}
}
void clear (const PixelBufferAccess& access, const IVec4& color)
{
const int pixelSize = access.getFormat().getPixelSize();
const int pixelPitch = access.getPixelPitch();
const bool rowPixelsTightlyPacked = (pixelSize == pixelPitch);
if (access.getWidth()*access.getHeight()*access.getDepth() >= CLEAR_OPTIMIZE_THRESHOLD &&
pixelSize < CLEAR_OPTIMIZE_MAX_PIXEL_SIZE && rowPixelsTightlyPacked)
{
// Convert to destination format.
union
{
deUint8 u8[CLEAR_OPTIMIZE_MAX_PIXEL_SIZE];
deUint64 u64; // Forces 64-bit alignment.
} pixel;
DE_STATIC_ASSERT(sizeof(pixel) == CLEAR_OPTIMIZE_MAX_PIXEL_SIZE);
PixelBufferAccess(access.getFormat(), 1, 1, 1, 0, 0, &pixel.u8[0]).setPixel(color, 0, 0);
for (int z = 0; z < access.getDepth(); z++)
for (int y = 0; y < access.getHeight(); y++)
fillRow(access, y, z, pixelSize, &pixel.u8[0]);
}
else
{
for (int z = 0; z < access.getDepth(); z++)
for (int y = 0; y < access.getHeight(); y++)
for (int x = 0; x < access.getWidth(); x++)
access.setPixel(color, x, y, z);
}
}
void clear (const PixelBufferAccess& access, const UVec4& color)
{
clear(access, color.cast<deInt32>());
}
void clearDepth (const PixelBufferAccess& access, float depth)
{
DE_ASSERT(access.getFormat().order == TextureFormat::DS || access.getFormat().order == TextureFormat::D);
clear(getEffectiveDepthStencilAccess(access, Sampler::MODE_DEPTH), tcu::Vec4(depth, 0.0f, 0.0f, 0.0f));
}
void clearStencil (const PixelBufferAccess& access, int stencil)
{
DE_ASSERT(access.getFormat().order == TextureFormat::DS || access.getFormat().order == TextureFormat::S);
clear(getEffectiveDepthStencilAccess(access, Sampler::MODE_STENCIL), tcu::UVec4(stencil, 0u, 0u, 0u));
}
static void fillWithComponentGradients1D (const PixelBufferAccess& access, const Vec4& minVal, const Vec4& maxVal)
{
DE_ASSERT(access.getHeight() == 1);
for (int x = 0; x < access.getWidth(); x++)
{
float s = ((float)x + 0.5f) / (float)access.getWidth();
float r = linearInterpolate(s, minVal.x(), maxVal.x());
float g = linearInterpolate(s, minVal.y(), maxVal.y());
float b = linearInterpolate(s, minVal.z(), maxVal.z());
float a = linearInterpolate(s, minVal.w(), maxVal.w());
access.setPixel(tcu::Vec4(r, g, b, a), x, 0);
}
}
static void fillWithComponentGradients2D (const PixelBufferAccess& access, const Vec4& minVal, const Vec4& maxVal)
{
for (int y = 0; y < access.getHeight(); y++)
{
for (int x = 0; x < access.getWidth(); x++)
{
float s = ((float)x + 0.5f) / (float)access.getWidth();
float t = ((float)y + 0.5f) / (float)access.getHeight();
float r = linearInterpolate(( s + t) *0.5f, minVal.x(), maxVal.x());
float g = linearInterpolate(( s + (1.0f-t))*0.5f, minVal.y(), maxVal.y());
float b = linearInterpolate(((1.0f-s) + t) *0.5f, minVal.z(), maxVal.z());
float a = linearInterpolate(((1.0f-s) + (1.0f-t))*0.5f, minVal.w(), maxVal.w());
access.setPixel(tcu::Vec4(r, g, b, a), x, y);
}
}
}
static void fillWithComponentGradients3D (const PixelBufferAccess& dst, const Vec4& minVal, const Vec4& maxVal)
{
for (int z = 0; z < dst.getDepth(); z++)
{
for (int y = 0; y < dst.getHeight(); y++)
{
for (int x = 0; x < dst.getWidth(); x++)
{
float s = ((float)x + 0.5f) / (float)dst.getWidth();
float t = ((float)y + 0.5f) / (float)dst.getHeight();
float p = ((float)z + 0.5f) / (float)dst.getDepth();
float r = linearInterpolate(s, minVal.x(), maxVal.x());
float g = linearInterpolate(t, minVal.y(), maxVal.y());
float b = linearInterpolate(p, minVal.z(), maxVal.z());
float a = linearInterpolate(1.0f - (s+t+p)/3.0f, minVal.w(), maxVal.w());
dst.setPixel(tcu::Vec4(r, g, b, a), x, y, z);
}
}
}
}
void fillWithComponentGradients (const PixelBufferAccess& access, const Vec4& minVal, const Vec4& maxVal)
{
if (isCombinedDepthStencilType(access.getFormat().type))
{
const bool hasDepth = access.getFormat().order == tcu::TextureFormat::DS || access.getFormat().order == tcu::TextureFormat::D;
const bool hasStencil = access.getFormat().order == tcu::TextureFormat::DS || access.getFormat().order == tcu::TextureFormat::S;
DE_ASSERT(hasDepth || hasStencil);
// For combined formats, treat D and S as separate channels
if (hasDepth)
fillWithComponentGradients(getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_DEPTH), minVal, maxVal);
if (hasStencil)
fillWithComponentGradients(getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_STENCIL), minVal.swizzle(3,2,1,0), maxVal.swizzle(3,2,1,0));
}
else
{
if (access.getHeight() == 1 && access.getDepth() == 1)
fillWithComponentGradients1D(access, minVal, maxVal);
else if (access.getDepth() == 1)
fillWithComponentGradients2D(access, minVal, maxVal);
else
fillWithComponentGradients3D(access, minVal, maxVal);
}
}
static void fillWithGrid1D (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
{
for (int x = 0; x < access.getWidth(); x++)
{
int mx = (x / cellSize) % 2;
if (mx)
access.setPixel(colorB, x, 0);
else
access.setPixel(colorA, x, 0);
}
}
static void fillWithGrid2D (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
{
for (int y = 0; y < access.getHeight(); y++)
{
for (int x = 0; x < access.getWidth(); x++)
{
int mx = (x / cellSize) % 2;
int my = (y / cellSize) % 2;
if (mx ^ my)
access.setPixel(colorB, x, y);
else
access.setPixel(colorA, x, y);
}
}
}
static void fillWithGrid3D (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
{
for (int z = 0; z < access.getDepth(); z++)
{
for (int y = 0; y < access.getHeight(); y++)
{
for (int x = 0; x < access.getWidth(); x++)
{
int mx = (x / cellSize) % 2;
int my = (y / cellSize) % 2;
int mz = (z / cellSize) % 2;
if (mx ^ my ^ mz)
access.setPixel(colorB, x, y, z);
else
access.setPixel(colorA, x, y, z);
}
}
}
}
void fillWithGrid (const PixelBufferAccess& access, int cellSize, const Vec4& colorA, const Vec4& colorB)
{
if (isCombinedDepthStencilType(access.getFormat().type))
{
const bool hasDepth = access.getFormat().order == tcu::TextureFormat::DS || access.getFormat().order == tcu::TextureFormat::D;
const bool hasStencil = access.getFormat().order == tcu::TextureFormat::DS || access.getFormat().order == tcu::TextureFormat::S;
DE_ASSERT(hasDepth || hasStencil);
// For combined formats, treat D and S as separate channels
if (hasDepth)
fillWithGrid(getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_DEPTH), cellSize, colorA, colorB);
if (hasStencil)
fillWithGrid(getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_STENCIL), cellSize, colorA.swizzle(3,2,1,0), colorB.swizzle(3,2,1,0));
}
else
{
if (access.getHeight() == 1 && access.getDepth() == 1)
fillWithGrid1D(access, cellSize, colorA, colorB);
else if (access.getDepth() == 1)
fillWithGrid2D(access, cellSize, colorA, colorB);
else
fillWithGrid3D(access, cellSize, colorA, colorB);
}
}
void fillWithRepeatableGradient (const PixelBufferAccess& access, const Vec4& colorA, const Vec4& colorB)
{
for (int y = 0; y < access.getHeight(); y++)
{
for (int x = 0; x < access.getWidth(); x++)
{
float s = ((float)x + 0.5f) / (float)access.getWidth();
float t = ((float)y + 0.5f) / (float)access.getHeight();
float a = s > 0.5f ? (2.0f - 2.0f*s) : 2.0f*s;
float b = t > 0.5f ? (2.0f - 2.0f*t) : 2.0f*t;
float p = deFloatClamp(deFloatSqrt(a*a + b*b), 0.0f, 1.0f);
access.setPixel(linearInterpolate(p, colorA, colorB), x, y);
}
}
}
void fillWithRGBAQuads (const PixelBufferAccess& dst)
{
TCU_CHECK_INTERNAL(dst.getDepth() == 1);
int width = dst.getWidth();
int height = dst.getHeight();
int left = width/2;
int top = height/2;
clear(getSubregion(dst, 0, 0, 0, left, top, 1), Vec4(1.0f, 0.0f, 0.0f, 1.0f));
clear(getSubregion(dst, left, 0, 0, width-left, top, 1), Vec4(0.0f, 1.0f, 0.0f, 1.0f));
clear(getSubregion(dst, 0, top, 0, left, height-top, 1), Vec4(0.0f, 0.0f, 1.0f, 0.0f));
clear(getSubregion(dst, left, top, 0, width-left, height-top, 1), Vec4(0.5f, 0.5f, 0.5f, 1.0f));
}
// \todo [2012-11-13 pyry] There is much better metaballs code in CL SIR value generators.
void fillWithMetaballs (const PixelBufferAccess& dst, int numBalls, deUint32 seed)
{
TCU_CHECK_INTERNAL(dst.getDepth() == 1);
std::vector<Vec2> points(numBalls);
de::Random rnd(seed);
for (int i = 0; i < numBalls; i++)
{
float x = rnd.getFloat();
float y = rnd.getFloat();
points[i] = (Vec2(x, y));
}
for (int y = 0; y < dst.getHeight(); y++)
for (int x = 0; x < dst.getWidth(); x++)
{
Vec2 p((float)x/(float)dst.getWidth(), (float)y/(float)dst.getHeight());
float sum = 0.0f;
for (std::vector<Vec2>::const_iterator i = points.begin(); i != points.end(); i++)
{
Vec2 d = p - *i;
float f = 0.01f / (d.x()*d.x() + d.y()*d.y());
sum += f;
}
dst.setPixel(Vec4(sum), x, y);
}
}
void copy (const PixelBufferAccess& dst, const ConstPixelBufferAccess& src)
{
DE_ASSERT(src.getSize() == dst.getSize());
const int width = dst.getWidth();
const int height = dst.getHeight();
const int depth = dst.getDepth();
const int srcPixelSize = src.getFormat().getPixelSize();
const int dstPixelSize = dst.getFormat().getPixelSize();
const int srcPixelPitch = src.getPixelPitch();
const int dstPixelPitch = dst.getPixelPitch();
const bool srcTightlyPacked = (srcPixelSize == srcPixelPitch);
const bool dstTightlyPacked = (dstPixelSize == dstPixelPitch);
const bool srcHasDepth = (src.getFormat().order == tcu::TextureFormat::DS || src.getFormat().order == tcu::TextureFormat::D);
const bool srcHasStencil = (src.getFormat().order == tcu::TextureFormat::DS || src.getFormat().order == tcu::TextureFormat::S);
const bool dstHasDepth = (dst.getFormat().order == tcu::TextureFormat::DS || dst.getFormat().order == tcu::TextureFormat::D);
const bool dstHasStencil = (dst.getFormat().order == tcu::TextureFormat::DS || dst.getFormat().order == tcu::TextureFormat::S);
if (src.getFormat() == dst.getFormat() && srcTightlyPacked && dstTightlyPacked)
{
// Fast-path for matching formats.
for (int z = 0; z < depth; z++)
for (int y = 0; y < height; y++)
deMemcpy(dst.getPixelPtr(0, y, z), src.getPixelPtr(0, y, z), srcPixelSize*width);
}
else if (src.getFormat() == dst.getFormat())
{
// Bit-exact copy for matching formats.
for (int z = 0; z < depth; z++)
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
deMemcpy(dst.getPixelPtr(x, y, z), src.getPixelPtr(x, y, z), srcPixelSize);
}
else if (srcHasDepth || srcHasStencil || dstHasDepth || dstHasStencil)
{
DE_ASSERT((srcHasDepth && dstHasDepth) || (srcHasStencil && dstHasStencil)); // must have at least one common channel
if (dstHasDepth && srcHasDepth)
{
for (int z = 0; z < depth; z++)
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
dst.setPixDepth(src.getPixDepth(x, y, z), x, y, z);
}
else if (dstHasDepth && !srcHasDepth)
{
// consistency with color copies
tcu::clearDepth(dst, 0.0f);
}
if (dstHasStencil && srcHasStencil)
{
for (int z = 0; z < depth; z++)
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
dst.setPixStencil(src.getPixStencil(x, y, z), x, y, z);
}
else if (dstHasStencil && !srcHasStencil)
{
// consistency with color copies
tcu::clearStencil(dst, 0u);
}
}
else
{
TextureChannelClass srcClass = getTextureChannelClass(src.getFormat().type);
TextureChannelClass dstClass = getTextureChannelClass(dst.getFormat().type);
bool srcIsInt = srcClass == TEXTURECHANNELCLASS_SIGNED_INTEGER || srcClass == TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
bool dstIsInt = dstClass == TEXTURECHANNELCLASS_SIGNED_INTEGER || dstClass == TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
if (srcIsInt && dstIsInt)
{
for (int z = 0; z < depth; z++)
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
dst.setPixel(src.getPixelInt(x, y, z), x, y, z);
}
else
{
for (int z = 0; z < depth; z++)
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
dst.setPixel(src.getPixel(x, y, z), x, y, z);
}
}
}
void scale (const PixelBufferAccess& dst, const ConstPixelBufferAccess& src, Sampler::FilterMode filter)
{
DE_ASSERT(filter == Sampler::NEAREST || filter == Sampler::LINEAR);
Sampler sampler(Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE,
filter, filter, 0.0f, false);
float sX = (float)src.getWidth() / (float)dst.getWidth();
float sY = (float)src.getHeight() / (float)dst.getHeight();
float sZ = (float)src.getDepth() / (float)dst.getDepth();
if (dst.getDepth() == 1 && src.getDepth() == 1)
{
for (int y = 0; y < dst.getHeight(); y++)
for (int x = 0; x < dst.getWidth(); x++)
dst.setPixel(linearToSRGBIfNeeded(dst.getFormat(), src.sample2D(sampler, filter, ((float)x+0.5f)*sX, ((float)y+0.5f)*sY, 0)), x, y);
}
else
{
for (int z = 0; z < dst.getDepth(); z++)
for (int y = 0; y < dst.getHeight(); y++)
for (int x = 0; x < dst.getWidth(); x++)
dst.setPixel(linearToSRGBIfNeeded(dst.getFormat(), src.sample3D(sampler, filter, ((float)x+0.5f)*sX, ((float)y+0.5f)*sY, ((float)z+0.5f)*sZ)), x, y, z);
}
}
void estimatePixelValueRange (const ConstPixelBufferAccess& access, Vec4& minVal, Vec4& maxVal)
{
const TextureFormat& format = access.getFormat();
switch (getTextureChannelClass(format.type))
{
case TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
// Normalized unsigned formats.
minVal = Vec4(0.0f);
maxVal = Vec4(1.0f);
break;
case TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
// Normalized signed formats.
minVal = Vec4(-1.0f);
maxVal = Vec4(+1.0f);
break;
default:
// \note Samples every 4/8th pixel.
minVal = Vec4(std::numeric_limits<float>::max());
maxVal = Vec4(std::numeric_limits<float>::min());
for (int z = 0; z < access.getDepth(); z += 2)
{
for (int y = 0; y < access.getHeight(); y += 2)
{
for (int x = 0; x < access.getWidth(); x += 2)
{
Vec4 p = access.getPixel(x, y, z);
minVal[0] = (deFloatIsNaN(p[0]) ? minVal[0] : de::min(minVal[0], p[0]));
minVal[1] = (deFloatIsNaN(p[1]) ? minVal[1] : de::min(minVal[1], p[1]));
minVal[2] = (deFloatIsNaN(p[2]) ? minVal[2] : de::min(minVal[2], p[2]));
minVal[3] = (deFloatIsNaN(p[3]) ? minVal[3] : de::min(minVal[3], p[3]));
maxVal[0] = (deFloatIsNaN(p[0]) ? maxVal[0] : de::max(maxVal[0], p[0]));
maxVal[1] = (deFloatIsNaN(p[1]) ? maxVal[1] : de::max(maxVal[1], p[1]));
maxVal[2] = (deFloatIsNaN(p[2]) ? maxVal[2] : de::max(maxVal[2], p[2]));
maxVal[3] = (deFloatIsNaN(p[3]) ? maxVal[3] : de::max(maxVal[3], p[3]));
}
}
}
break;
}
}
void computePixelScaleBias (const ConstPixelBufferAccess& access, Vec4& scale, Vec4& bias)
{
Vec4 minVal, maxVal;
estimatePixelValueRange(access, minVal, maxVal);
const float eps = 0.0001f;
for (int c = 0; c < 4; c++)
{
if (maxVal[c] - minVal[c] < eps)
{
scale[c] = (maxVal[c] < eps) ? 1.0f : (1.0f / maxVal[c]);
bias[c] = (c == 3) ? (1.0f - maxVal[c]*scale[c]) : (0.0f - minVal[c]*scale[c]);
}
else
{
scale[c] = 1.0f / (maxVal[c] - minVal[c]);
bias[c] = 0.0f - minVal[c]*scale[c];
}
}
}
int getCubeArrayFaceIndex (CubeFace face)
{
DE_ASSERT((int)face >= 0 && face < CUBEFACE_LAST);
switch (face)
{
case CUBEFACE_POSITIVE_X: return 0;
case CUBEFACE_NEGATIVE_X: return 1;
case CUBEFACE_POSITIVE_Y: return 2;
case CUBEFACE_NEGATIVE_Y: return 3;
case CUBEFACE_POSITIVE_Z: return 4;
case CUBEFACE_NEGATIVE_Z: return 5;
default:
return -1;
}
}
deUint32 packRGB999E5 (const tcu::Vec4& color)
{
const int mBits = 9;
const int eBits = 5;
const int eBias = 15;
const int eMax = (1<<eBits)-1;
const float maxVal = (float)(((1<<mBits) - 1) * (1<<(eMax-eBias))) / (float)(1<<mBits);
float rc = deFloatClamp(color[0], 0.0f, maxVal);
float gc = deFloatClamp(color[1], 0.0f, maxVal);
float bc = deFloatClamp(color[2], 0.0f, maxVal);
float maxc = de::max(rc, de::max(gc, bc));
int expp = de::max(-eBias - 1, deFloorFloatToInt32(deFloatLog2(maxc))) + 1 + eBias;
float e = deFloatPow(2.0f, (float)(expp-eBias-mBits));
int maxs = deFloorFloatToInt32(maxc / e + 0.5f);
deUint32 exps = maxs == (1<<mBits) ? expp+1 : expp;
deUint32 rs = (deUint32)deClamp32(deFloorFloatToInt32(rc / e + 0.5f), 0, (1<<9)-1);
deUint32 gs = (deUint32)deClamp32(deFloorFloatToInt32(gc / e + 0.5f), 0, (1<<9)-1);
deUint32 bs = (deUint32)deClamp32(deFloorFloatToInt32(bc / e + 0.5f), 0, (1<<9)-1);
DE_ASSERT((exps & ~((1<<5)-1)) == 0);
DE_ASSERT((rs & ~((1<<9)-1)) == 0);
DE_ASSERT((gs & ~((1<<9)-1)) == 0);
DE_ASSERT((bs & ~((1<<9)-1)) == 0);
return rs | (gs << 9) | (bs << 18) | (exps << 27);
}
// Sampler utils
static const void* addOffset (const void* ptr, int numBytes)
{
return (const deUint8*)ptr + numBytes;
}
static void* addOffset (void* ptr, int numBytes)
{
return (deUint8*)ptr + numBytes;
}
template <typename AccessType>
static AccessType toSamplerAccess (const AccessType& baseAccess, Sampler::DepthStencilMode mode)
{
// make sure to update this if type table is updated
DE_STATIC_ASSERT(TextureFormat::CHANNELTYPE_LAST == 40);
if (!isCombinedDepthStencilType(baseAccess.getFormat().type))
return baseAccess;
else
{
#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
const deUint32 uint32ByteOffsetBits0To8 = 0; //!< least significant byte in the lowest address
const deUint32 uint32ByteOffsetBits0To24 = 0;
const deUint32 uint32ByteOffsetBits8To32 = 1;
const deUint32 uint32ByteOffsetBits16To32 = 2;
const deUint32 uint32ByteOffsetBits24To32 = 3;
#else
const deUint32 uint32ByteOffsetBits0To8 = 3; //!< least significant byte in the highest address
const deUint32 uint32ByteOffsetBits0To24 = 1;
const deUint32 uint32ByteOffsetBits8To32 = 0;
const deUint32 uint32ByteOffsetBits16To32 = 0;
const deUint32 uint32ByteOffsetBits24To32 = 0;
#endif
// Sampled channel must exist
DE_ASSERT(baseAccess.getFormat().order == TextureFormat::DS ||
(mode == Sampler::MODE_DEPTH && baseAccess.getFormat().order == TextureFormat::D) ||
(mode == Sampler::MODE_STENCIL && baseAccess.getFormat().order == TextureFormat::S));
// combined formats have multiple channel classes, detect on sampler settings
switch (baseAccess.getFormat().type)
{
case TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV:
{
if (mode == Sampler::MODE_DEPTH)
{
// select the float component
return AccessType(TextureFormat(TextureFormat::D, TextureFormat::FLOAT),
baseAccess.getSize(),
baseAccess.getPitch(),
baseAccess.getDataPtr());
}
else if (mode == Sampler::MODE_STENCIL)
{
// select the uint 8 component
return AccessType(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8),
baseAccess.getSize(),
baseAccess.getPitch(),
addOffset(baseAccess.getDataPtr(), 4 + uint32ByteOffsetBits0To8));
}
else
{
// unknown sampler mode
DE_ASSERT(false);
return AccessType();
}
}
case TextureFormat::UNSIGNED_INT_16_8_8:
{
if (mode == Sampler::MODE_DEPTH)
{
// select the unorm16 component
return AccessType(TextureFormat(TextureFormat::D, TextureFormat::UNORM_INT16),
baseAccess.getSize(),
baseAccess.getPitch(),
addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits16To32));
}
else if (mode == Sampler::MODE_STENCIL)
{
// select the uint 8 component
return AccessType(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8),
baseAccess.getSize(),
baseAccess.getPitch(),
addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits0To8));
}
else
{
// unknown sampler mode
DE_ASSERT(false);
return AccessType();
}
}
case TextureFormat::UNSIGNED_INT_24_8:
{
if (mode == Sampler::MODE_DEPTH)
{
// select the unorm24 component
return AccessType(TextureFormat(TextureFormat::D, TextureFormat::UNORM_INT24),
baseAccess.getSize(),
baseAccess.getPitch(),
addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits8To32));
}
else if (mode == Sampler::MODE_STENCIL)
{
// select the uint 8 component
return AccessType(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8),
baseAccess.getSize(),
baseAccess.getPitch(),
addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits0To8));
}
else
{
// unknown sampler mode
DE_ASSERT(false);
return AccessType();
}
}
case TextureFormat::UNSIGNED_INT_24_8_REV:
{
if (mode == Sampler::MODE_DEPTH)
{
// select the unorm24 component
return AccessType(TextureFormat(TextureFormat::D, TextureFormat::UNORM_INT24),
baseAccess.getSize(),
baseAccess.getPitch(),
addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits0To24));
}
else if (mode == Sampler::MODE_STENCIL)
{
// select the uint 8 component
return AccessType(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8),
baseAccess.getSize(),
baseAccess.getPitch(),
addOffset(baseAccess.getDataPtr(), uint32ByteOffsetBits24To32));
}
else
{
// unknown sampler mode
DE_ASSERT(false);
return AccessType();
}
}
default:
{
// unknown combined format
DE_ASSERT(false);
return AccessType();
}
}
}
}
PixelBufferAccess getEffectiveDepthStencilAccess (const PixelBufferAccess& baseAccess, Sampler::DepthStencilMode mode)
{
return toSamplerAccess<PixelBufferAccess>(baseAccess, mode);
}
ConstPixelBufferAccess getEffectiveDepthStencilAccess (const ConstPixelBufferAccess& baseAccess, Sampler::DepthStencilMode mode)
{
return toSamplerAccess<ConstPixelBufferAccess>(baseAccess, mode);
}
TextureFormat getEffectiveDepthStencilTextureFormat (const TextureFormat& baseFormat, Sampler::DepthStencilMode mode)
{
return toSamplerAccess(ConstPixelBufferAccess(baseFormat, IVec3(0, 0, 0), DE_NULL), mode).getFormat();
}
template <typename ViewType>
ViewType getEffectiveTView (const ViewType& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
storage.resize(src.getNumLevels());
ViewType view = ViewType(src.getNumLevels(), &storage[0]);
for (int levelNdx = 0; levelNdx < src.getNumLevels(); ++levelNdx)
storage[levelNdx] = tcu::getEffectiveDepthStencilAccess(src.getLevel(levelNdx), sampler.depthStencilMode);
return view;
}
tcu::TextureCubeView getEffectiveTView (const tcu::TextureCubeView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
storage.resize(tcu::CUBEFACE_LAST * src.getNumLevels());
const tcu::ConstPixelBufferAccess* storagePtrs[tcu::CUBEFACE_LAST] =
{
&storage[0 * src.getNumLevels()],
&storage[1 * src.getNumLevels()],
&storage[2 * src.getNumLevels()],
&storage[3 * src.getNumLevels()],
&storage[4 * src.getNumLevels()],
&storage[5 * src.getNumLevels()],
};
tcu::TextureCubeView view = tcu::TextureCubeView(src.getNumLevels(), storagePtrs);
for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; ++faceNdx)
for (int levelNdx = 0; levelNdx < src.getNumLevels(); ++levelNdx)
storage[faceNdx * src.getNumLevels() + levelNdx] = tcu::getEffectiveDepthStencilAccess(src.getLevelFace(levelNdx, (tcu::CubeFace)faceNdx), sampler.depthStencilMode);
return view;
}
tcu::Texture1DView getEffectiveTextureView (const tcu::Texture1DView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
return getEffectiveTView(src, storage, sampler);
}
tcu::Texture2DView getEffectiveTextureView (const tcu::Texture2DView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
return getEffectiveTView(src, storage, sampler);
}
tcu::Texture3DView getEffectiveTextureView (const tcu::Texture3DView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
return getEffectiveTView(src, storage, sampler);
}
tcu::Texture1DArrayView getEffectiveTextureView (const tcu::Texture1DArrayView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
return getEffectiveTView(src, storage, sampler);
}
tcu::Texture2DArrayView getEffectiveTextureView (const tcu::Texture2DArrayView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
return getEffectiveTView(src, storage, sampler);
}
tcu::TextureCubeView getEffectiveTextureView (const tcu::TextureCubeView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
return getEffectiveTView(src, storage, sampler);
}
tcu::TextureCubeArrayView getEffectiveTextureView (const tcu::TextureCubeArrayView& src, std::vector<tcu::ConstPixelBufferAccess>& storage, const tcu::Sampler& sampler)
{
return getEffectiveTView(src, storage, sampler);
}
//! Returns the effective swizzle of a border color. The effective swizzle is the
//! equal to first writing an RGBA color with a write swizzle and then reading
//! it back using a read swizzle, i.e. BorderSwizzle(c) == readSwizzle(writeSwizzle(C))
static const TextureSwizzle& getBorderColorReadSwizzle (TextureFormat::ChannelOrder order)
{
// make sure to update these tables when channel orders are updated
DE_STATIC_ASSERT(TextureFormat::CHANNELORDER_LAST == 21);
static const TextureSwizzle INV = {{ TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ONE }};
static const TextureSwizzle R = {{ TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ONE }};
static const TextureSwizzle A = {{ TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_3 }};
static const TextureSwizzle I = {{ TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_0 }};
static const TextureSwizzle L = {{ TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_ONE }};
static const TextureSwizzle LA = {{ TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3 }};
static const TextureSwizzle RG = {{ TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ONE }};
static const TextureSwizzle RA = {{ TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_3 }};
static const TextureSwizzle RGB = {{ TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1, TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_ONE }};
static const TextureSwizzle RGBA = {{ TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1, TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3 }};
static const TextureSwizzle D = {{ TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ONE }};
static const TextureSwizzle S = {{ TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ZERO, TextureSwizzle::CHANNEL_ONE }};
const TextureSwizzle* swizzle;
switch (order)
{
case TextureFormat::R: swizzle = &R; break;
case TextureFormat::A: swizzle = &A; break;
case TextureFormat::I: swizzle = &I; break;
case TextureFormat::L: swizzle = &L; break;
case TextureFormat::LA: swizzle = &LA; break;
case TextureFormat::RG: swizzle = &RG; break;
case TextureFormat::RA: swizzle = &RA; break;
case TextureFormat::RGB: swizzle = &RGB; break;
case TextureFormat::RGBA: swizzle = &RGBA; break;
case TextureFormat::ARGB: swizzle = &RGBA; break;
case TextureFormat::BGR: swizzle = &RGB; break;
case TextureFormat::BGRA: swizzle = &RGBA; break;
case TextureFormat::sR: swizzle = &R; break;
case TextureFormat::sRG: swizzle = &RG; break;
case TextureFormat::sRGB: swizzle = &RGB; break;
case TextureFormat::sRGBA: swizzle = &RGBA; break;
case TextureFormat::sBGR: swizzle = &RGB; break;
case TextureFormat::sBGRA: swizzle = &RGBA; break;
case TextureFormat::D: swizzle = &D; break;
case TextureFormat::S: swizzle = &S; break;
case TextureFormat::DS:
DE_ASSERT(false); // combined depth-stencil border color?
swizzle = &INV;
break;
default:
DE_ASSERT(false);
swizzle = &INV;
break;
}
#ifdef DE_DEBUG
{
// check that BorderSwizzle(c) == readSwizzle(writeSwizzle(C))
const TextureSwizzle& readSwizzle = getChannelReadSwizzle(order);
const TextureSwizzle& writeSwizzle = getChannelWriteSwizzle(order);
for (int ndx = 0; ndx < 4; ++ndx)
{
TextureSwizzle::Channel writeRead = readSwizzle.components[ndx];
if (deInRange32(writeRead, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_3) == DE_TRUE)
writeRead = writeSwizzle.components[(int)writeRead];
DE_ASSERT(writeRead == swizzle->components[ndx]);
}
}
#endif
return *swizzle;
}
static tcu::UVec4 getNBitUnsignedIntegerVec4MaxValue (const tcu::IVec4& numBits)
{
return tcu::UVec4((numBits[0] > 0) ? (deUintMaxValue32(numBits[0])) : (0),
(numBits[1] > 0) ? (deUintMaxValue32(numBits[1])) : (0),
(numBits[2] > 0) ? (deUintMaxValue32(numBits[2])) : (0),
(numBits[3] > 0) ? (deUintMaxValue32(numBits[3])) : (0));
}
static tcu::IVec4 getNBitSignedIntegerVec4MaxValue (const tcu::IVec4& numBits)
{
return tcu::IVec4((numBits[0] > 0) ? (deIntMaxValue32(numBits[0])) : (0),
(numBits[1] > 0) ? (deIntMaxValue32(numBits[1])) : (0),
(numBits[2] > 0) ? (deIntMaxValue32(numBits[2])) : (0),
(numBits[3] > 0) ? (deIntMaxValue32(numBits[3])) : (0));
}
static tcu::IVec4 getNBitSignedIntegerVec4MinValue (const tcu::IVec4& numBits)
{
return tcu::IVec4((numBits[0] > 0) ? (deIntMinValue32(numBits[0])) : (0),
(numBits[1] > 0) ? (deIntMinValue32(numBits[1])) : (0),
(numBits[2] > 0) ? (deIntMinValue32(numBits[2])) : (0),
(numBits[3] > 0) ? (deIntMinValue32(numBits[3])) : (0));
}
static tcu::Vec4 getTextureBorderColorFloat (const TextureFormat& format, const Sampler& sampler)
{
const tcu::TextureChannelClass channelClass = getTextureChannelClass(format.type);
const TextureSwizzle::Channel* channelMap = getBorderColorReadSwizzle(format.order).components;
const bool isFloat = channelClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT;
const bool isSigned = channelClass != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT;
const float valueMin = (isSigned) ? (-1.0f) : (0.0f);
const float valueMax = 1.0f;
Vec4 result;
DE_ASSERT(channelClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT ||
channelClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT ||
channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT);
for (int c = 0; c < 4; c++)
{
const TextureSwizzle::Channel map = channelMap[c];
if (map == TextureSwizzle::CHANNEL_ZERO)
result[c] = 0.0f;
else if (map == TextureSwizzle::CHANNEL_ONE)
result[c] = 1.0f;
else if (isFloat)
{
// floating point values are not clamped
result[c] = sampler.borderColor.getAccess<float>()[(int)map];
}
else
{
// fixed point values are clamped to a representable range
result[c] = de::clamp(sampler.borderColor.getAccess<float>()[(int)map], valueMin, valueMax);
}
}
return result;
}
static tcu::IVec4 getTextureBorderColorInt (const TextureFormat& format, const Sampler& sampler)
{
const tcu::TextureChannelClass channelClass = getTextureChannelClass(format.type);
const TextureSwizzle::Channel* channelMap = getBorderColorReadSwizzle(format.order).components;
const IVec4 channelBits = getChannelBitDepth(format.type);
const IVec4 valueMin = getNBitSignedIntegerVec4MinValue(channelBits);
const IVec4 valueMax = getNBitSignedIntegerVec4MaxValue(channelBits);
IVec4 result;
DE_ASSERT(channelClass == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER);
DE_UNREF(channelClass);
for (int c = 0; c < 4; c++)
{
const TextureSwizzle::Channel map = channelMap[c];
if (map == TextureSwizzle::CHANNEL_ZERO)
result[c] = 0;
else if (map == TextureSwizzle::CHANNEL_ONE)
result[c] = 1;
else
{
// integer values are clamped to a representable range
result[c] = de::clamp(sampler.borderColor.getAccess<deInt32>()[(int)map], valueMin[(int)map], valueMax[(int)map]);
}
}
return result;
}
static tcu::UVec4 getTextureBorderColorUint (const TextureFormat& format, const Sampler& sampler)
{
const tcu::TextureChannelClass channelClass = getTextureChannelClass(format.type);
const TextureSwizzle::Channel* channelMap = getBorderColorReadSwizzle(format.order).components;
const IVec4 channelBits = getChannelBitDepth(format.type);
const UVec4 valueMax = getNBitUnsignedIntegerVec4MaxValue(channelBits);
UVec4 result;
DE_ASSERT(channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER);
DE_UNREF(channelClass);
for (int c = 0; c < 4; c++)
{
const TextureSwizzle::Channel map = channelMap[c];
if (map == TextureSwizzle::CHANNEL_ZERO)
result[c] = 0;
else if (map == TextureSwizzle::CHANNEL_ONE)
result[c] = 1;
else
{
// integer values are clamped to a representable range
result[c] = de::min(sampler.borderColor.getAccess<deUint32>()[(int)map], valueMax[(int)map]);
}
}
return result;
}
template <typename ScalarType>
tcu::Vector<ScalarType, 4> sampleTextureBorder (const TextureFormat& format, const Sampler& sampler)
{
const tcu::TextureChannelClass channelClass = getTextureChannelClass(format.type);
switch (channelClass)
{
case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
return getTextureBorderColorFloat(format, sampler).cast<ScalarType>();
case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
return getTextureBorderColorInt(format, sampler).cast<ScalarType>();
case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
return getTextureBorderColorUint(format, sampler).cast<ScalarType>();
default:
DE_ASSERT(false);
return tcu::Vector<ScalarType, 4>();
}
}
// instantiation
template tcu::Vector<float, 4> sampleTextureBorder (const TextureFormat& format, const Sampler& sampler);
template tcu::Vector<deInt32, 4> sampleTextureBorder (const TextureFormat& format, const Sampler& sampler);
template tcu::Vector<deUint32, 4> sampleTextureBorder (const TextureFormat& format, const Sampler& sampler);
} // tcu