Support for BC1, BC2, BC3, BC4 and BC5
Added a block compression (BCn) decoder and logic for all
formats for n in the [1, 5] range.
The decompression follows the same logic as the ETC2
decompression in vk::Image.
Tests: dEQP-VK.*bc*
Bug: b/146052572
Change-Id: I64fac0a7af52e1be209c1cfd5373744918c7df14
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/39369
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Device/BC_Decoder.cpp b/src/Device/BC_Decoder.cpp
new file mode 100644
index 0000000..f14d871
--- /dev/null
+++ b/src/Device/BC_Decoder.cpp
@@ -0,0 +1,305 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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 "BC_Decoder.hpp"
+
+namespace
+{
+ static constexpr int BlockWidth = 4;
+ static constexpr int BlockHeight = 4;
+
+ struct BC_color
+ {
+ void decode(unsigned char* dst, int x, int y, int dstW, int dstH, int dstPitch, int dstBpp, bool hasAlphaChannel, bool hasSeparateAlpha) const
+ {
+ Color c[4];
+ c[0].extract565(c0);
+ c[1].extract565(c1);
+ if(hasSeparateAlpha || (c0 > c1))
+ {
+ c[2] = ((c[0] * 2) + c[1]) / 3;
+ c[3] = ((c[1] * 2) + c[0]) / 3;
+ }
+ else
+ {
+ c[2] = (c[0] + c[1]) >> 1;
+ if(hasAlphaChannel)
+ {
+ c[3].clearAlpha();
+ }
+ }
+
+ for(int j = 0; j < BlockHeight && (y + j) < dstH; j++)
+ {
+ int dstOffset = j * dstPitch;
+ int idxOffset = j * BlockHeight;
+ for(int i = 0; i < BlockWidth && (x + i) < dstW; i++, idxOffset++, dstOffset += dstBpp)
+ {
+ *reinterpret_cast<unsigned int*>(dst + dstOffset) = c[getIdx(idxOffset)].pack8888();
+ }
+ }
+ }
+
+ private:
+ struct Color
+ {
+ Color()
+ {
+ c[0] = c[1] = c[2] = 0;
+ c[3] = 0xFF000000;
+ }
+
+ void extract565(const unsigned int c565)
+ {
+ c[0] = ((c565 & 0x0000001F) << 3) | ((c565 & 0x0000001C) >> 2);
+ c[1] = ((c565 & 0x000007E0) >> 3) | ((c565 & 0x00000600) >> 9);
+ c[2] = ((c565 & 0x0000F800) >> 8) | ((c565 & 0x0000E000) >> 13);
+ }
+
+ unsigned int pack8888() const
+ {
+ return ((c[2] & 0xFF) << 16) | ((c[1] & 0xFF) << 8) | (c[0] & 0xFF) | c[3];
+ }
+
+ void clearAlpha()
+ {
+ c[3] = 0;
+ }
+
+ Color operator*(int factor) const
+ {
+ Color res;
+ for(int i = 0; i < 4; ++i)
+ {
+ res.c[i] = c[i] * factor;
+ }
+ return res;
+ }
+
+ Color operator/(int factor) const
+ {
+ Color res;
+ for(int i = 0; i < 4; ++i)
+ {
+ res.c[i] = c[i] / factor;
+ }
+ return res;
+ }
+
+ Color operator>>(int shift) const
+ {
+ Color res;
+ for(int i = 0; i < 4; ++i)
+ {
+ res.c[i] = c[i] >> shift;
+ }
+ return res;
+ }
+
+ Color operator+(Color const& obj) const
+ {
+ Color res;
+ for(int i = 0; i < 4; ++i)
+ {
+ res.c[i] = c[i] + obj.c[i];
+ }
+ return res;
+ }
+
+ private:
+ int c[4];
+ };
+
+ unsigned int getIdx(int i) const
+ {
+ int offset = i << 1; // 2 bytes per index
+ return (idx & (0x3 << offset)) >> offset;
+ }
+
+ unsigned short c0;
+ unsigned short c1;
+ unsigned int idx;
+ };
+
+ struct BC_channel
+ {
+ void decode(unsigned char* dst, int x, int y, int dstW, int dstH, int dstPitch, int dstBpp, int channel, bool isSigned) const
+ {
+ int c[8] = { 0 };
+
+ if(isSigned)
+ {
+ c[0] = static_cast<signed char>(data & 0xFF);
+ c[1] = static_cast<signed char>((data & 0xFF00) >> 8);
+ }
+ else
+ {
+ c[0] = static_cast<unsigned char>(data & 0xFF);
+ c[1] = static_cast<unsigned char>((data & 0xFF00) >> 8);
+ }
+
+ if(c[0] > c[1])
+ {
+ for(int i = 2; i < 8; ++i)
+ {
+ c[i] = ((8 - i) * c[0] + (i - 1) * c[1]) / 7;
+ }
+ }
+ else
+ {
+ for(int i = 2; i < 6; ++i)
+ {
+ c[i] = ((6 - i) * c[0] + (i - 1) * c[1]) / 5;
+ }
+ c[6] = isSigned ? -128 : 0;
+ c[7] = isSigned ? 127 : 255;
+ }
+
+ for(int j = 0; j < BlockHeight && (y + j) < dstH; j++)
+ {
+ for(int i = 0; i < BlockWidth && (x + i) < dstW; i++)
+ {
+ dst[channel + (i * dstBpp) + (j * dstPitch)] = static_cast<unsigned char>(c[getIdx((j * BlockHeight) + i)]);
+ }
+ }
+ }
+
+ private:
+ unsigned char getIdx(int i) const
+ {
+ int offset = i * 3 + 16;
+ return static_cast<unsigned char>((data & (0x7ull << offset)) >> offset);
+ }
+
+ unsigned long long data;
+ };
+
+ struct BC_alpha
+ {
+ void decode(unsigned char* dst, int x, int y, int dstW, int dstH, int dstPitch, int dstBpp) const
+ {
+ dst += 3; // Write only to alpha (channel 3)
+ for(int j = 0; j < BlockHeight && (y + j) < dstH; j++, dst += dstPitch)
+ {
+ unsigned char* dstRow = dst;
+ for(int i = 0; i < BlockWidth && (x + i) < dstW; i++, dstRow += dstBpp)
+ {
+ *dstRow = getAlpha(j * BlockHeight + i);
+ }
+ }
+ }
+
+ private:
+ unsigned char getAlpha(int i) const
+ {
+ int offset = i << 2;
+ int alpha = (data & (0xFull << offset)) >> offset;
+ return static_cast<unsigned char>(alpha | (alpha << 4));
+ }
+
+ unsigned long long data;
+ };
+} // end namespace
+
+// Decodes 1 to 4 channel images to 8 bit output
+bool BC_Decoder::Decode(const unsigned char* src, unsigned char* dst, int w, int h, int dstW, int dstH, int dstPitch, int dstBpp, int n, bool isNoAlphaU)
+{
+ static_assert(sizeof(BC_color) == 8, "BC_color must be 8 bytes");
+ static_assert(sizeof(BC_channel) == 8, "BC_channel must be 8 bytes");
+ static_assert(sizeof(BC_alpha) == 8, "BC_alpha must be 8 bytes");
+
+ const int dx = BlockWidth * dstBpp;
+ const int dy = BlockHeight * dstPitch;
+ const bool isAlpha = (n == 1) && !isNoAlphaU;
+ const bool isSigned = ((n == 4) || (n == 5) || (n == 6)) && !isNoAlphaU;
+
+ switch(n)
+ {
+ case 1: // BC1
+ {
+ const BC_color* color = reinterpret_cast<const BC_color*>(src);
+ for(int y = 0; y < h; y += BlockHeight, dst += dy)
+ {
+ unsigned char* dstRow = dst;
+ for(int x = 0; x < w; x += BlockWidth, ++color, dstRow += dx)
+ {
+ color->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, isAlpha, false);
+ }
+ }
+ }
+ break;
+ case 2: // BC2
+ {
+ const BC_alpha* alpha = reinterpret_cast<const BC_alpha*>(src);
+ const BC_color* color = reinterpret_cast<const BC_color*>(src + 8);
+ for(int y = 0; y < h; y += BlockHeight, dst += dy)
+ {
+ unsigned char* dstRow = dst;
+ for(int x = 0; x < w; x += BlockWidth, alpha += 2, color += 2, dstRow += dx)
+ {
+ color->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, isAlpha, true);
+ alpha->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp);
+ }
+ }
+ }
+ break;
+ case 3: // BC3
+ {
+ const BC_channel* alpha = reinterpret_cast<const BC_channel*>(src);
+ const BC_color* color = reinterpret_cast<const BC_color*>(src + 8);
+ for(int y = 0; y < h; y += BlockHeight, dst += dy)
+ {
+ unsigned char* dstRow = dst;
+ for(int x = 0; x < w; x += BlockWidth, alpha += 2, color += 2, dstRow += dx)
+ {
+ color->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, isAlpha, true);
+ alpha->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, 3, isSigned);
+ }
+ }
+ }
+ break;
+ case 4: // BC4
+ {
+ const BC_channel* red = reinterpret_cast<const BC_channel*>(src);
+ for(int y = 0; y < h; y += BlockHeight, dst += dy)
+ {
+ unsigned char* dstRow = dst;
+ for(int x = 0; x < w; x += BlockWidth, ++red, dstRow += dx)
+ {
+ red->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, 0, isSigned);
+ }
+ }
+ }
+ break;
+ case 5: // BC5
+ {
+ const BC_channel* red = reinterpret_cast<const BC_channel*>(src);
+ const BC_channel* green = reinterpret_cast<const BC_channel*>(src + 8);
+ for(int y = 0; y < h; y += BlockHeight, dst += dy)
+ {
+ unsigned char* dstRow = dst;
+ for(int x = 0; x < w; x += BlockWidth, red += 2, green += 2, dstRow += dx)
+ {
+ red->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, 0, isSigned);
+ green->decode(dstRow, x, y, dstW, dstH, dstPitch, dstBpp, 1, isSigned);
+ }
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/Device/BC_Decoder.hpp b/src/Device/BC_Decoder.hpp
new file mode 100644
index 0000000..28547bb
--- /dev/null
+++ b/src/Device/BC_Decoder.hpp
@@ -0,0 +1,33 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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.
+
+class BC_Decoder
+{
+public:
+
+ /// BCn_Decoder::Decode - Decodes 1 to 4 channel images to 8 bit output
+ /// @param src Pointer to BCn encoded image
+ /// @param dst Pointer to decoded output image
+ /// @param w src image width
+ /// @param h src image height
+ /// @param dstW dst image width
+ /// @param dstH dst image height
+ /// @param dstPitch dst image pitch (bytes per row)
+ /// @param dstBpp dst image bytes per pixel
+ /// @param n n in BCn format
+ /// @param isNoAlphaU BC1: true if RGB, BC2/BC3: unused, BC4/BC5: true if unsigned
+ /// @return true if the decoding was performed
+
+ static bool Decode(const unsigned char* src, unsigned char* dst, int w, int h, int dstW, int dstH, int dstPitch, int dstBpp, int n, bool isNoAlphaU);
+};
diff --git a/src/Device/BUILD.gn b/src/Device/BUILD.gn
index c412c71..e46869b 100644
--- a/src/Device/BUILD.gn
+++ b/src/Device/BUILD.gn
@@ -16,6 +16,7 @@
swiftshader_source_set("Device_headers") {
sources = [
+ "BC_Decoder.hpp",
"Blitter.hpp",
"Clipper.hpp",
"Color.hpp",
@@ -37,6 +38,7 @@
swiftshader_source_set("Device") {
sources = [
+ "BC_Decoder.cpp",
"Blitter.cpp",
"Clipper.cpp",
"Color.cpp",
diff --git a/src/Vulkan/VkFormat.cpp b/src/Vulkan/VkFormat.cpp
index f454d94..9d97c49 100644
--- a/src/Vulkan/VkFormat.cpp
+++ b/src/Vulkan/VkFormat.cpp
@@ -305,6 +305,10 @@
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
return true;
default:
return false;
@@ -555,6 +559,24 @@
return VK_FORMAT_R16G16_UNORM;
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
return VK_FORMAT_R16G16_SNORM;
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ return VK_FORMAT_B8G8R8A8_UNORM;
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ return VK_FORMAT_B8G8R8A8_SRGB;
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ return VK_FORMAT_R8_UNORM;
+ case VK_FORMAT_BC4_SNORM_BLOCK:
+ return VK_FORMAT_R8_SNORM;
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ return VK_FORMAT_R8G8_UNORM;
+ case VK_FORMAT_BC5_SNORM_BLOCK:
+ return VK_FORMAT_R8G8_SNORM;
default:
UNIMPLEMENTED("format: %d", int(format));
return VK_FORMAT_UNDEFINED;
@@ -1112,6 +1134,8 @@
case VK_FORMAT_D16_UNORM_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
return 1;
@@ -1136,6 +1160,8 @@
case VK_FORMAT_R64G64_UINT:
case VK_FORMAT_R64G64_SINT:
case VK_FORMAT_R64G64_SFLOAT:
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
return 2;
@@ -1172,6 +1198,8 @@
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
return 3;
@@ -1226,6 +1254,12 @@
case VK_FORMAT_R64G64B64A64_UINT:
case VK_FORMAT_R64G64B64A64_SINT:
case VK_FORMAT_R64G64B64A64_SFLOAT:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
@@ -1313,11 +1347,24 @@
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC5_UNORM_BLOCK:
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
return true;
case VK_FORMAT_R8G8B8A8_SNORM:
case VK_FORMAT_R8G8B8A8_SSCALED:
@@ -1342,11 +1389,10 @@
case VK_FORMAT_R32G32B32A32_SFLOAT:
case VK_FORMAT_R64G64B64A64_SINT:
case VK_FORMAT_R64G64B64A64_SFLOAT:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
- case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
- case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
- case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
// YCbCr formats treated as signed because VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY
// expects chroma components to be in range [-0.5, 0.5]
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
@@ -1636,6 +1682,12 @@
switch(format)
{
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
@@ -1643,6 +1695,12 @@
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
return 8 * ((width + 3) / 4); // 64 bit per 4x4 block, computed per 4 rows
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
@@ -1701,6 +1759,18 @@
switch(format)
{
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index e5d38e1..1606386 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -17,6 +17,7 @@
#include "VkDevice.hpp"
#include "VkImage.hpp"
#include "Device/Blitter.hpp"
+#include "Device/BC_Decoder.hpp"
#include "Device/ETC_Decoder.hpp"
#include <cstring>
@@ -53,6 +54,60 @@
}
}
+int GetBCn(const vk::Format& format)
+{
+ switch(format)
+ {
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ return 1;
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ return 2;
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ return 3;
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
+ return 4;
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
+ return 5;
+ default:
+ UNIMPLEMENTED("format: %d", int(format));
+ return 0;
+ }
+}
+
+// Returns true for BC1 if we have an RGB format, false for RGBA
+// Returns true for BC4 and BC5 if we have an unsigned format, false for signed
+// Ignored by BC2 and BC3
+bool GetNoAlphaOrUnsigned(const vk::Format& format)
+{
+ switch(format)
+ {
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ return true;
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
+ return false;
+ default:
+ UNIMPLEMENTED("format: %d", int(format));
+ return false;
+ }
+}
+
} // anonymous namespace
namespace vk {
@@ -917,6 +972,20 @@
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
decodeETC2(subresourceRange);
break;
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
+ decodeBC(subresourceRange);
+ break;
default:
break;
}
@@ -992,4 +1061,37 @@
}
}
-} // namespace vk
+void Image::decodeBC(const VkImageSubresourceRange& subresourceRange) const
+{
+ ASSERT(decompressedImage);
+
+ int n = GetBCn(format);
+ int noAlphaU = GetNoAlphaOrUnsigned(format);
+
+ uint32_t lastLayer = getLastLayerIndex(subresourceRange);
+ uint32_t lastMipLevel = getLastMipLevel(subresourceRange);
+
+ int bytes = decompressedImage->format.bytes();
+
+ VkImageSubresourceLayers subresourceLayers = { subresourceRange.aspectMask, subresourceRange.baseMipLevel, subresourceRange.baseArrayLayer, 1 };
+ for(; subresourceLayers.baseArrayLayer <= lastLayer; subresourceLayers.baseArrayLayer++)
+ {
+ for(; subresourceLayers.mipLevel <= lastMipLevel; subresourceLayers.mipLevel++)
+ {
+ VkExtent3D mipLevelExtent = getMipLevelExtent(static_cast<VkImageAspectFlagBits>(subresourceLayers.aspectMask), subresourceLayers.mipLevel);
+
+ int pitchB = decompressedImage->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, subresourceLayers.mipLevel);
+
+ for(int32_t depth = 0; depth < static_cast<int32_t>(mipLevelExtent.depth); depth++)
+ {
+ uint8_t* source = static_cast<uint8_t*>(getTexelPointer({ 0, 0, depth }, subresourceLayers));
+ uint8_t* dest = static_cast<uint8_t*>(decompressedImage->getTexelPointer({ 0, 0, depth }, subresourceLayers));
+
+ BC_Decoder::Decode(source, dest, mipLevelExtent.width, mipLevelExtent.height,
+ mipLevelExtent.width, mipLevelExtent.height, pitchB, bytes, n, noAlphaU);
+ }
+ }
+ }
+}
+
+} // namespace vk
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index bc67d7e..358834f 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -108,6 +108,7 @@
void clear(void* pixelData, VkFormat pixelFormat, const vk::Format& viewFormat, const VkImageSubresourceRange& subresourceRange, const VkRect2D& renderArea);
int borderSize() const;
void decodeETC2(const VkImageSubresourceRange& subresourceRange) const;
+ void decodeBC(const VkImageSubresourceRange& subresourceRange) const;
const Device *const device = nullptr;
DeviceMemory* deviceMemory = nullptr;
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 92d6b9a..b60e56e 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -475,6 +475,18 @@
case VK_FORMAT_R16G16B16A16_SFLOAT:
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: