diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index 1d8d20b3cc..cf82614c8d 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -283,7 +283,8 @@ enum class PixelDataType : uint8_t { FLOAT, //!< float (32-bits float) COMPRESSED, //!< compressed pixels, @see CompressedPixelDataType UINT_10F_11F_11F_REV, //!< three low precision floating-point numbers - USHORT_565 //!< unsigned int (16-bit), encodes 3 RGB channels + USHORT_565, //!< unsigned int (16-bit), encodes 3 RGB channels + UINT_2_10_10_10_REV, //!< unsigned normalized 10 bits RGB, 2 bits alpha }; //! Compressed pixel data types diff --git a/filament/backend/include/backend/PixelBufferDescriptor.h b/filament/backend/include/backend/PixelBufferDescriptor.h index eac1b23d19..39b215acd2 100644 --- a/filament/backend/include/backend/PixelBufferDescriptor.h +++ b/filament/backend/include/backend/PixelBufferDescriptor.h @@ -170,6 +170,11 @@ public: assert(format == PixelDataFormat::RGB); bpp = 4; break; + case PixelDataType::UINT_2_10_10_10_REV: + // Special case, format must be RGBA and uses 4 bytes + assert(format == PixelDataFormat::RGBA); + bpp = 4; + break; } size_t bpr = bpp * stride; diff --git a/filament/backend/src/CommandStream.cpp b/filament/backend/src/CommandStream.cpp index 7697e809c1..9b11772196 100644 --- a/filament/backend/src/CommandStream.cpp +++ b/filament/backend/src/CommandStream.cpp @@ -302,6 +302,8 @@ io::ostream& operator<<(io::ostream& out, PixelDataType format) { CASE(PixelDataType, FLOAT) CASE(PixelDataType, COMPRESSED) CASE(PixelDataType, UINT_10F_11F_11F_REV) + CASE(PixelDataType, USHORT_565) + CASE(PixelDataType, UINT_2_10_10_10_REV) } return out; } diff --git a/filament/backend/src/opengl/GLUtils.h b/filament/backend/src/opengl/GLUtils.h index 5e05f7deed..0716ebd29b 100644 --- a/filament/backend/src/opengl/GLUtils.h +++ b/filament/backend/src/opengl/GLUtils.h @@ -270,6 +270,7 @@ constexpr inline GLenum getType(backend::PixelDataType type) noexcept { case PixelDataType::FLOAT: return GL_FLOAT; case PixelDataType::UINT_10F_11F_11F_REV: return GL_UNSIGNED_INT_10F_11F_11F_REV; case PixelDataType::USHORT_565: return GL_UNSIGNED_SHORT_5_6_5; + case PixelDataType::UINT_2_10_10_10_REV: return GL_UNSIGNED_INT_2_10_10_10_REV; case PixelDataType::COMPRESSED: return 0; // should never happen } } diff --git a/filament/backend/src/vulkan/VulkanUtility.cpp b/filament/backend/src/vulkan/VulkanUtility.cpp index 7a82582ac8..2a4d27dfc6 100644 --- a/filament/backend/src/vulkan/VulkanUtility.cpp +++ b/filament/backend/src/vulkan/VulkanUtility.cpp @@ -140,7 +140,7 @@ VkFormat getVkFormat(TextureFormat format) { case TextureFormat::RGBA8: return VK_FORMAT_R8G8B8A8_UNORM; case TextureFormat::SRGB8_A8: return VK_FORMAT_R8G8B8A8_SRGB; case TextureFormat::RGBA8_SNORM: return VK_FORMAT_R8G8B8A8_SNORM; - case TextureFormat::RGB10_A2: return VK_FORMAT_A2R10G10B10_UNORM_PACK32; + case TextureFormat::RGB10_A2: return VK_FORMAT_A2B10G10R10_UNORM_PACK32; case TextureFormat::RGBA8UI: return VK_FORMAT_R8G8B8A8_UINT; case TextureFormat::RGBA8I: return VK_FORMAT_R8G8B8A8_SINT; case TextureFormat::DEPTH32F: return VK_FORMAT_D32_SFLOAT; diff --git a/filament/src/ColorGrading.cpp b/filament/src/ColorGrading.cpp index 38b8034af2..51208c3ab6 100644 --- a/filament/src/ColorGrading.cpp +++ b/filament/src/ColorGrading.cpp @@ -33,6 +33,8 @@ #include +#include + namespace filament { using namespace utils; @@ -368,17 +370,35 @@ size_t selectLutDimension(ColorGrading::QualityLevel quality) { } } -TextureFormat selectLutTextureFormat(ColorGrading::QualityLevel quality) { +void selectLutTextureParams(ColorGrading::QualityLevel quality, + TextureFormat& internalFormat, PixelDataFormat& format, PixelDataType& type) { // We use RGBA16F for high quality modes instead of RGB16F because RGB16F // is not supported everywhere switch (quality) { - case ColorGrading::QualityLevel::LOW: return TextureFormat::RGB10_A2; - case ColorGrading::QualityLevel::MEDIUM: return TextureFormat::RGB10_A2; - case ColorGrading::QualityLevel::HIGH: return TextureFormat::RGBA16F; - case ColorGrading::QualityLevel::ULTRA: return TextureFormat::RGBA16F; + case ColorGrading::QualityLevel::LOW: + internalFormat = TextureFormat::RGB10_A2; + format = PixelDataFormat::RGBA; + type = PixelDataType::UINT_2_10_10_10_REV; + break; + case ColorGrading::QualityLevel::MEDIUM: + internalFormat = TextureFormat::RGB10_A2; + format = PixelDataFormat::RGBA; + type = PixelDataType::UINT_2_10_10_10_REV; + break; + case ColorGrading::QualityLevel::HIGH: + internalFormat = TextureFormat::RGBA16F; + format = PixelDataFormat::RGBA; + type = PixelDataType::HALF; + break; + case ColorGrading::QualityLevel::ULTRA: + internalFormat = TextureFormat::RGBA16F; + format = PixelDataFormat::RGBA; + type = PixelDataType::HALF; + break; } } + //------------------------------------------------------------------------------ // Tone mapping //------------------------------------------------------------------------------ @@ -433,8 +453,8 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) { size_t lutElementCount = config.lutDimension * config.lutDimension * config.lutDimension; - constexpr size_t elementSize = sizeof(half4); - void* const data = malloc(lutElementCount * elementSize); + size_t elementSize = sizeof(half4); + void* data = malloc(lutElementCount * elementSize); //auto now = std::chrono::steady_clock::now(); @@ -508,6 +528,8 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) { // TODO: allow to customize the output color space, v = config.colorGradingTransformOut * v; + v = saturate(v); + // Apply OECF v = OECF_sRGB(v); @@ -525,17 +547,41 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) { //std::chrono::duration duration = std::chrono::steady_clock::now() - now; //slog.d << "LUT generation time: " << duration.count() << " ms" << io::endl; - TextureFormat textureFormat = selectLutTextureFormat(builder->quality); + TextureFormat textureFormat; + PixelDataFormat format; + PixelDataType type; + selectLutTextureParams(builder->quality, textureFormat, format, type); mLutHandle = driver.createTexture(SamplerType::SAMPLER_3D, 1, textureFormat, 0, config.lutDimension, config.lutDimension, config.lutDimension, TextureUsage::DEFAULT); + // convert input to UINT_2_10_10_10_REV if needed + if (type == PixelDataType::UINT_2_10_10_10_REV) { + uint32_t* const UTILS_RESTRICT dst = (uint32_t*)malloc(lutElementCount * sizeof(uint32_t)); + half4 const* UTILS_RESTRICT src = (half4*)data; + + // we use a vectorize width of 8 because, on ARMv8 it allows the compiler to write eight + // 32-bits results in one go. + const size_t count = lutElementCount & ~0x7; // tell the compiler that we're a multiple of 8 + #pragma clang loop vectorize_width(8) + for (size_t i = 0; i < count; ++i) { + float4 v{ src[i] }; + uint32_t r = uint32_t(floorf(v.x * 1023.0f + 0.5f)); + uint32_t g = uint32_t(floorf(v.y * 1023.0f + 0.5f)); + uint32_t b = uint32_t(floorf(v.z * 1023.0f + 0.5f)); + dst[i] = (b << 20u) | (g << 10u) | r; + } + + free(data); + data = (void*)dst; + elementSize = sizeof(uint32_t); + } + driver.update3DImage(mLutHandle, 0, 0, 0, 0, config.lutDimension, config.lutDimension, config.lutDimension, PixelBufferDescriptor{ - data, lutElementCount * elementSize, - PixelDataFormat::RGBA, PixelDataType::HALF, + data, lutElementCount * elementSize,format, type, [](void* buffer, size_t, void*) { free(buffer); } } );