From 28f9c606e4c8e61015e864219c4bc83a3fdb4a86 Mon Sep 17 00:00:00 2001 From: jvanverth Date: Fri, 5 Dec 2014 13:06:35 -0800 Subject: [PATCH] Add support for half float alpha textures. This allows us to create distance field textures with better precision, which may help text quality. BUG=skia:3103 Review URL: https://codereview.chromium.org/762923003 --- include/gpu/GrColor.h | 2 + include/gpu/GrTypes.h | 11 +++++- src/core/SkHalf.h | 4 ++ src/gpu/GrDrawTarget.cpp | 4 +- src/gpu/gl/GrGLCaps.cpp | 30 ++++++++++++++- src/gpu/gl/GrGLDefines.h | 4 ++ src/gpu/gl/GrGpuGL.cpp | 14 +++++++ tests/FloatingPointTextureTest.cpp | 59 +++++++++++++++++++++++++++++- 8 files changed, 124 insertions(+), 4 deletions(-) diff --git a/include/gpu/GrColor.h b/include/gpu/GrColor.h index f862ecc1d0..98bdb22840 100644 --- a/include/gpu/GrColor.h +++ b/include/gpu/GrColor.h @@ -168,6 +168,7 @@ static inline uint32_t GrPixelConfigComponentMask(GrPixelConfig config) { kA_GrColorComponentFlag, // kR11_EAC_GrPixelConfig kRGBA_GrColorComponentFlags, // kASTC_12x12_GrPixelConfig kRGBA_GrColorComponentFlags, // kRGBA_float_GrPixelConfig + kA_GrColorComponentFlag, // kAlpha_16_GrPixelConfig }; return kFlags[config]; @@ -183,6 +184,7 @@ static inline uint32_t GrPixelConfigComponentMask(GrPixelConfig config) { GR_STATIC_ASSERT(9 == kR11_EAC_GrPixelConfig); GR_STATIC_ASSERT(10 == kASTC_12x12_GrPixelConfig); GR_STATIC_ASSERT(11 == kRGBA_float_GrPixelConfig); + GR_STATIC_ASSERT(12 == kAlpha_half_GrPixelConfig); GR_STATIC_ASSERT(SK_ARRAY_COUNT(kFlags) == kGrPixelConfigCnt); } diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h index 0ef964dcc4..1ed7070d44 100644 --- a/include/gpu/GrTypes.h +++ b/include/gpu/GrTypes.h @@ -302,7 +302,13 @@ enum GrPixelConfig { * Byte order is r, g, b, a. This color format is 32 bits per channel */ kRGBA_float_GrPixelConfig, - kLast_GrPixelConfig = kRGBA_float_GrPixelConfig + + /** + * This color format is a single 16 bit float channel + */ + kAlpha_half_GrPixelConfig, + + kLast_GrPixelConfig = kAlpha_half_GrPixelConfig }; static const int kGrPixelConfigCnt = kLast_GrPixelConfig + 1; @@ -364,6 +370,7 @@ static inline size_t GrBytesPerPixel(GrPixelConfig config) { return 1; case kRGB_565_GrPixelConfig: case kRGBA_4444_GrPixelConfig: + case kAlpha_half_GrPixelConfig: return 2; case kRGBA_8888_GrPixelConfig: case kBGRA_8888_GrPixelConfig: @@ -382,6 +389,7 @@ static inline size_t GrUnpackAlignment(GrPixelConfig config) { return 1; case kRGB_565_GrPixelConfig: case kRGBA_4444_GrPixelConfig: + case kAlpha_half_GrPixelConfig: return 2; case kRGBA_8888_GrPixelConfig: case kBGRA_8888_GrPixelConfig: @@ -408,6 +416,7 @@ static inline bool GrPixelConfigIsAlphaOnly(GrPixelConfig config) { case kLATC_GrPixelConfig: case kASTC_12x12_GrPixelConfig: case kAlpha_8_GrPixelConfig: + case kAlpha_half_GrPixelConfig: return true; default: return false; diff --git a/src/core/SkHalf.h b/src/core/SkHalf.h index 3fd07793e9..7e41c6ff0c 100644 --- a/src/core/SkHalf.h +++ b/src/core/SkHalf.h @@ -15,6 +15,10 @@ // only used for storage typedef uint16_t SkHalf; +#define SK_HalfMin 0x0400 // 2^-24 (minimum positive normal value) +#define SK_HalfMax 0x7bff // 65504 +#define SK_HalfEpsilon 0x1400 // 2^-10 + // convert between half and single precision floating point float SkHalfToFloat(SkHalf h); SkHalf SkFloatToHalf(float f); diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index 7728ea5497..65764b88ce 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -1104,7 +1104,8 @@ SkString GrDrawTargetCaps::dump() const { "LATC", // kLATC_GrPixelConfig, "R11EAC", // kR11_EAC_GrPixelConfig, "ASTC12x12",// kASTC_12x12_GrPixelConfig, - "RGBAFloat", // kRGBA_float_GrPixelConfig + "RGBAFloat",// kRGBA_float_GrPixelConfig + "AlphaHalf",// kAlpha_half_GrPixelConfig }; GR_STATIC_ASSERT(0 == kUnknown_GrPixelConfig); GR_STATIC_ASSERT(1 == kAlpha_8_GrPixelConfig); @@ -1118,6 +1119,7 @@ SkString GrDrawTargetCaps::dump() const { GR_STATIC_ASSERT(9 == kR11_EAC_GrPixelConfig); GR_STATIC_ASSERT(10 == kASTC_12x12_GrPixelConfig); GR_STATIC_ASSERT(11 == kRGBA_float_GrPixelConfig); + GR_STATIC_ASSERT(12 == kAlpha_half_GrPixelConfig); GR_STATIC_ASSERT(SK_ARRAY_COUNT(kConfigNames) == kGrPixelConfigCnt); SkASSERT(!fConfigRenderSupport[kUnknown_GrPixelConfig][0]); diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index b4ee4a640d..cf442a0430 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -390,7 +390,6 @@ bool GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) { } void GrGLCaps::initConfigRenderableTable(const GrGLContextInfo& ctxInfo) { - // OpenGL < 3.0 // no support for render targets unless the GL_ARB_framebuffer_object // extension is supported (in which case we get ALPHA, RED, RG, RGB, @@ -470,8 +469,24 @@ void GrGLCaps::initConfigRenderableTable(const GrGLContextInfo& ctxInfo) { if (this->isConfigTexturable(kRGBA_float_GrPixelConfig)) { fConfigRenderSupport[kRGBA_float_GrPixelConfig][kNo_MSAA] = true; + if (kGL_GrGLStandard == standard) { + fConfigRenderSupport[kRGBA_float_GrPixelConfig][kYes_MSAA] = true; + } else { + // for now we don't support float point MSAA on ES + fConfigRenderSupport[kAlpha_half_GrPixelConfig][kYes_MSAA] = false; + } } + if (this->isConfigTexturable(kAlpha_half_GrPixelConfig)) { + fConfigRenderSupport[kAlpha_half_GrPixelConfig][kNo_MSAA] = true; + if (kGL_GrGLStandard == standard) { + fConfigRenderSupport[kAlpha_half_GrPixelConfig][kYes_MSAA] = true; + } else { + // for now we don't support float point MSAA on ES + fConfigRenderSupport[kAlpha_half_GrPixelConfig][kYes_MSAA] = false; + } + } + // If we don't support MSAA then undo any places above where we set a config as renderable with // msaa. if (kNone_MSFBOType == fMSFBOType) { @@ -607,6 +622,19 @@ void GrGLCaps::initConfigTexturableTable(const GrGLContextInfo& ctxInfo, const G ctxInfo.hasExtension("GL_OES_texture_float")); } fConfigTextureSupport[kRGBA_float_GrPixelConfig] = hasFPTextures; + + // Check for fp16 texture support + // NOTE: We disallow floating point textures on ES devices if linear + // filtering modes are not supported. This is for simplicity, but a more + // granular approach is possible. Coincidentally, 16-bit floating point textures became part of + // the standard in ES3.1 / OGL 3.1, hence the shorthand + bool hasHalfFPTextures = version >= GR_GL_VER(3, 1); + if (!hasHalfFPTextures) { + hasHalfFPTextures = ctxInfo.hasExtension("GL_ARB_texture_float") || + (ctxInfo.hasExtension("OES_texture_half_float_linear") && + ctxInfo.hasExtension("GL_OES_texture_half_float")); + } + fConfigTextureSupport[kAlpha_half_GrPixelConfig] = hasHalfFPTextures && fTextureRedSupport; } bool GrGLCaps::doReadPixelsSupported(const GrGLInterface* intf, diff --git a/src/gpu/gl/GrGLDefines.h b/src/gpu/gl/GrGLDefines.h index 68d8da8030..dc9468fd1e 100644 --- a/src/gpu/gl/GrGLDefines.h +++ b/src/gpu/gl/GrGLDefines.h @@ -302,7 +302,9 @@ #define GR_GL_INT 0x1404 #define GR_GL_UNSIGNED_INT 0x1405 #define GR_GL_FLOAT 0x1406 +#define GR_GL_HALF_FLOAT 0x140B #define GR_GL_FIXED 0x140C +#define GR_GL_HALF_FLOAT_OES 0x8D61 /* Lighting */ #define GR_GL_LIGHTING 0x0B50 @@ -379,6 +381,8 @@ #define GR_GL_ALPHA8 0x803C #define GR_GL_R8 0x8229 +#define GR_GL_R16F 0x822D +#define GR_GL_ALPHA16F 0x881C /* PixelType */ /* GL_UNSIGNED_BYTE */ diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp index 383427d9c2..56eeb47efc 100644 --- a/src/gpu/gl/GrGpuGL.cpp +++ b/src/gpu/gl/GrGpuGL.cpp @@ -2245,6 +2245,20 @@ bool GrGpuGL::configToGLFormats(GrPixelConfig config, *externalType = GR_GL_FLOAT; break; + case kAlpha_half_GrPixelConfig: + if (this->glCaps().textureRedSupport()) { + *internalFormat = GR_GL_R16F; + *externalFormat = GR_GL_RED; + *externalType = (kGLES_GrGLStandard == this->glStandard()) ? GR_GL_HALF_FLOAT + : GR_GL_HALF_FLOAT_OES; + } else { + *internalFormat = GR_GL_ALPHA16F; + *externalFormat = GR_GL_ALPHA; + *externalType = (kGLES_GrGLStandard == this->glStandard()) ? GR_GL_HALF_FLOAT + : GR_GL_HALF_FLOAT_OES; + } + break; + default: return false; } diff --git a/tests/FloatingPointTextureTest.cpp b/tests/FloatingPointTextureTest.cpp index e058c1139c..d6074b92fd 100644 --- a/tests/FloatingPointTextureTest.cpp +++ b/tests/FloatingPointTextureTest.cpp @@ -17,10 +17,12 @@ #include "GrContext.h" #include "GrTexture.h" #include "GrContextFactory.h" + #include "SkGpuDevice.h" +#include "SkHalf.h" static const int DEV_W = 100, DEV_H = 100; -static const int FP_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * sizeof(float); +static const int FP_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * 4; static const float kMaxIntegerRepresentableInSPFloatingPoint = 16777216; // 2 ^ 24 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H); @@ -77,4 +79,59 @@ DEF_GPUTEST(FloatingPointTextureTest, reporter, factory) { } } +static const int HALF_CONTROL_ARRAY_SIZE = DEV_W * DEV_H; + +DEF_GPUTEST(HalfFloatTextureTest, reporter, factory) { + SkHalf controlPixelData[HALF_CONTROL_ARRAY_SIZE]; + SkHalf readBuffer[HALF_CONTROL_ARRAY_SIZE]; + for (int i = 0; i < HALF_CONTROL_ARRAY_SIZE; i += 4) { + controlPixelData[i] = SK_HalfMin; + controlPixelData[i + 1] = SK_HalfMax; + controlPixelData[i + 2] = SK_HalfEpsilon; + controlPixelData[i + 3] = 0x6800; // 2^11 + } + + for (int origin = 0; origin < 2; ++origin) { + int glCtxTypeCnt = 1; + glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt; + for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) { + GrSurfaceDesc desc; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = DEV_W; + desc.fHeight = DEV_H; + desc.fConfig = kAlpha_half_GrPixelConfig; + desc.fOrigin = 0 == origin ? + kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin; + + GrContext* context = NULL; + GrContextFactory::GLContextType type = + static_cast(glCtxType); + if (!GrContextFactory::IsRenderingGLContext(type)) { + continue; + } + context = factory->get(type); + if (NULL == context){ + continue; + } + + SkAutoTUnref fpTexture(context->createUncachedTexture(desc, + NULL, + 0)); + + // 16-bit floating point textures are NOT supported everywhere + if (NULL == fpTexture) { + continue; + } + + // write square + fpTexture->writePixels(0, 0, DEV_W, DEV_H, desc.fConfig, controlPixelData, 0); + fpTexture->readPixels(0, 0, DEV_W, DEV_H, desc.fConfig, readBuffer, 0); + for (int j = 0; j < HALF_CONTROL_ARRAY_SIZE; ++j) { + SkASSERT(readBuffer[j] == controlPixelData[j]); + REPORTER_ASSERT(reporter, readBuffer[j] == controlPixelData[j]); + } + } + } +} + #endif