diff --git a/src/Image/Image_Format.hxx b/src/Image/Image_Format.hxx index 28bfcf4d69..6c94daa3da 100644 --- a/src/Image/Image_Format.hxx +++ b/src/Image/Image_Format.hxx @@ -33,7 +33,9 @@ enum Image_Format Image_Format_BGRF, //!< same as RGBF but with different components order Image_Format_RGBAF, //!< 4 floats (16-bytes) RGBA image plane Image_Format_BGRAF, //!< same as RGBAF but with different components order + Image_Format_RGF_half, //!< 2 half-floats (4-bytes) RG image plane + Image_Format_RGBAF_half, //!< 4 half-floats (8-bytes) RGBA image plane }; -enum { Image_Format_NB = Image_Format_BGRAF + 1 }; +enum { Image_Format_NB = Image_Format_RGBAF_half + 1 }; #endif // _Image_Format_HeaderFile diff --git a/src/Image/Image_PixMap.cxx b/src/Image/Image_PixMap.cxx index 15d2fad63f..02d7bdad7d 100644 --- a/src/Image/Image_PixMap.cxx +++ b/src/Image/Image_PixMap.cxx @@ -62,6 +62,8 @@ namespace ImageFormatInfo(BGRF, 3, sizeof(float) * 3), ImageFormatInfo(RGBAF, 4, sizeof(float) * 4), ImageFormatInfo(BGRAF, 4, sizeof(float) * 4), + ImageFormatInfo(RGF_half, 2, sizeof(uint16_t) * 2), + ImageFormatInfo(RGBAF_half, 4, sizeof(uint16_t) * 4), CompressedImageFormatInfo(RGB_S3TC_DXT1, 3, 1), // DXT1 uses circa half a byte per pixel (64 bits per 4x4 block) CompressedImageFormatInfo(RGBA_S3TC_DXT1, 4, 1), CompressedImageFormatInfo(RGBA_S3TC_DXT3, 4, 1), // DXT3/5 uses circa 1 byte per pixel (128 bits per 4x4 block) @@ -294,6 +296,17 @@ Quantity_ColorRGBA Image_PixMap::PixelColor (const Standard_Integer theX, const Image_ColorBGRF& aPixel = Value (theY, theX); return Quantity_ColorRGBA (NCollection_Vec4 (aPixel.r(), aPixel.g(), aPixel.b(), 1.0f)); // opaque } + case Image_Format_RGF_half: + { + const NCollection_Vec2& aPixel = Value> (theY, theX); + return Quantity_ColorRGBA (NCollection_Vec4 (ConvertFromHalfFloat (aPixel.x()), ConvertFromHalfFloat (aPixel.y()), 0.0f, 1.0f)); + } + case Image_Format_RGBAF_half: + { + const NCollection_Vec4& aPixel = Value> (theY, theX); + return Quantity_ColorRGBA (NCollection_Vec4 (ConvertFromHalfFloat (aPixel.r()), ConvertFromHalfFloat (aPixel.g()), + ConvertFromHalfFloat (aPixel.b()), ConvertFromHalfFloat (aPixel.a()))); + } case Image_Format_RGBA: { const Image_ColorRGBA& aPixel = Value (theY, theX); @@ -440,6 +453,22 @@ void Image_PixMap::SetPixelColor (const Standard_Integer theX, aPixel.b() = aColor.b(); return; } + case Image_Format_RGF_half: + { + NCollection_Vec2& aPixel = ChangeValue> (theY, theX); + aPixel.x() = ConvertToHalfFloat (aColor.r()); + aPixel.y() = ConvertToHalfFloat (aColor.g()); + return; + } + case Image_Format_RGBAF_half: + { + NCollection_Vec4& aPixel = ChangeValue> (theY, theX); + aPixel.r() = ConvertToHalfFloat (aColor.r()); + aPixel.g() = ConvertToHalfFloat (aColor.g()); + aPixel.b() = ConvertToHalfFloat (aColor.b()); + aPixel.a() = ConvertToHalfFloat (aColor.a()); + return; + } case Image_Format_RGBA: { Image_ColorRGBA& aPixel = ChangeValue (theY, theX); diff --git a/src/Image/Image_PixMap.hxx b/src/Image/Image_PixMap.hxx index a043f8894a..35e263ad54 100644 --- a/src/Image/Image_PixMap.hxx +++ b/src/Image/Image_PixMap.hxx @@ -303,6 +303,36 @@ public: //! @name low-level API for batch-processing (pixels reading / compariso return myData.ChangeValue (theRow, theCol); } +public: + + //! Convert 16-bit half-float value into 32-bit float (simple conversion). + static float ConvertFromHalfFloat (const uint16_t theHalf) + { + union FloatUint32 { float Float32; uint32_t UInt32; }; + + const uint32_t e = (theHalf & 0x7C00) >> 10; // exponent + const uint32_t m = (theHalf & 0x03FF) << 13; // mantissa + FloatUint32 mf, aRes; + mf.Float32 = (float )m; + const uint32_t v = mf.UInt32 >> 23; // evil log2 bit hack to count leading zeros in denormalized format + aRes.UInt32 = (theHalf & 0x8000)<<16 | (e != 0) * ((e + 112) << 23 | m) | ((e == 0) & (m != 0)) * ((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000)); // sign : normalized : denormalized + return aRes.Float32; + } + + //! Convert 32-bit float value into IEEE-754 16-bit floating-point format without infinity: + //! 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits. + static uint16_t ConvertToHalfFloat (const float theFloat) + { + union FloatUint32 { float Float32; uint32_t UInt32; }; + FloatUint32 anInput; + anInput.Float32 = theFloat; + const uint32_t b = anInput.UInt32 + 0x00001000; // round-to-nearest-even: add last bit after truncated mantissa + const uint32_t e = (b & 0x7F800000) >> 23; // exponent + const uint32_t m = b & 0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding + return (uint16_t)((b & 0x80000000) >> 16 | (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13) + | ((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143) * 0x7FFF); // sign : normalized : denormalized : saturate + } + protected: Image_PixMapData myData; //!< data buffer diff --git a/src/Media/Media_Frame.cxx b/src/Media/Media_Frame.cxx index f0ad43e27c..94d8843c52 100644 --- a/src/Media/Media_Frame.cxx +++ b/src/Media/Media_Frame.cxx @@ -97,6 +97,8 @@ int Media_Frame::FormatOcct2FFmpeg (Image_Format theFormat) case Image_Format_RGBF: case Image_Format_BGRAF: case Image_Format_BGRF: + case Image_Format_RGF_half: + case Image_Format_RGBAF_half: case Image_Format_UNKNOWN: return AV_PIX_FMT_NONE; // unsupported } diff --git a/src/OpenGl/OpenGl_Context.cxx b/src/OpenGl/OpenGl_Context.cxx index 6147eaf0e7..79e87cc18a 100644 --- a/src/OpenGl/OpenGl_Context.cxx +++ b/src/OpenGl/OpenGl_Context.cxx @@ -3389,9 +3389,17 @@ void OpenGl_Context::init (const Standard_Boolean theIsCoreProfile) mySupportedFormats->Add (Image_Format_AlphaF); mySupportedFormats->Add (Image_Format_RGBF); mySupportedFormats->Add (Image_Format_RGBAF); + if (hasHalfFloatBuffer) + { + mySupportedFormats->Add (Image_Format_RGBAF_half); + } if (arbTexRG) { mySupportedFormats->Add (Image_Format_RGF); + if (hasHalfFloatBuffer) + { + mySupportedFormats->Add (Image_Format_RGF_half); + } } if (extBgra) { @@ -3442,7 +3450,8 @@ void OpenGl_Context::init (const Standard_Boolean theIsCoreProfile) && arbTexFloat && (IsGlGreaterEqual (3, 0) #if defined(GL_ES_VERSION_2_0) - || true // || CheckExtension ("GL_EXT_shader_texture_lod") fallback is used when extension is unavailable + || hasHighp + // || CheckExtension ("GL_EXT_shader_texture_lod") fallback is used when extension is unavailable #else || (IsGlGreaterEqual (2, 1) && CheckExtension ("GL_EXT_gpu_shader4")) #endif diff --git a/src/OpenGl/OpenGl_FrameBuffer.cxx b/src/OpenGl/OpenGl_FrameBuffer.cxx index 0246700a94..50d47ef364 100644 --- a/src/OpenGl/OpenGl_FrameBuffer.cxx +++ b/src/OpenGl/OpenGl_FrameBuffer.cxx @@ -922,9 +922,14 @@ Standard_Boolean OpenGl_FrameBuffer::BufferDump (const Handle(OpenGl_Context)& t aFormat = GL_RGBA; aType = GL_FLOAT; break; + case Image_Format_RGBAF_half: + aFormat = GL_RGBA; + aType = GL_HALF_FLOAT; + break; case Image_Format_Alpha: case Image_Format_AlphaF: return Standard_False; // GL_ALPHA is no more supported in core context + case Image_Format_RGF_half: case Image_Format_UNKNOWN: return Standard_False; } diff --git a/src/OpenGl/OpenGl_TextureFormat.cxx b/src/OpenGl/OpenGl_TextureFormat.cxx index ec07a2c2d2..be055ae72d 100644 --- a/src/OpenGl/OpenGl_TextureFormat.cxx +++ b/src/OpenGl/OpenGl_TextureFormat.cxx @@ -217,6 +217,34 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindFormat (const Handle(OpenGl_Conte return OpenGl_TextureFormat(); #endif } + case Image_Format_RGF_half: + { + aFormat.SetNbComponents (2); + aFormat.SetInternalFormat (GL_RG16F); + aFormat.SetPixelFormat (GL_RG); + aFormat.SetDataType (GL_HALF_FLOAT); + if (theCtx->hasHalfFloatBuffer == OpenGl_FeatureInExtensions) + { + #if defined(GL_ES_VERSION_2_0) + aFormat.SetDataType (GL_HALF_FLOAT_OES); + #endif + } + return aFormat; + } + case Image_Format_RGBAF_half: + { + aFormat.SetNbComponents (4); + aFormat.SetInternalFormat (GL_RGBA16F); + aFormat.SetPixelFormat (GL_RGBA); + aFormat.SetDataType (GL_HALF_FLOAT); + if (theCtx->hasHalfFloatBuffer == OpenGl_FeatureInExtensions) + { + #if defined(GL_ES_VERSION_2_0) + aFormat.SetDataType (GL_HALF_FLOAT_OES); + #endif + } + return aFormat; + } case Image_Format_RGBA: { aFormat.SetNbComponents (4); @@ -442,7 +470,7 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_ aFormat.SetInternalFormat (theSizedFormat); aFormat.SetPixelFormat (GL_RGBA); aFormat.SetDataType (GL_HALF_FLOAT); - aFormat.SetImageFormat (Image_Format_RGBAF); + aFormat.SetImageFormat (Image_Format_RGBAF_half); if (theCtx->hasHalfFloatBuffer == OpenGl_FeatureInExtensions) { #if defined(GL_ES_VERSION_2_0) @@ -470,6 +498,23 @@ OpenGl_TextureFormat OpenGl_TextureFormat::FindSizedFormat (const Handle(OpenGl_ } return aFormat; } + case GL_RG16F: + { + aFormat.SetNbComponents (2); + aFormat.SetInternalFormat (theSizedFormat); + aFormat.SetPixelFormat (GL_RG); + aFormat.SetDataType (GL_HALF_FLOAT); + aFormat.SetImageFormat (Image_Format_RGF_half); + if (theCtx->hasHalfFloatBuffer == OpenGl_FeatureInExtensions) + { + #if defined(GL_ES_VERSION_2_0) + aFormat.SetDataType (GL_HALF_FLOAT_OES); + #else + aFormat.SetDataType (GL_FLOAT); + #endif + } + return aFormat; + } case GL_SRGB8_ALPHA8: case GL_RGBA8: case GL_RGBA: diff --git a/src/OpenGl/OpenGl_View.cxx b/src/OpenGl/OpenGl_View.cxx index 4d58b9eb7b..57af07f5b1 100644 --- a/src/OpenGl/OpenGl_View.cxx +++ b/src/OpenGl/OpenGl_View.cxx @@ -1188,41 +1188,70 @@ bool OpenGl_View::prepareFrameBuffers (Graphic3d_Camera::Projection& theProj) static const TCollection_AsciiString THE_SHARED_ENV_LUT_KEY("EnvLUT"); if (!aCtx->GetResource (THE_SHARED_ENV_LUT_KEY, anEnvLUT)) { - Handle(Graphic3d_TextureParams) aParams = new Graphic3d_TextureParams(); - aParams->SetFilter (Graphic3d_TOTF_BILINEAR); - aParams->SetRepeat (Standard_False); - aParams->SetTextureUnit (aCtx->PBREnvLUTTexUnit()); - anEnvLUT = new OpenGl_Texture(THE_SHARED_ENV_LUT_KEY, aParams); - Handle(Image_PixMap) aPixMap = new Image_PixMap(); + bool toConvertHalfFloat = false; + #if defined(GL_ES_VERSION_2_0) + // GL_RG32F is not texture-filterable format in OpenGL ES without OES_texture_float_linear extension. + // GL_RG16F is texture-filterable since OpenGL ES 3.0 or OpenGL ES 2.0 + OES_texture_half_float_linear. + // OpenGL ES 3.0 allows initialization of GL_RG16F from 32-bit float data, but OpenGL ES 2.0 + OES_texture_half_float does not. + // Note that it is expected that GL_RG16F has enough precision for this table, so that it can be used also on desktop OpenGL. + const bool hasHalfFloat = aCtx->IsGlGreaterEqual (3, 0) || aCtx->CheckExtension ("GL_OES_texture_half_float_linear"); + toConvertHalfFloat = !aCtx->IsGlGreaterEqual (3, 0) && hasHalfFloat; + #endif + Image_Format anImgFormat = Image_Format_UNKNOWN; if (aCtx->arbTexRG) + { + anImgFormat = toConvertHalfFloat ? Image_Format_RGF_half : Image_Format_RGF; + } + else + { + anImgFormat = toConvertHalfFloat ? Image_Format_RGBAF_half : Image_Format_RGBAF; + } + + Handle(Image_PixMap) aPixMap = new Image_PixMap(); + if (anImgFormat == Image_Format_RGF) { aPixMap->InitWrapper (Image_Format_RGF, (Standard_Byte*)Textures_EnvLUT, Textures_EnvLUTSize, Textures_EnvLUTSize); } else { + aPixMap->InitZero (anImgFormat, Textures_EnvLUTSize, Textures_EnvLUTSize); Image_PixMap aPixMapRG; aPixMapRG.InitWrapper (Image_Format_RGF, (Standard_Byte*)Textures_EnvLUT, Textures_EnvLUTSize, Textures_EnvLUTSize); - aPixMap->InitZero (Image_Format_RGBAF, Textures_EnvLUTSize, Textures_EnvLUTSize); for (Standard_Size aRowIter = 0; aRowIter < aPixMapRG.SizeY(); ++aRowIter) { for (Standard_Size aColIter = 0; aColIter < aPixMapRG.SizeX(); ++aColIter) { const Image_ColorRGF& aPixelRG = aPixMapRG.Value (aRowIter, aColIter); - Image_ColorRGBAF& aPixelRGBA = aPixMap->ChangeValue (aRowIter, aColIter); - aPixelRGBA.r() = aPixelRG.r(); - aPixelRGBA.g() = aPixelRG.g(); + if (toConvertHalfFloat) + { + NCollection_Vec2& aPixelRGBA = aPixMap->ChangeValue> (aRowIter, aColIter); + aPixelRGBA.x() = Image_PixMap::ConvertToHalfFloat (aPixelRG.r()); + aPixelRGBA.y() = Image_PixMap::ConvertToHalfFloat (aPixelRG.g()); + } + else + { + Image_ColorRGBAF& aPixelRGBA = aPixMap->ChangeValue (aRowIter, aColIter); + aPixelRGBA.r() = aPixelRG.r(); + aPixelRGBA.g() = aPixelRG.g(); + } } } } OpenGl_TextureFormat aTexFormat = OpenGl_TextureFormat::FindFormat (aCtx, aPixMap->Format(), false); #if defined(GL_ES_VERSION_2_0) - // GL_RG32F is not texture-filterable format on OpenGL ES without OES_texture_float_linear extension. - // GL_RG16F is texture-filterable since OpenGL ES 3.0 and can be initialized from 32-bit floats. - // Note that it is expected that GL_RG16F has enough precision for this table, so that it can be used also on desktop OpenGL. - //if (!aCtx->hasTexFloatLinear) - aTexFormat.SetInternalFormat (aCtx->arbTexRG ? GL_RG16F : GL_RGBA16F); + if (aTexFormat.IsValid() + && hasHalfFloat) + { + aTexFormat.SetInternalFormat (aCtx->arbTexRG ? GL_RG16F : GL_RGBA16F); + } #endif + + Handle(Graphic3d_TextureParams) aParams = new Graphic3d_TextureParams(); + aParams->SetFilter (Graphic3d_TOTF_BILINEAR); + aParams->SetRepeat (Standard_False); + aParams->SetTextureUnit (aCtx->PBREnvLUTTexUnit()); + anEnvLUT = new OpenGl_Texture(THE_SHARED_ENV_LUT_KEY, aParams); if (!aTexFormat.IsValid() || !anEnvLUT->Init (aCtx, aTexFormat, Graphic3d_Vec2i((Standard_Integer)Textures_EnvLUTSize), Graphic3d_TOT_2D, aPixMap.get())) {