From 811540f9c3085346a43663b8d003a232ff6cc4dc Mon Sep 17 00:00:00 2001 From: Hailin Zhang Date: Mon, 10 Jan 2022 22:44:23 -0800 Subject: add rgtc externsion support Bug: b/213168245 Change-Id: Ib05f46888221ea76ab3707baae762d6f124efedb --- host-common/FeatureControlDefHost.h | 1 + stream-servers/GfxStreamBackend.cpp | 1 + stream-servers/RenderControl.cpp | 6 +- .../glestranslator/GLES_V2/GLESv2Validate.cpp | 4 + stream-servers/glestranslator/GLcommon/Android.bp | 1 + .../glestranslator/GLcommon/CMakeLists.txt | 1 + .../glestranslator/GLcommon/GLEScontext.cpp | 6 + .../glestranslator/GLcommon/TextureUtils.cpp | 116 +++++++++++++++- stream-servers/glestranslator/GLcommon/rgtc.cpp | 149 +++++++++++++++++++++ .../glestranslator/include/GLcommon/GLEScontext.h | 1 + .../glestranslator/include/GLcommon/TextureUtils.h | 26 ++++ .../glestranslator/include/GLcommon/rgtc.h | 42 ++++++ 12 files changed, 350 insertions(+), 4 deletions(-) create mode 100644 stream-servers/glestranslator/GLcommon/rgtc.cpp create mode 100644 stream-servers/glestranslator/include/GLcommon/rgtc.h diff --git a/host-common/FeatureControlDefHost.h b/host-common/FeatureControlDefHost.h index 8b437887..72d3c9f5 100644 --- a/host-common/FeatureControlDefHost.h +++ b/host-common/FeatureControlDefHost.h @@ -71,5 +71,6 @@ FEATURE_CONTROL_ITEM(BptcTextureSupport) FEATURE_CONTROL_ITEM(GuestUsesAngle) FEATURE_CONTROL_ITEM(VirtioVsockPipe) FEATURE_CONTROL_ITEM(S3tcTextureSupport) +FEATURE_CONTROL_ITEM(RgtcTextureSupport) FEATURE_CONTROL_ITEM(VulkanNativeSwapchain) FEATURE_CONTROL_ITEM(VirtioGpuFenceContexts) diff --git a/stream-servers/GfxStreamBackend.cpp b/stream-servers/GfxStreamBackend.cpp index dd6fc61f..4c69ee2c 100644 --- a/stream-servers/GfxStreamBackend.cpp +++ b/stream-servers/GfxStreamBackend.cpp @@ -407,6 +407,7 @@ extern "C" VG_EXPORT void gfxstream_backend_init( kFeature_BptcTextureSupport, bptcTextureSupport); feature_set_enabled_override( kFeature_S3tcTextureSupport, s3tcTextureSupport); + feature_set_enabled_override(kFeature_RgtcTextureSupport, true); feature_set_enabled_override( kFeature_GLDirectMem, false); feature_set_enabled_override( diff --git a/stream-servers/RenderControl.cpp b/stream-servers/RenderControl.cpp index db6507b4..09dbf9c1 100644 --- a/stream-servers/RenderControl.cpp +++ b/stream-servers/RenderControl.cpp @@ -659,7 +659,11 @@ static EGLint rcGetGLString(EGLenum name, void* buffer, EGLint bufferSize) { if (feature_is_enabled(kFeature_S3tcTextureSupport)) { glStr += "GL_EXT_texture_compression_s3tc "; - } + } + + if (feature_is_enabled(kFeature_RgtcTextureSupport)) { + glStr += "GL_EXT_texture_compression_rgtc "; + } // Host side tracing support. glStr += kHostSideTracing; diff --git a/stream-servers/glestranslator/GLES_V2/GLESv2Validate.cpp b/stream-servers/glestranslator/GLES_V2/GLESv2Validate.cpp index 1db5a503..c661bed6 100644 --- a/stream-servers/glestranslator/GLES_V2/GLESv2Validate.cpp +++ b/stream-servers/glestranslator/GLES_V2/GLESv2Validate.cpp @@ -759,6 +759,10 @@ bool GLESv2Validate::isCompressedFormat(GLenum format) { case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: + case GL_COMPRESSED_RED_RGTC1_EXT: + case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT: + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: + case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: return true; } return false; diff --git a/stream-servers/glestranslator/GLcommon/Android.bp b/stream-servers/glestranslator/GLcommon/Android.bp index c44b1473..91be25d5 100644 --- a/stream-servers/glestranslator/GLcommon/Android.bp +++ b/stream-servers/glestranslator/GLcommon/Android.bp @@ -25,6 +25,7 @@ cc_library_static { ], srcs: [ "etc.cpp", + "rgtc.cpp", "FramebufferData.cpp", "GLBackgroundLoader.cpp", "GLDispatch.cpp", diff --git a/stream-servers/glestranslator/GLcommon/CMakeLists.txt b/stream-servers/glestranslator/GLcommon/CMakeLists.txt index 671f9b4d..176f990d 100644 --- a/stream-servers/glestranslator/GLcommon/CMakeLists.txt +++ b/stream-servers/glestranslator/GLcommon/CMakeLists.txt @@ -1,6 +1,7 @@ add_library( GLcommon etc.cpp + rgtc.cpp FramebufferData.cpp GLBackgroundLoader.cpp GLDispatch.cpp diff --git a/stream-servers/glestranslator/GLcommon/GLEScontext.cpp b/stream-servers/glestranslator/GLcommon/GLEScontext.cpp index 5462d814..f8131dc4 100644 --- a/stream-servers/glestranslator/GLcommon/GLEScontext.cpp +++ b/stream-servers/glestranslator/GLcommon/GLEScontext.cpp @@ -1880,6 +1880,12 @@ void GLEScontext::initCapsLocked(const GLubyte * extensionString) s_glSupport.hasS3tcSupport = true; } } + + if (feature_is_enabled(kFeature_RgtcTextureSupport)) { + if (strstr(cstring, "GL_EXT_texture_compression_rgtc") != NULL) { + s_glSupport.hasRgtcSupport = true; + } + } } void GLEScontext::buildStrings(bool isGles1, const char* baseVendor, diff --git a/stream-servers/glestranslator/GLcommon/TextureUtils.cpp b/stream-servers/glestranslator/GLcommon/TextureUtils.cpp index d716437a..2daf1dd1 100644 --- a/stream-servers/glestranslator/GLcommon/TextureUtils.cpp +++ b/stream-servers/glestranslator/GLcommon/TextureUtils.cpp @@ -343,7 +343,14 @@ GLenum decompressedInternalFormat(GLEScontext* ctx, GLenum compressedFormat) { case GL_PALETTE8_RGBA4_OES: case GL_PALETTE8_RGB5_A1_OES: return glrgba; - + case GL_COMPRESSED_RED_RGTC1_EXT: // BC4U + return GL_R8; + case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT: // BC4S + return GL_R8_SNORM; + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: // BC5U + return GL_RG8; + case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: // BC5S + return GL_RG8_SNORM; default: return compressedFormat; } @@ -400,7 +407,7 @@ void doCompressedTexImage2D(GLEScontext* ctx, GLenum target, GLint level, &unpackBuffer); needUnpackBuffer = unpackBuffer; } - + TextureUnpackReset unpack(ctx); if (isEtcFormat(internalformat)) { GLint format = GL_RGB; GLint type = GL_UNSIGNED_BYTE; @@ -514,7 +521,8 @@ void doCompressedTexImage2D(GLEScontext* ctx, GLenum target, GLint level, width, height, ctx->getMaxTexSize() + 2), GL_INVALID_VALUE); SET_ERROR_IF(!data,GL_INVALID_OPERATION); - + //the decoder fully packed the pixels. + ctx->dispatcher().glPixelStorei(GL_UNPACK_ALIGNMENT, 1); int nMipmaps = -level + 1; GLsizei tmpWidth = width; GLsizei tmpHeight = height; @@ -532,6 +540,61 @@ void doCompressedTexImage2D(GLEScontext* ctx, GLenum target, GLint level, tmpHeight /= 2; delete [] uncompressed; } + } else if (isRgtcFormat(internalformat)) { + GLint format, type; + GLint convertedInternalFormat = decompressedInternalFormat(ctx, internalformat); + RGTCImageFormat rgtcFormat; + switch (internalformat) { + case GL_COMPRESSED_RED_RGTC1_EXT: // BC4U + format = GL_RED; + type = GL_UNSIGNED_BYTE; + rgtcFormat = BC4_UNORM; + break; + case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT: // BC4S + format = GL_RED; + type = GL_BYTE; + rgtcFormat = BC4_SNORM; + break; + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: // BC5U + format = GL_RG; + type = GL_UNSIGNED_BYTE; + rgtcFormat = BC5_UNORM; + break; + case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: // BC5S + format = GL_RG; + type = GL_BYTE; + rgtcFormat = BC5_SNORM; + break; + } + size_t pixelSize = rgtc_get_decoded_pixel_size(rgtcFormat); + GLsizei compressedSize = rgtc_get_encoded_image_size(rgtcFormat, width, height); + SET_ERROR_IF((compressedSize != imageSize), GL_INVALID_VALUE); + std::unique_ptr unpackData; + bool emulateCompressedData = false; + if (needUnpackBuffer) { + unpackData.reset( + new ScopedFetchUnpackData(ctx, reinterpret_cast(data), compressedSize)); + data = unpackData->data(); + SET_ERROR_IF(!data, GL_INVALID_OPERATION); + } else { + if (!data) { + emulateCompressedData = true; + data = new char[compressedSize]; + } + } + const int32_t align = ctx->getUnpackAlignment() - 1; + const int32_t bpr = ((width * pixelSize) + align) & ~align; + const size_t size = bpr * height; + std::unique_ptr pOut(new uint8_t[size]); + + int res = + rgtc_decode_image((const uint8_t*)data, rgtcFormat, pOut.get(), width, height, bpr); + SET_ERROR_IF(res != 0, GL_INVALID_VALUE); + glTexImage2DPtr(target, level, convertedInternalFormat, width, height, border, format, type, + pOut.get()); + if (emulateCompressedData) { + delete[](char*) data; + } } else { SET_ERROR_IF(1, GL_INVALID_ENUM); } @@ -791,6 +854,19 @@ void forEachS3tcFormat(std::function f) { f(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT); } +bool isRgtcFormat(GLenum format) { + switch (format) { + case GL_COMPRESSED_RED_RGTC1_EXT: // BC4U + case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT: // BC4S + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: // BC5U + case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: // BC5S + return true; + default: + break; + } + return false; +} + bool isEtc2OrAstcFormat(GLenum format) { switch (format) { case GL_COMPRESSED_RGB8_ETC2: @@ -819,6 +895,8 @@ bool shouldPassthroughCompressedFormat(GLEScontext* ctx, GLenum internalformat) return ctx->getCaps()->hasBptcSupport; } else if (isS3tcFormat(internalformat)) { return ctx->getCaps()->hasS3tcSupport; + } else if (isRgtcFormat(internalformat)) { + return ctx->getCaps()->hasRgtcSupport; } return false; } @@ -1154,3 +1232,35 @@ GLenum getTypeFromInternalFormat(GLint internalFormat) { return GL_UNSIGNED_BYTE; } } + + +GLint TextureUnpackReset::unpackCheckAndUpdate(GLenum name, GLint newValue) { + GLint curValue; + glesContext->dispatcher().glGetIntegerv(name, &curValue); + if (curValue != newValue) { + glesContext->dispatcher().glPixelStorei(name, newValue); + } + return curValue; +} + +TextureUnpackReset::TextureUnpackReset(GLEScontext* ctx) : glesContext(ctx) { + if (glesContext->getMajorVersion() >= 3) { + unpackRowLength = unpackCheckAndUpdate(GL_UNPACK_ROW_LENGTH, kUnpackRowLength); + unpackImageHeight = unpackCheckAndUpdate(GL_UNPACK_IMAGE_HEIGHT, kUnpackImageHeight); + unpackSkipRows = unpackCheckAndUpdate(GL_UNPACK_SKIP_ROWS, kUnpackSkipRows); + unpackSkipPixels = unpackCheckAndUpdate(GL_UNPACK_SKIP_PIXELS, kUnpackSkipPixels); + unpackSkipImages = unpackCheckAndUpdate(GL_UNPACK_SKIP_IMAGES, kUnpackSkipImages); + unpackAlignment = unpackCheckAndUpdate(GL_UNPACK_ALIGNMENT, kUnpackAlignment); + } +} +TextureUnpackReset::~TextureUnpackReset() { + if (glesContext->getMajorVersion() >= 3) { + unpackCheckAndUpdate(GL_UNPACK_ROW_LENGTH, unpackRowLength); + unpackCheckAndUpdate(GL_UNPACK_IMAGE_HEIGHT, unpackImageHeight); + unpackCheckAndUpdate(GL_UNPACK_SKIP_ROWS, unpackSkipRows); + unpackCheckAndUpdate(GL_UNPACK_SKIP_PIXELS, unpackSkipPixels); + unpackCheckAndUpdate(GL_UNPACK_SKIP_IMAGES, unpackSkipImages); + unpackCheckAndUpdate(GL_UNPACK_ALIGNMENT, unpackAlignment); + } +} + diff --git a/stream-servers/glestranslator/GLcommon/rgtc.cpp b/stream-servers/glestranslator/GLcommon/rgtc.cpp new file mode 100644 index 00000000..7cebe32d --- /dev/null +++ b/stream-servers/glestranslator/GLcommon/rgtc.cpp @@ -0,0 +1,149 @@ +// Copyright 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "GLcommon/rgtc.h" +#include +#include +#include + +// From https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_compression_rgtc.txt +// according to the spec +// RGTC1_RED = BC4_UNORM, +// RGTC1_SIGNED_RED = BC4_SNORM, +// RGTC2_RG = BC5_UNORM, +// RGTC2_SIGNED_RG = BC5_SNORM. +// the full codec spec can be found here +// https://docs.microsoft.com/en-gb/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#bc5 + +static constexpr int kBlockSize = 4; + +inline size_t rgtc_get_block_size(RGTCImageFormat format) { + switch (format) { + case BC4_UNORM: + case BC4_SNORM: + return 8; + case BC5_UNORM: + case BC5_SNORM: + return 16; + default: + assert(0); + return 0; + } +} + +size_t rgtc_get_decoded_pixel_size(RGTCImageFormat format) { + switch (format) { + case BC4_UNORM: + case BC4_SNORM: + return 1; + case BC5_UNORM: + case BC5_SNORM: + return 2; + default: + assert(0); + return 0; + } +} + +template +struct get_expand_type {}; + +template <> +struct get_expand_type { + typedef int32_t type; +}; + +template <> +struct get_expand_type { + typedef uint32_t type; +}; + +template +void rgtc_decode_subblock(const uint32_t* data, T* out, int step, T d0, T d1 ) { + T r0 = static_cast(data[0] & 0xff); + T r1 = static_cast((data[0] >> 8) & 0xff); + uint64_t color_indexs = ((uint64_t)data[1] << 32 | data[0]) >> 16; + T colors[8] = {r0, r1,}; + typename get_expand_type::type c0 = r0; + typename get_expand_type::type c1 = r1; + if (c0 > c1) { + // 6 interpolated color values + for (int i = 2; i < 8; i++) { + colors[i] = static_cast((c0 * (8 - i) + c1 * (i - 1)) / 7.0f + 0.5f); + } + } else { + // 4 interpolated color values + for (int i = 2; i < 6; i++) { + colors[i] = static_cast((c0 * (6 - i) + c1 * (i - 1)) / 5.0f + 0.5f); + } + colors[6] = d0; + colors[7] = d1; + } + uint64_t index = color_indexs; + for (int i = 0 ; i < 16 ; i ++) { + *out = colors[index & 0x7]; + out += step; + index >>= 3; + } +} + +int rgtc_decode_image(const uint8_t* in, RGTCImageFormat format, uint8_t* out, uint32_t width, + uint32_t height, uint32_t stride) { + size_t data_block_size = rgtc_get_block_size(format); + size_t texel_size = rgtc_get_decoded_pixel_size(format); + const uint8_t* data_in = in; + // BC5 2 bytes per pixel + uint8_t pixels[kBlockSize * kBlockSize * 2]; + for (uint32_t y = 0; y < height; y += kBlockSize) { + uint32_t yEnd = height - y; + if (yEnd > kBlockSize) { + yEnd = kBlockSize; + } + for (uint32_t x = 0; x < width; x += kBlockSize) { + uint32_t xEnd = width - x; + if (xEnd > kBlockSize) { + xEnd = kBlockSize; + } + switch (format) { + case BC4_UNORM: + rgtc_decode_subblock((const uint32_t*)data_in, pixels, 1, 0, 1); + break; + case BC4_SNORM: + rgtc_decode_subblock((const uint32_t*)data_in, (int8_t*)pixels, 1, -1, 1); + break; + case BC5_UNORM: + rgtc_decode_subblock((const uint32_t*)data_in, pixels, 2, 0, 1); + rgtc_decode_subblock((const uint32_t*)data_in + 2, pixels + 1, 2, 0, 1); + break; + case BC5_SNORM: + rgtc_decode_subblock((const uint32_t*)data_in, (int8_t*)pixels, 2, -1, 1); + rgtc_decode_subblock((const uint32_t*)data_in + 2, (int8_t*)(pixels + 1), + 2, -1, 1); + break; + } + for (uint32_t cy = 0; cy < yEnd; cy ++) { + uint8_t* data_out = out + (y + cy) * stride + x * texel_size; + std::memcpy(data_out, pixels + kBlockSize * texel_size * cy, texel_size * xEnd); + } + data_in += data_block_size; + } + } + return 0; +} + +size_t rgtc_get_encoded_image_size(RGTCImageFormat format, uint32_t width, uint32_t height) { + uint32_t w = (width + kBlockSize - 1) / kBlockSize; + uint32_t h = (height + kBlockSize - 1) / kBlockSize; + return w * h * rgtc_get_block_size(format); +} \ No newline at end of file diff --git a/stream-servers/glestranslator/include/GLcommon/GLEScontext.h b/stream-servers/glestranslator/include/GLcommon/GLEScontext.h index e5809e5e..4ebf1871 100644 --- a/stream-servers/glestranslator/include/GLcommon/GLEScontext.h +++ b/stream-servers/glestranslator/include/GLcommon/GLEScontext.h @@ -111,6 +111,7 @@ struct GLSupport { bool hasAstcSupport = false; bool hasBptcSupport = false; bool hasS3tcSupport = false; + bool hasRgtcSupport = false; }; struct ArrayData { diff --git a/stream-servers/glestranslator/include/GLcommon/TextureUtils.h b/stream-servers/glestranslator/include/GLcommon/TextureUtils.h index 77e60ae0..21cb70f1 100644 --- a/stream-servers/glestranslator/include/GLcommon/TextureUtils.h +++ b/stream-servers/glestranslator/include/GLcommon/TextureUtils.h @@ -19,6 +19,7 @@ #include "GLEScontext.h" #include "PaletteTexture.h" #include "etc.h" +#include "rgtc.h" #include #include @@ -37,6 +38,7 @@ bool isEtc2Format(GLenum internalformat); bool isBptcFormat(GLenum internalformat); bool isS3tcFormat(GLenum internalformat); bool isPaletteFormat(GLenum internalformat); +bool isRgtcFormat(GLenum internalformat); int getCompressedFormats(int majorVersion, int* formats); void doCompressedTexImage2D(GLEScontext* ctx, GLenum target, GLint level, GLenum internalformat, GLsizei width, @@ -102,4 +104,28 @@ uint32_t texImageSize(GLenum internalformat, GLenum getFormatFromInternalFormat(GLint internalFormat); GLenum getTypeFromInternalFormat(GLint internalFormat); +class TextureUnpackReset { + public: + // Initial value for unpack + static constexpr GLint kUnpackRowLength = 0; + static constexpr GLint kUnpackImageHeight = 0; + static constexpr GLint kUnpackSkipRows = 0; + static constexpr GLint kUnpackSkipPixels = 0; + static constexpr GLint kUnpackSkipImages = 0; + static constexpr GLint kUnpackAlignment = 4; + + TextureUnpackReset(GLEScontext* ctx); + ~TextureUnpackReset(); + + GLint unpackRowLength; + GLint unpackImageHeight; + GLint unpackSkipRows; + GLint unpackSkipPixels; + GLint unpackSkipImages; + GLint unpackAlignment; + GLEScontext* glesContext = nullptr; + private: + GLint unpackCheckAndUpdate(GLenum name, GLint newValue); +}; + #endif diff --git a/stream-servers/glestranslator/include/GLcommon/rgtc.h b/stream-servers/glestranslator/include/GLcommon/rgtc.h new file mode 100644 index 00000000..0de5c60d --- /dev/null +++ b/stream-servers/glestranslator/include/GLcommon/rgtc.h @@ -0,0 +1,42 @@ +// Copyright 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +enum RGTCImageFormat { BC4_UNORM, BC4_SNORM, BC5_UNORM, BC5_SNORM }; + +#ifdef __cplusplus +extern "C" { +#endif + +// Decode an entire image. +// pIn - pointer to encoded data. +// pOut - pointer to the image data. Will be written such that +// pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be +// large enough to store entire image. +// (pixelSize=3 for rgb images, pixelSize=4 for images with alpha channel) +// returns non-zero if there is an error. +int rgtc_decode_image(const uint8_t* pIn, RGTCImageFormat format, uint8_t* pOut, uint32_t width, + uint32_t height, uint32_t stride); + +size_t rgtc_get_encoded_image_size(RGTCImageFormat format, uint32_t width, uint32_t height); + +size_t rgtc_get_decoded_pixel_size(RGTCImageFormat format); + +#ifdef __cplusplus +} +#endif \ No newline at end of file -- cgit v1.2.3