mirror of
https://github.com/godotengine/godot-angle-static.git
synced 2026-01-03 14:09:33 +03:00
D3D11: Transcode ETC1 to BC1.
Adds a new extension to allow transcode ETC formats to suitable BC formats. This commit implements ETC1 to BC1. More formats will be supported soon. BUG=angleproject:1285 Change-Id: Iacbfbc2248dfe1aebf24b92696249a9404e331cd Reviewed-on: https://chromium-review.googlesource.com/316511 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
committed by
Corentin Wallez
parent
7718c05b79
commit
e3939b985c
159
extensions/ANGLE_lossy_etc_decode.txt
Normal file
159
extensions/ANGLE_lossy_etc_decode.txt
Normal file
@@ -0,0 +1,159 @@
|
||||
Name
|
||||
|
||||
ANGLE_lossy_etc_decode
|
||||
|
||||
Name Strings
|
||||
|
||||
GL_ANGLE_lossy_etc_decode
|
||||
|
||||
Contributors
|
||||
|
||||
Minmin Gong (mgong 'at' microsoft.com)
|
||||
|
||||
Contacts
|
||||
|
||||
Minmin Gong (mgong 'at' microsoft.com)
|
||||
|
||||
Status
|
||||
|
||||
Draft
|
||||
|
||||
Version
|
||||
|
||||
Last Modified Date: Nov 25, 2015
|
||||
Author Revision: 1
|
||||
|
||||
Number
|
||||
|
||||
TBD
|
||||
|
||||
Dependencies
|
||||
|
||||
Requires OpenGL ES 3.0 for ETC2 and EAC formats, or OpenGL ES 2.0 and
|
||||
OES_compressed_ETC1_RGB8_texture for ETC1 format.
|
||||
The extension is written against the OpenGL ES 2.0 specification.
|
||||
|
||||
Overview
|
||||
|
||||
Both the OpenGL ES 3.0 specification and OES_compressed_ETC1_RGB8_texture
|
||||
specify that Ericsson Texture Compression (ETC) decoding must not be lossy.
|
||||
The goal of this extension is to allow a lossy decode of
|
||||
compressed textures in the ETC formats in OpenGL ES, for lower memory
|
||||
and bandwidth consumption.
|
||||
|
||||
This extension uses the same ETC compression format as OpenGL ES 3.0
|
||||
and OES_compressed_ETC1_RGB8_texture, with the restriction that the texture
|
||||
dimensions must be a multiple of four (except for mip levels where the
|
||||
dimensions are either 2 or 1). And the requirement that ETC decoding must
|
||||
not be lossy is relaxed.
|
||||
|
||||
See OES_compressed_ETC1_RGB8_texture for a description of the ETC1 format.
|
||||
Also see OpenGL ES 3.0 specification appendix C.2 (ETC Compressed Texture
|
||||
ImageFormats) for a description of ETC2 and EAC formats.
|
||||
|
||||
IP Status
|
||||
|
||||
See Ericsson's "IP Statement"
|
||||
|
||||
New Procedures and Functions
|
||||
|
||||
None.
|
||||
|
||||
New Types
|
||||
|
||||
None.
|
||||
|
||||
New Tokens
|
||||
|
||||
Accepted by the <internalformat> parameter of CompressedTexImage2D
|
||||
and the <format> parameter of CompressedTexSubImage2D:
|
||||
|
||||
ETC1_RGB8_LOSSY_DECODE_ANGLE 0x9690
|
||||
COMPRESSED_R11_LOSSY_DECODE_EAC_ANGLE 0x9691
|
||||
COMPRESSED_SIGNED_R11_LOSSY_DECODE_EAC_ANGLE 0x9692
|
||||
COMPRESSED_RG11_LOSSY_DECODE_EAC_ANGLE 0x9693
|
||||
COMPRESSED_SIGNED_RG11_LOSSY_DECODE_EAC_ANGLE 0x9694
|
||||
COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE 0x9695
|
||||
COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE 0x9696
|
||||
COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE 0x9697
|
||||
COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE 0x9698
|
||||
COMPRESSED_RGBA8_LOSSY_DECODE_ETC2_EAC_ANGLE 0x9699
|
||||
COMPRESSED_SRGB8_ALPHA8_LOSSY_DECODE_ETC2_EAC_ANGLE 0x969A
|
||||
|
||||
Additions to Chapter 3 of the OpenGL ES 2.0 Specification (Rasterization)
|
||||
|
||||
Add the following to Section 3.7.3 (Compressed Texture Images)
|
||||
(at the end of the description of the CompressedTexImage2D command):
|
||||
|
||||
Compressed Internal Format Base Internal Format
|
||||
========================== ====================
|
||||
ETC1_RGB8_LOSSY_DECODE_ANGLE RGB
|
||||
COMPRESSED_R11_LOSSY_DECODE_EAC_ANGLE R
|
||||
COMPRESSED_SIGNED_R11_LOSSY_DECODE_EAC_ANGLE R
|
||||
COMPRESSED_RG11_LOSSY_DECODE_EAC_ANGLE RG
|
||||
COMPRESSED_SIGNED_RG11_LOSSY_DECODE_EAC_ANGLE RG
|
||||
COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE RGB
|
||||
COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE RGB
|
||||
COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE RGBA
|
||||
COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE RGBA
|
||||
COMPRESSED_RGBA8_LOSSY_DECODE_ETC2_EAC_ANGLE RGBA
|
||||
COMPRESSED_SRGB8_ALPHA8_LOSSY_DECODE_ETC2_EAC_ANGLE RGBA
|
||||
|
||||
Table 3.x: Specific Compressed Internal Formats
|
||||
|
||||
If <internalformat> is one of the ETC lossy decode formats listed in
|
||||
Table 3.x, the compressed texture is stored in an unspecified compressed
|
||||
texture format, that may introduce losses of precision in the texture data.
|
||||
The GL and the ETC texture compression algorithm support only 2D images
|
||||
without borders.
|
||||
|
||||
CompressedTexImage2D will produce the INVALID_OPERATION error when
|
||||
<internalformat> is one of the lossy decode ETC-format values from
|
||||
Table 3.x under the following conditions:
|
||||
|
||||
* <border> is non-zero.
|
||||
* <width> is not one, two, nor a multiple of four.
|
||||
* <height> is not one, two, nor a multiple of four.
|
||||
|
||||
Add the following to Section 3.7.3 (Compressed Texture Images)
|
||||
(at the end of the description of the CompressedTexSubImage2D command):
|
||||
|
||||
If the internal format of the texture image being modified is an ETC-format
|
||||
listed in Table 3.x, the compressed texture is stored in an unspecified
|
||||
compressed texture format. The xoffset and yoffset must also be aligned to
|
||||
4x4 texel block boundaries, since ETC encoding makes it difficult to modify
|
||||
non-aligned regions. CompressedTexSubImage2D will result in an
|
||||
INVALID_OPERATION error only if one of the following conditions occurs:
|
||||
|
||||
* <width> is not a multiple of four nor equal to TEXTURE_WIDTH.
|
||||
* <height> is not a multiple of four nor equal to TEXTURE_HEIGHT.
|
||||
* <xoffset> or <yoffset> is not a multiple of four.
|
||||
* <format> does not match the internal format of the texture image
|
||||
being modified.
|
||||
|
||||
Errors
|
||||
|
||||
INVALID_OPERATION is generated by CompressedTexImage2D if
|
||||
lossy decode ETC-format is used and <internalformat> is one of the
|
||||
compressed internal formats from Table 3.x and any of the following apply:
|
||||
- <border> is not equal to zero.
|
||||
- <width> is not one, two, nor a multiple of four.
|
||||
- <height> is not one, two, nor a multiple of four.
|
||||
|
||||
INVALID_OPERATION is generated by CompressedTexSubImage2D if
|
||||
lossy decode ETC-format is used and <format> is one of the compressed
|
||||
interal formats from Table 3.x and any of the following apply:
|
||||
- <width> is not a multiple of four nor equal to TEXTURE_WIDTH;
|
||||
- <height> is not a multiple of four nor equal to TEXTURE_HEIGHT;
|
||||
- <xoffset> or <yoffset> is not a multiple of four;
|
||||
- <format> does not match the internal format of the texture image
|
||||
being modified.
|
||||
|
||||
New State
|
||||
|
||||
None.
|
||||
|
||||
Revision History
|
||||
|
||||
Revision 1, 2015/11/25 - mgong
|
||||
- Initial revision
|
||||
@@ -2920,6 +2920,21 @@ GL_APICALL void GL_APIENTRY glEndTilingQCOM (GLbitfield preserveMask);
|
||||
#define GL_SHADER_BINARY_VIV 0x8FC4
|
||||
#endif /* GL_VIV_shader_binary */
|
||||
|
||||
#ifndef GL_ANGLE_lossy_etc_decode
|
||||
#define GL_ANGLE_lossy_etc_decode 1
|
||||
#define GL_ETC1_RGB8_LOSSY_DECODE_ANGLE 0x9690
|
||||
#define GL_COMPRESSED_R11_LOSSY_DECODE_EAC_ANGLE 0x9691
|
||||
#define GL_COMPRESSED_SIGNED_R11_LOSSY_DECODE_EAC_ANGLE 0x9692
|
||||
#define GL_COMPRESSED_RG11_LOSSY_DECODE_EAC_ANGLE 0x9693
|
||||
#define GL_COMPRESSED_SIGNED_RG11_LOSSY_DECODE_EAC_ANGLE 0x9694
|
||||
#define GL_COMPRESSED_RGB8_LOSSY_DECODE_ETC2_ANGLE 0x9695
|
||||
#define GL_COMPRESSED_SRGB8_LOSSY_DECODE_ETC2_ANGLE 0x9696
|
||||
#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE 0x9697
|
||||
#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_LOSSY_DECODE_ETC2_ANGLE 0x9698
|
||||
#define GL_COMPRESSED_RGBA8_LOSSY_DECODE_ETC2_EAC_ANGLE 0x9699
|
||||
#define GL_COMPRESSED_SRGB8_ALPHA8_LOSSY_DECODE_ETC2_EAC_ANGLE 0x969A
|
||||
#endif /* GL_ANGLE_lossy_etc_decode */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -156,6 +156,7 @@ Extensions::Extensions()
|
||||
maxDebugGroupStackDepth(0),
|
||||
maxLabelLength(0),
|
||||
noError(false),
|
||||
lossyETCDecode(false),
|
||||
colorBufferFloat(false)
|
||||
{
|
||||
}
|
||||
@@ -225,6 +226,8 @@ std::vector<std::string> Extensions::getStrings() const
|
||||
InsertExtensionString("GL_KHR_debug", debug, &extensionStrings);
|
||||
// TODO(jmadill): Enable this when complete.
|
||||
//InsertExtensionString("GL_KHR_no_error", noError, &extensionStrings);
|
||||
|
||||
InsertExtensionString("GL_ANGLE_lossy_etc_decode", lossyETCDecode, &extensionStrings);
|
||||
// clang-format on
|
||||
|
||||
return extensionStrings;
|
||||
|
||||
@@ -275,6 +275,9 @@ struct Extensions
|
||||
// KHR_no_error
|
||||
bool noError;
|
||||
|
||||
// GL_ANGLE_lossy_etc_decode
|
||||
bool lossyETCDecode;
|
||||
|
||||
// ES3 Extension support
|
||||
|
||||
// GL_EXT_color_buffer_float
|
||||
|
||||
@@ -565,6 +565,10 @@ static InternalFormatInfoMap BuildInternalFormatInfoMap()
|
||||
// - It affects only validation of internalformat in RenderbufferStorageMultisample.
|
||||
// | Internal format | |D |S |X | Format | Type | Component type | Supported | Renderable | Filterable |
|
||||
map.insert(InternalFormatInfoPair(GL_STENCIL_INDEX8, DepthStencilFormat(0, 8, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_BYTE, GL_UNSIGNED_NORMALIZED, RequireES<2>, RequireES<2>, NeverSupported)));
|
||||
|
||||
// From GL_ANGLE_lossy_etc_decode
|
||||
map.insert(InternalFormatInfoPair(GL_ETC1_RGB8_LOSSY_DECODE_ANGLE, CompressedFormat(4, 4, 64, 3, GL_ETC1_RGB8_OES, GL_UNSIGNED_BYTE, false, RequireExt<&Extensions::lossyETCDecode>, NeverSupported, AlwaysSupported)));
|
||||
|
||||
// clang-format on
|
||||
|
||||
return map;
|
||||
|
||||
@@ -1103,5 +1103,14 @@
|
||||
"requiresConversion": "true"
|
||||
}
|
||||
]
|
||||
},
|
||||
"GL_ETC1_RGB8_LOSSY_DECODE_ANGLE": {
|
||||
"GL_UNSIGNED_BYTE": [
|
||||
{
|
||||
"loadFunction": "LoadETC1RGB8ToBC1",
|
||||
"dxgiFormat": "DXGI_FORMAT_BC1_UNORM",
|
||||
"requiresConversion": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -771,6 +771,24 @@ const std::map<GLenum, LoadImageFunction> &GetLoadFunctionsMap(GLenum internalFo
|
||||
}
|
||||
}
|
||||
}
|
||||
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
|
||||
{
|
||||
switch (dxgiFormat)
|
||||
{
|
||||
case DXGI_FORMAT_BC1_UNORM:
|
||||
{
|
||||
static const std::map<GLenum, LoadImageFunction> loadFunctionsMap = []() {
|
||||
std::map<GLenum, LoadImageFunction> loadMap;
|
||||
loadMap[GL_UNSIGNED_BYTE] = LoadETC1RGB8ToBC1;
|
||||
return loadMap;
|
||||
}();
|
||||
|
||||
return loadFunctionsMap;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
case GL_ETC1_RGB8_OES:
|
||||
{
|
||||
switch (dxgiFormat)
|
||||
|
||||
@@ -1218,6 +1218,7 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons
|
||||
extensions->packSubimage = true;
|
||||
extensions->vertexArrayObject = true;
|
||||
extensions->noError = true;
|
||||
extensions->lossyETCDecode = true;
|
||||
|
||||
// D3D11 Feature Level 10_0+ uses SV_IsFrontFace in HLSL to emulate gl_FrontFacing.
|
||||
// D3D11 Feature Level 9_3 doesn't support SV_IsFrontFace, and has no equivalent, so can't support gl_FrontFacing.
|
||||
|
||||
@@ -238,6 +238,12 @@
|
||||
"srvFormat": "DXGI_FORMAT_R8G8B8A8_UNORM"
|
||||
}
|
||||
],
|
||||
"GL_ETC1_RGB8_LOSSY_DECODE_ANGLE": [
|
||||
{
|
||||
"texFormat": "DXGI_FORMAT_BC1_UNORM",
|
||||
"srvFormat": "DXGI_FORMAT_BC1_UNORM"
|
||||
}
|
||||
],
|
||||
"GL_LUMINANCE": [
|
||||
{
|
||||
"texFormat": "DXGI_FORMAT_R8G8B8A8_UNORM",
|
||||
|
||||
@@ -732,6 +732,22 @@ const TextureFormat &GetTextureFormatInfo(GLenum internalFormat,
|
||||
break;
|
||||
}
|
||||
}
|
||||
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
|
||||
{
|
||||
if (AnyDevice(renderer11DeviceCaps))
|
||||
{
|
||||
static const TextureFormat textureFormat = GetD3D11FormatInfo(internalFormat,
|
||||
DXGI_FORMAT_BC1_UNORM,
|
||||
DXGI_FORMAT_BC1_UNORM,
|
||||
DXGI_FORMAT_UNKNOWN,
|
||||
DXGI_FORMAT_UNKNOWN);
|
||||
return textureFormat;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
case GL_ETC1_RGB8_OES:
|
||||
{
|
||||
if (AnyDevice(renderer11DeviceCaps))
|
||||
|
||||
@@ -15,6 +15,43 @@ namespace rx
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Table 3.17.2 sorted according to table 3.17.3
|
||||
// clang-format off
|
||||
static const int intensityModifierDefault[][4] =
|
||||
{
|
||||
{ 2, 8, -2, -8 },
|
||||
{ 5, 17, -5, -17 },
|
||||
{ 9, 29, -9, -29 },
|
||||
{ 13, 42, -13, -42 },
|
||||
{ 18, 60, -18, -60 },
|
||||
{ 24, 80, -24, -80 },
|
||||
{ 33, 106, -33, -106 },
|
||||
{ 47, 183, -47, -183 },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// Table C.12, intensity modifier for non opaque punchthrough alpha
|
||||
// clang-format off
|
||||
static const int intensityModifierNonOpaque[][4] =
|
||||
{
|
||||
{ 0, 8, 0, -8 },
|
||||
{ 0, 17, 0, -17 },
|
||||
{ 0, 29, 0, -29 },
|
||||
{ 0, 42, 0, -42 },
|
||||
{ 0, 60, 0, -60 },
|
||||
{ 0, 80, 0, -80 },
|
||||
{ 0, 106, 0, -106 },
|
||||
{ 0, 183, 0, -183 },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// Table C.7, mapping from pixel index values to modifier value orders
|
||||
// clang-format off
|
||||
static const int valueMappingTable[] =
|
||||
{
|
||||
2, 3, 1, 0
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
struct ETC2Block
|
||||
{
|
||||
@@ -92,6 +129,49 @@ struct ETC2Block
|
||||
}
|
||||
}
|
||||
|
||||
// Transcodes RGB block to BC1
|
||||
void transcodeAsBC1(uint8_t *dest,
|
||||
size_t x,
|
||||
size_t y,
|
||||
size_t w,
|
||||
size_t h,
|
||||
const uint8_t alphaValues[4][4],
|
||||
bool punchThroughAlpha) const
|
||||
{
|
||||
bool opaqueBit = u.idht.mode.idm.diffbit;
|
||||
bool nonOpaquePunchThroughAlpha = punchThroughAlpha && !opaqueBit;
|
||||
// Select mode
|
||||
if (u.idht.mode.idm.diffbit || punchThroughAlpha)
|
||||
{
|
||||
const auto &block = u.idht.mode.idm.colors.diff;
|
||||
int r = (block.R + block.dR);
|
||||
int g = (block.G + block.dG);
|
||||
int b = (block.B + block.dB);
|
||||
if (r < 0 || r > 31)
|
||||
{
|
||||
transcodeTBlockToBC1(dest, x, y, w, h, alphaValues, nonOpaquePunchThroughAlpha);
|
||||
}
|
||||
else if (g < 0 || g > 31)
|
||||
{
|
||||
transcodeHBlockToBC1(dest, x, y, w, h, alphaValues, nonOpaquePunchThroughAlpha);
|
||||
}
|
||||
else if (b < 0 || b > 31)
|
||||
{
|
||||
transcodePlanarBlockToBC1(dest, x, y, w, h, alphaValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
transcodeDifferentialBlockToBC1(dest, x, y, w, h, alphaValues,
|
||||
nonOpaquePunchThroughAlpha);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
transcodeIndividualBlockToBC1(dest, x, y, w, h, alphaValues,
|
||||
nonOpaquePunchThroughAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
union
|
||||
{
|
||||
@@ -329,48 +409,18 @@ struct ETC2Block
|
||||
const uint8_t alphaValues[4][4],
|
||||
bool nonOpaquePunchThroughAlpha) const
|
||||
{
|
||||
// Table 3.17.2 sorted according to table 3.17.3
|
||||
// clang-format off
|
||||
static const int intensityModifierDefault[8][4] =
|
||||
{
|
||||
{ 2, 8, -2, -8 },
|
||||
{ 5, 17, -5, -17 },
|
||||
{ 9, 29, -9, -29 },
|
||||
{ 13, 42, -13, -42 },
|
||||
{ 18, 60, -18, -60 },
|
||||
{ 24, 80, -24, -80 },
|
||||
{ 33, 106, -33, -106 },
|
||||
{ 47, 183, -47, -183 },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// Table C.12, intensity modifier for non opaque punchthrough alpha
|
||||
// clang-format off
|
||||
static const int intensityModifierNonOpaque[8][4] =
|
||||
{
|
||||
{ 0, 8, 0, -8 },
|
||||
{ 0, 17, 0, -17 },
|
||||
{ 0, 29, 0, -29 },
|
||||
{ 0, 42, 0, -42 },
|
||||
{ 0, 60, 0, -60 },
|
||||
{ 0, 80, 0, -80 },
|
||||
{ 0, 106, 0, -106 },
|
||||
{ 0, 183, 0, -183 },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
const int(&intensityModifier)[8][4] =
|
||||
const auto intensityModifier =
|
||||
nonOpaquePunchThroughAlpha ? intensityModifierNonOpaque : intensityModifierDefault;
|
||||
|
||||
R8G8B8A8 subblockColors0[4];
|
||||
R8G8B8A8 subblockColors1[4];
|
||||
for (size_t blockIdx = 0; blockIdx < 4; blockIdx++)
|
||||
for (size_t modifierIdx = 0; modifierIdx < 4; modifierIdx++)
|
||||
{
|
||||
const int i1 = intensityModifier[u.idht.mode.idm.cw1][blockIdx];
|
||||
subblockColors0[blockIdx] = createRGBA(r1 + i1, g1 + i1, b1 + i1);
|
||||
const int i1 = intensityModifier[u.idht.mode.idm.cw1][modifierIdx];
|
||||
subblockColors0[modifierIdx] = createRGBA(r1 + i1, g1 + i1, b1 + i1);
|
||||
|
||||
const int i2 = intensityModifier[u.idht.mode.idm.cw2][blockIdx];
|
||||
subblockColors1[blockIdx] = createRGBA(r2 + i2, g2 + i2, b2 + i2);
|
||||
const int i2 = intensityModifier[u.idht.mode.idm.cw2][modifierIdx];
|
||||
subblockColors1[modifierIdx] = createRGBA(r2 + i2, g2 + i2, b2 + i2);
|
||||
}
|
||||
|
||||
if (u.idht.mode.idm.flipbit)
|
||||
@@ -583,6 +633,392 @@ struct ETC2Block
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t RGB8ToRGB565(const R8G8B8A8 &rgba) const
|
||||
{
|
||||
return (static_cast<uint16_t>(rgba.R >> 3) << 11) |
|
||||
(static_cast<uint16_t>(rgba.G >> 2) << 5) |
|
||||
(static_cast<uint16_t>(rgba.B >> 3) << 0);
|
||||
}
|
||||
|
||||
uint32_t matchBC1Bits(const R8G8B8A8 *rgba,
|
||||
const R8G8B8A8 &minColor,
|
||||
const R8G8B8A8 &maxColor,
|
||||
bool opaque) const
|
||||
{
|
||||
// Project each pixel on the (maxColor, minColor) line to decide which
|
||||
// BC1 code to assign to it.
|
||||
|
||||
uint8_t decodedColors[2][3] = {{maxColor.R, maxColor.G, maxColor.B},
|
||||
{minColor.R, minColor.G, minColor.B}};
|
||||
|
||||
int direction[3];
|
||||
for (int ch = 0; ch < 3; ch++)
|
||||
{
|
||||
direction[ch] = decodedColors[0][ch] - decodedColors[1][ch];
|
||||
}
|
||||
|
||||
int stops[2];
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
stops[i] = decodedColors[i][0] * direction[0] + decodedColors[i][1] * direction[1] +
|
||||
decodedColors[i][2] * direction[2];
|
||||
}
|
||||
|
||||
uint32_t bits = 0;
|
||||
if (opaque)
|
||||
{
|
||||
for (int i = 15; i >= 0; i--)
|
||||
{
|
||||
// In opaque mode, the code is from 0 to 3.
|
||||
|
||||
bits <<= 2;
|
||||
const int dot =
|
||||
rgba[i].R * direction[0] + rgba[i].G * direction[1] + rgba[i].B * direction[2];
|
||||
const int factor = gl::clamp(
|
||||
static_cast<int>(
|
||||
(static_cast<float>(dot - stops[1]) / (stops[0] - stops[1])) * 3 + 0.5f),
|
||||
0, 3);
|
||||
switch (factor)
|
||||
{
|
||||
case 0:
|
||||
bits |= 1;
|
||||
break;
|
||||
case 1:
|
||||
bits |= 3;
|
||||
break;
|
||||
case 2:
|
||||
bits |= 2;
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
bits |= 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 15; i >= 0; i--)
|
||||
{
|
||||
// In non-opaque mode, 3 is for tranparent pixels.
|
||||
|
||||
bits <<= 2;
|
||||
if (0 == rgba[i].A)
|
||||
{
|
||||
bits |= 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int dot = rgba[i].R * direction[0] + rgba[i].G * direction[1] +
|
||||
rgba[i].B * direction[2];
|
||||
const int factor = gl::clamp(
|
||||
static_cast<int>(
|
||||
(static_cast<float>(dot - stops[1]) / (stops[0] - stops[1])) * 2 +
|
||||
0.5f),
|
||||
0, 2);
|
||||
switch (factor)
|
||||
{
|
||||
case 0:
|
||||
bits |= 0;
|
||||
break;
|
||||
case 1:
|
||||
bits |= 2;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
bits |= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
void packBC1(void *bc1,
|
||||
const R8G8B8A8 *rgba,
|
||||
R8G8B8A8 &minColor,
|
||||
R8G8B8A8 &maxColor,
|
||||
bool opaque) const
|
||||
{
|
||||
uint32_t bits;
|
||||
uint16_t max16 = RGB8ToRGB565(maxColor);
|
||||
uint16_t min16 = RGB8ToRGB565(minColor);
|
||||
if (max16 != min16)
|
||||
{
|
||||
// Find the best BC1 code for each pixel
|
||||
bits = matchBC1Bits(rgba, minColor, maxColor, opaque);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Same colors, BC1 index 0 is the color in both opaque and transparent mode
|
||||
bits = 0;
|
||||
// BC1 index 3 is transparent
|
||||
if (!opaque)
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (0 == rgba[i].A)
|
||||
{
|
||||
bits |= (3 << (i * 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (max16 < min16)
|
||||
{
|
||||
std::swap(max16, min16);
|
||||
|
||||
uint32_t xorMask = 0;
|
||||
if (opaque)
|
||||
{
|
||||
// In opaque mode switching the two colors is doing the
|
||||
// following code swaps: 0 <-> 1 and 2 <-> 3. This is
|
||||
// equivalent to flipping the first bit of each code
|
||||
// (5 = 0b0101)
|
||||
xorMask = 0x55555555;
|
||||
}
|
||||
else
|
||||
{
|
||||
// In transparent mode switching the colors is doing the
|
||||
// following code swap: 0 <-> 1. 0xA selects the second bit of
|
||||
// each code, bits >> 1 selects the first bit of the code when
|
||||
// the seconds bit is set (case 2 and 3). We invert all the
|
||||
// non-selected bits, that is the first bit when the code is
|
||||
// 0 or 1.
|
||||
xorMask = ~((bits >> 1) | 0xAAAAAAAA);
|
||||
}
|
||||
bits ^= xorMask;
|
||||
}
|
||||
|
||||
struct BC1Block
|
||||
{
|
||||
uint16_t color0;
|
||||
uint16_t color1;
|
||||
uint32_t bits;
|
||||
};
|
||||
|
||||
// Encode the opaqueness in the order of the two BC1 colors
|
||||
BC1Block *dest = reinterpret_cast<BC1Block *>(bc1);
|
||||
if (opaque)
|
||||
{
|
||||
dest->color0 = max16;
|
||||
dest->color1 = min16;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest->color0 = min16;
|
||||
dest->color1 = max16;
|
||||
}
|
||||
dest->bits = bits;
|
||||
}
|
||||
|
||||
void transcodeIndividualBlockToBC1(uint8_t *dest,
|
||||
size_t x,
|
||||
size_t y,
|
||||
size_t w,
|
||||
size_t h,
|
||||
const uint8_t alphaValues[4][4],
|
||||
bool nonOpaquePunchThroughAlpha) const
|
||||
{
|
||||
const auto &block = u.idht.mode.idm.colors.indiv;
|
||||
int r1 = extend_4to8bits(block.R1);
|
||||
int g1 = extend_4to8bits(block.G1);
|
||||
int b1 = extend_4to8bits(block.B1);
|
||||
int r2 = extend_4to8bits(block.R2);
|
||||
int g2 = extend_4to8bits(block.G2);
|
||||
int b2 = extend_4to8bits(block.B2);
|
||||
transcodeIndividualOrDifferentialBlockToBC1(dest, x, y, w, h, r1, g1, b1, r2, g2, b2,
|
||||
alphaValues, nonOpaquePunchThroughAlpha);
|
||||
}
|
||||
|
||||
void transcodeDifferentialBlockToBC1(uint8_t *dest,
|
||||
size_t x,
|
||||
size_t y,
|
||||
size_t w,
|
||||
size_t h,
|
||||
const uint8_t alphaValues[4][4],
|
||||
bool nonOpaquePunchThroughAlpha) const
|
||||
{
|
||||
const auto &block = u.idht.mode.idm.colors.diff;
|
||||
int b1 = extend_5to8bits(block.B);
|
||||
int g1 = extend_5to8bits(block.G);
|
||||
int r1 = extend_5to8bits(block.R);
|
||||
int r2 = extend_5to8bits(block.R + block.dR);
|
||||
int g2 = extend_5to8bits(block.G + block.dG);
|
||||
int b2 = extend_5to8bits(block.B + block.dB);
|
||||
transcodeIndividualOrDifferentialBlockToBC1(dest, x, y, w, h, r1, g1, b1, r2, g2, b2,
|
||||
alphaValues, nonOpaquePunchThroughAlpha);
|
||||
}
|
||||
|
||||
void decodeSubblock(R8G8B8A8 *rgbaBlock,
|
||||
size_t pixelRange[2][2],
|
||||
size_t x,
|
||||
size_t y,
|
||||
size_t w,
|
||||
size_t h,
|
||||
const uint8_t alphaValues[4][4],
|
||||
bool flipbit,
|
||||
size_t subblockIdx,
|
||||
const R8G8B8A8 subblockColors[2][4]) const
|
||||
{
|
||||
size_t dxBegin = 0;
|
||||
size_t dxEnd = 4;
|
||||
size_t dyBegin = subblockIdx * 2;
|
||||
size_t dyEnd = dyBegin + 2;
|
||||
if (!flipbit)
|
||||
{
|
||||
std::swap(dxBegin, dyBegin);
|
||||
std::swap(dxEnd, dyEnd);
|
||||
}
|
||||
|
||||
for (size_t j = dyBegin; j < dyEnd && (y + j) < h; j++)
|
||||
{
|
||||
R8G8B8A8 *row = &rgbaBlock[j * 4];
|
||||
for (size_t i = dxBegin; i < dxEnd && (x + i) < w; i++)
|
||||
{
|
||||
const size_t pixelIndex = getIndex(i, j);
|
||||
if (valueMappingTable[pixelIndex] < valueMappingTable[pixelRange[subblockIdx][0]])
|
||||
{
|
||||
pixelRange[subblockIdx][0] = pixelIndex;
|
||||
}
|
||||
if (valueMappingTable[pixelIndex] > valueMappingTable[pixelRange[subblockIdx][1]])
|
||||
{
|
||||
pixelRange[subblockIdx][1] = pixelIndex;
|
||||
}
|
||||
|
||||
row[i] = subblockColors[subblockIdx][pixelIndex];
|
||||
row[i].A = alphaValues[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void transcodeIndividualOrDifferentialBlockToBC1(uint8_t *dest,
|
||||
size_t x,
|
||||
size_t y,
|
||||
size_t w,
|
||||
size_t h,
|
||||
int r1,
|
||||
int g1,
|
||||
int b1,
|
||||
int r2,
|
||||
int g2,
|
||||
int b2,
|
||||
const uint8_t alphaValues[4][4],
|
||||
bool nonOpaquePunchThroughAlpha) const
|
||||
{
|
||||
// A BC1 block has 2 endpoints, pixels is encoded as linear
|
||||
// interpolations of them. A ETC1/ETC2 individual or differential block
|
||||
// has 2 subblocks. Each subblock has one color and a modifier. We
|
||||
// compute the max intensity and min intensity pixel values to use as
|
||||
// our two BC1 endpoints and then map pixels to BC1 by projecting on the
|
||||
// line between the two endpoints and choosing the right fraction.
|
||||
//
|
||||
// In the future, we have 2 potential improvements to this algorithm.
|
||||
// 1. We don't actually need to decode ETC blocks to RGBs. Instead,
|
||||
// the subblock colors and pixel indices alreay contains enough
|
||||
// information for transcode. A direct mapping would be more
|
||||
// efficient here.
|
||||
// 2. Currently the BC1 endpoints come from the max and min intensity
|
||||
// of ETC colors. A principal component analysis (PCA) on them might
|
||||
// give us better quality results, with limited costs
|
||||
|
||||
const auto intensityModifier =
|
||||
nonOpaquePunchThroughAlpha ? intensityModifierNonOpaque : intensityModifierDefault;
|
||||
|
||||
// Compute the colors that pixels can have in each subblock both for
|
||||
// the decoding of the RGBA data and BC1 encoding
|
||||
R8G8B8A8 subblockColors[2][4];
|
||||
for (size_t modifierIdx = 0; modifierIdx < 4; modifierIdx++)
|
||||
{
|
||||
const int i1 = intensityModifier[u.idht.mode.idm.cw1][modifierIdx];
|
||||
subblockColors[0][modifierIdx] = createRGBA(r1 + i1, g1 + i1, b1 + i1);
|
||||
|
||||
const int i2 = intensityModifier[u.idht.mode.idm.cw2][modifierIdx];
|
||||
subblockColors[1][modifierIdx] = createRGBA(r2 + i2, g2 + i2, b2 + i2);
|
||||
}
|
||||
|
||||
// 1 and 3 are the argmax and argmin of valueMappingTable
|
||||
size_t pixelRange[2][2] = {{1, 3}, {1, 3}};
|
||||
R8G8B8A8 rgbaBlock[16];
|
||||
// Decode the block in rgbaBlock and store the inverse valueTableMapping
|
||||
// of {min(modifier index), max(modifier index)}
|
||||
for (size_t blockIdx = 0; blockIdx < 2; blockIdx++)
|
||||
{
|
||||
decodeSubblock(rgbaBlock, pixelRange, x, y, w, h, alphaValues, u.idht.mode.idm.flipbit,
|
||||
blockIdx, subblockColors);
|
||||
}
|
||||
if (nonOpaquePunchThroughAlpha)
|
||||
{
|
||||
decodePunchThroughAlphaBlock(reinterpret_cast<uint8_t *>(rgbaBlock), x, y, w, h,
|
||||
sizeof(R8G8B8A8) * 4);
|
||||
}
|
||||
|
||||
// Get the "min" and "max" pixel colors that have been used.
|
||||
R8G8B8A8 minColor;
|
||||
const R8G8B8A8 &minColor0 = subblockColors[0][pixelRange[0][0]];
|
||||
const R8G8B8A8 &minColor1 = subblockColors[1][pixelRange[1][0]];
|
||||
if (minColor0.R + minColor0.G + minColor0.B < minColor1.R + minColor1.G + minColor1.B)
|
||||
{
|
||||
minColor = minColor0;
|
||||
}
|
||||
else
|
||||
{
|
||||
minColor = minColor1;
|
||||
}
|
||||
|
||||
R8G8B8A8 maxColor;
|
||||
const R8G8B8A8 &maxColor0 = subblockColors[0][pixelRange[0][1]];
|
||||
const R8G8B8A8 &maxColor1 = subblockColors[1][pixelRange[1][1]];
|
||||
if (maxColor0.R + maxColor0.G + maxColor0.B < maxColor1.R + maxColor1.G + maxColor1.B)
|
||||
{
|
||||
maxColor = maxColor1;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxColor = maxColor0;
|
||||
}
|
||||
|
||||
packBC1(dest, rgbaBlock, minColor, maxColor, !nonOpaquePunchThroughAlpha);
|
||||
}
|
||||
|
||||
void transcodeTBlockToBC1(uint8_t *dest,
|
||||
size_t x,
|
||||
size_t y,
|
||||
size_t w,
|
||||
size_t h,
|
||||
const uint8_t alphaValues[4][4],
|
||||
bool nonOpaquePunchThroughAlpha) const
|
||||
{
|
||||
// TODO (mgong): Will be implemented soon
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void transcodeHBlockToBC1(uint8_t *dest,
|
||||
size_t x,
|
||||
size_t y,
|
||||
size_t w,
|
||||
size_t h,
|
||||
const uint8_t alphaValues[4][4],
|
||||
bool nonOpaquePunchThroughAlpha) const
|
||||
{
|
||||
// TODO (mgong): Will be implemented soon
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void transcodePlanarBlockToBC1(uint8_t *dest,
|
||||
size_t x,
|
||||
size_t y,
|
||||
size_t w,
|
||||
size_t h,
|
||||
const uint8_t alphaValues[4][4]) const
|
||||
{
|
||||
// TODO (mgong): Will be implemented soon
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
// Single channel utility functions
|
||||
int getSingleChannel(size_t x, size_t y, bool isSigned) const
|
||||
{
|
||||
@@ -756,6 +1192,38 @@ void LoadETC2RGB8ToRGBA8(size_t width,
|
||||
}
|
||||
}
|
||||
|
||||
void LoadETC2RGB8ToBC1(size_t width,
|
||||
size_t height,
|
||||
size_t depth,
|
||||
const uint8_t *input,
|
||||
size_t inputRowPitch,
|
||||
size_t inputDepthPitch,
|
||||
uint8_t *output,
|
||||
size_t outputRowPitch,
|
||||
size_t outputDepthPitch,
|
||||
bool punchthroughAlpha)
|
||||
{
|
||||
for (size_t z = 0; z < depth; z++)
|
||||
{
|
||||
for (size_t y = 0; y < height; y += 4)
|
||||
{
|
||||
const ETC2Block *sourceRow =
|
||||
OffsetDataPointer<ETC2Block>(input, y / 4, z, inputRowPitch, inputDepthPitch);
|
||||
uint8_t *destRow =
|
||||
OffsetDataPointer<uint8_t>(output, y / 4, z, outputRowPitch, outputDepthPitch);
|
||||
|
||||
for (size_t x = 0; x < width; x += 4)
|
||||
{
|
||||
const ETC2Block *sourceBlock = sourceRow + (x / 4);
|
||||
uint8_t *destPixels = destRow + (x * 2);
|
||||
|
||||
sourceBlock->transcodeAsBC1(destPixels, x, y, width, height, DefaultETCAlphaValues,
|
||||
punchthroughAlpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoadETC2RGBA8ToRGBA8(size_t width,
|
||||
size_t height,
|
||||
size_t depth,
|
||||
@@ -810,6 +1278,20 @@ void LoadETC1RGB8ToRGBA8(size_t width,
|
||||
outputRowPitch, outputDepthPitch, false);
|
||||
}
|
||||
|
||||
void LoadETC1RGB8ToBC1(size_t width,
|
||||
size_t height,
|
||||
size_t depth,
|
||||
const uint8_t *input,
|
||||
size_t inputRowPitch,
|
||||
size_t inputDepthPitch,
|
||||
uint8_t *output,
|
||||
size_t outputRowPitch,
|
||||
size_t outputDepthPitch)
|
||||
{
|
||||
LoadETC2RGB8ToBC1(width, height, depth, input, inputRowPitch, inputDepthPitch, output,
|
||||
outputRowPitch, outputDepthPitch, false);
|
||||
}
|
||||
|
||||
void LoadEACR11ToR8(size_t width,
|
||||
size_t height,
|
||||
size_t depth,
|
||||
|
||||
@@ -26,6 +26,16 @@ void LoadETC1RGB8ToRGBA8(size_t width,
|
||||
size_t outputRowPitch,
|
||||
size_t outputDepthPitch);
|
||||
|
||||
void LoadETC1RGB8ToBC1(size_t width,
|
||||
size_t height,
|
||||
size_t depth,
|
||||
const uint8_t *input,
|
||||
size_t inputRowPitch,
|
||||
size_t inputDepthPitch,
|
||||
uint8_t *output,
|
||||
size_t outputRowPitch,
|
||||
size_t outputDepthPitch);
|
||||
|
||||
void LoadEACR11ToR8(size_t width,
|
||||
size_t height,
|
||||
size_t depth,
|
||||
|
||||
@@ -334,6 +334,7 @@ bool CompressedTextureFormatRequiresExactSize(GLenum internalFormat)
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
|
||||
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
||||
@@ -195,6 +195,14 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level,
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
|
||||
if (!context->getExtensions().lossyETCDecode)
|
||||
{
|
||||
context->recordError(
|
||||
Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported"));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
context->recordError(Error(
|
||||
GL_INVALID_ENUM, "internalformat is not a supported compressed internal format"));
|
||||
@@ -398,6 +406,21 @@ bool ValidateES2TexImageParameters(Context *context, GLenum target, GLint level,
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
|
||||
if (context->getExtensions().lossyETCDecode)
|
||||
{
|
||||
context->recordError(
|
||||
Error(GL_INVALID_OPERATION,
|
||||
"ETC1_RGB8_LOSSY_DECODE_ANGLE can't work with this type."));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
context->recordError(
|
||||
Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported."));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case GL_DEPTH_COMPONENT:
|
||||
case GL_DEPTH_STENCIL_OES:
|
||||
if (!context->getExtensions().depthTextures)
|
||||
@@ -564,6 +587,7 @@ bool ValidateES2CopyTexImageParameters(ValidationContext *context,
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
|
||||
case GL_ETC1_RGB8_OES:
|
||||
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
|
||||
context->recordError(Error(GL_INVALID_OPERATION));
|
||||
return false;
|
||||
case GL_DEPTH_COMPONENT:
|
||||
@@ -716,6 +740,20 @@ bool ValidateES2CopyTexImageParameters(ValidationContext *context,
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
|
||||
if (context->getExtensions().lossyETCDecode)
|
||||
{
|
||||
context->recordError(Error(GL_INVALID_OPERATION,
|
||||
"ETC1_RGB8_LOSSY_DECODE_ANGLE can't be copied to."));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
context->recordError(
|
||||
Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported."));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case GL_DEPTH_COMPONENT:
|
||||
case GL_DEPTH_COMPONENT16:
|
||||
case GL_DEPTH_COMPONENT32_OES:
|
||||
@@ -840,6 +878,14 @@ bool ValidateES2TexStorageParameters(Context *context, GLenum target, GLsizei le
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case GL_ETC1_RGB8_LOSSY_DECODE_ANGLE:
|
||||
if (!context->getExtensions().lossyETCDecode)
|
||||
{
|
||||
context->recordError(
|
||||
Error(GL_INVALID_ENUM, "ANGLE_lossy_etc_decode extension is not supported."));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case GL_RGBA32F_EXT:
|
||||
case GL_RGB32F_EXT:
|
||||
case GL_ALPHA32F_EXT:
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
'<(angle_path)/src/tests/gl_tests/DiscardFramebufferEXTTest.cpp',
|
||||
'<(angle_path)/src/tests/gl_tests/DrawBuffersTest.cpp',
|
||||
'<(angle_path)/src/tests/gl_tests/DrawElementsTest.cpp',
|
||||
'<(angle_path)/src/tests/gl_tests/ETCTextureTest.cpp',
|
||||
'<(angle_path)/src/tests/gl_tests/FenceSyncTests.cpp',
|
||||
'<(angle_path)/src/tests/gl_tests/FramebufferFormatsTest.cpp',
|
||||
'<(angle_path)/src/tests/gl_tests/FramebufferRenderMipmapTest.cpp',
|
||||
|
||||
79
src/tests/gl_tests/ETCTextureTest.cpp
Normal file
79
src/tests/gl_tests/ETCTextureTest.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// Copyright 2015 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// ETCTextureTest:
|
||||
// Tests for ETC lossy decode formats.
|
||||
//
|
||||
|
||||
#include "test_utils/ANGLETest.h"
|
||||
|
||||
using namespace angle;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class ETCTextureTest : public ANGLETest
|
||||
{
|
||||
protected:
|
||||
ETCTextureTest() : mTexture(0u)
|
||||
{
|
||||
setWindowWidth(128);
|
||||
setWindowHeight(128);
|
||||
setConfigRedBits(8);
|
||||
setConfigGreenBits(8);
|
||||
setConfigBlueBits(8);
|
||||
setConfigAlphaBits(8);
|
||||
}
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
ANGLETest::SetUp();
|
||||
|
||||
glGenTextures(1, &mTexture);
|
||||
ASSERT_GL_NO_ERROR();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
glDeleteTextures(1, &mTexture);
|
||||
|
||||
ANGLETest::TearDown();
|
||||
}
|
||||
|
||||
GLuint mTexture;
|
||||
};
|
||||
|
||||
// Tests a texture with ETC1 lossy decode format
|
||||
TEST_P(ETCTextureTest, ETC1Validation)
|
||||
{
|
||||
bool supported = extensionEnabled("GL_ANGLE_lossy_etc_decode");
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mTexture);
|
||||
|
||||
GLubyte pixel[8] = {0};
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_ETC1_RGB8_LOSSY_DECODE_ANGLE, 4, 4, 0,
|
||||
sizeof(pixel), pixel);
|
||||
if (supported)
|
||||
{
|
||||
EXPECT_GL_NO_ERROR();
|
||||
|
||||
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_ETC1_RGB8_LOSSY_DECODE_ANGLE,
|
||||
sizeof(pixel), pixel);
|
||||
EXPECT_GL_NO_ERROR();
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_GL_ERROR(GL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
|
||||
ANGLE_INSTANTIATE_TEST(ETCTextureTest,
|
||||
ES2_D3D9(),
|
||||
ES2_D3D11(),
|
||||
ES2_D3D11_FL9_3(),
|
||||
ES3_D3D11(),
|
||||
ES2_OPENGL(),
|
||||
ES3_OPENGL());
|
||||
} // anonymous namespace
|
||||
Reference in New Issue
Block a user