diff options
Diffstat (limited to 'libs/hwui')
52 files changed, 438 insertions, 862 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 81a183126cce..2d9b764f5504 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -45,7 +45,6 @@ hwui_src_files := \ AnimationContext.cpp \ Animator.cpp \ AnimatorManager.cpp \ - AssetAtlas.cpp \ BakedOpDispatcher.cpp \ BakedOpRenderer.cpp \ BakedOpState.cpp \ @@ -56,7 +55,6 @@ hwui_src_files := \ DeferredLayerUpdater.cpp \ DeviceInfo.cpp \ DisplayList.cpp \ - Dither.cpp \ Extensions.cpp \ FboCache.cpp \ FontRenderer.cpp \ @@ -130,6 +128,14 @@ ifeq ($(TARGET_USES_HWC2),true) hwui_cflags += -DUSE_HWC2 endif +# TODO: Linear blending should be enabled by default, but we are +# TODO: making it an opt-in while it's a work in progress +# TODO: The final test should be: +# TODO: ifneq ($(TARGET_ENABLE_LINEAR_BLENDING),false) +ifeq ($(TARGET_ENABLE_LINEAR_BLENDING),true) + hwui_cflags += -DANDROID_ENABLE_LINEAR_BLENDING +endif + # GCC false-positives on this warning, and since we -Werror that's # a problem hwui_cflags += -Wno-free-nonheap-object @@ -143,7 +149,6 @@ ifeq (true, $(BUGREPORT_FONT_CACHE_USAGE)) hwui_cflags += -DBUGREPORT_FONT_CACHE_USAGE endif - ifndef HWUI_COMPILE_SYMBOLS hwui_cflags += -fvisibility=hidden endif diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp deleted file mode 100644 index e2e7037202b8..000000000000 --- a/libs/hwui/AssetAtlas.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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 "AssetAtlas.h" -#include "Caches.h" -#include "Image.h" - -#include <GLES2/gl2ext.h> - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Lifecycle -/////////////////////////////////////////////////////////////////////////////// - -void AssetAtlas::init(const sp<GraphicBuffer>& buffer, int64_t* map, int count) { - if (mImage) { - return; - } - - ATRACE_NAME("AssetAtlas::init"); - - mImage = new Image(buffer); - if (mImage->getTexture()) { - if (!mTexture) { - Caches& caches = Caches::getInstance(); - mTexture = new Texture(caches); - mTexture->wrap(mImage->getTexture(), - buffer->getWidth(), buffer->getHeight(), GL_RGBA); - createEntries(caches, map, count); - } - } else { - ALOGW("Could not create atlas image"); - terminate(); - } -} - -void AssetAtlas::terminate() { - delete mImage; - mImage = nullptr; - delete mTexture; - mTexture = nullptr; - mEntries.clear(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Entries -/////////////////////////////////////////////////////////////////////////////// - -AssetAtlas::Entry* AssetAtlas::getEntry(const SkPixelRef* pixelRef) const { - auto result = mEntries.find(pixelRef); - return result != mEntries.end() ? result->second.get() : nullptr; -} - -Texture* AssetAtlas::getEntryTexture(const SkPixelRef* pixelRef) const { - auto result = mEntries.find(pixelRef); - return result != mEntries.end() ? result->second->texture : nullptr; -} - -/** - * Delegates changes to wrapping and filtering to the base atlas texture - * instead of applying the changes to the virtual textures. - */ -struct DelegateTexture: public Texture { - DelegateTexture(Caches& caches, Texture* delegate) - : Texture(caches), mDelegate(delegate) { } - - virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, - bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override { - mDelegate->setWrapST(wrapS, wrapT, bindTexture, force, renderTarget); - } - - virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, - bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override { - mDelegate->setFilterMinMag(min, mag, bindTexture, force, renderTarget); - } - -private: - Texture* const mDelegate; -}; // struct DelegateTexture - -void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) { - const float width = float(mTexture->width()); - const float height = float(mTexture->height()); - - for (int i = 0; i < count; ) { - SkPixelRef* pixelRef = reinterpret_cast<SkPixelRef*>(map[i++]); - // NOTE: We're converting from 64 bit signed values to 32 bit - // signed values. This is guaranteed to be safe because the "x" - // and "y" coordinate values are guaranteed to be representable - // with 32 bits. The array is 64 bits wide so that it can carry - // pointers on 64 bit architectures. - const int x = static_cast<int>(map[i++]); - const int y = static_cast<int>(map[i++]); - - // Bitmaps should never be null, we're just extra paranoid - if (!pixelRef) continue; - - const UvMapper mapper( - x / width, (x + pixelRef->info().width()) / width, - y / height, (y + pixelRef->info().height()) / height); - - Texture* texture = new DelegateTexture(caches, mTexture); - texture->blend = !SkAlphaTypeIsOpaque(pixelRef->info().alphaType()); - texture->wrap(mTexture->id(), pixelRef->info().width(), - pixelRef->info().height(), mTexture->format()); - - std::unique_ptr<Entry> entry(new Entry(pixelRef, texture, mapper, *this)); - texture->uvMapper = &entry->uvMapper; - - mEntries.emplace(entry->pixelRef, std::move(entry)); - } -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h deleted file mode 100644 index b32e51851b94..000000000000 --- a/libs/hwui/AssetAtlas.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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. - */ - -#ifndef ANDROID_HWUI_ASSET_ATLAS_H -#define ANDROID_HWUI_ASSET_ATLAS_H - -#include "Texture.h" -#include "UvMapper.h" - -#include <cutils/compiler.h> -#include <GLES2/gl2.h> -#include <ui/GraphicBuffer.h> -#include <SkBitmap.h> - -#include <memory> -#include <unordered_map> - -namespace android { -namespace uirenderer { - -class Caches; -class Image; - -/** - * An asset atlas holds a collection of framework bitmaps in a single OpenGL - * texture. Each bitmap is associated with a location, defined in pixels, - * inside the atlas. The atlas is generated by the framework and bound as - * an external texture using the EGLImageKHR extension. - */ -class AssetAtlas { -public: - /** - * Entry representing the texture and uvMapper of a PixelRef in the - * atlas - */ - class Entry { - public: - /* - * A "virtual texture" object that represents the texture - * this entry belongs to. This texture should never be - * modified. - */ - Texture* texture; - - /** - * Maps texture coordinates in the [0..1] range into the - * correct range to sample this entry from the atlas. - */ - const UvMapper uvMapper; - - /** - * Unique identifier used to merge bitmaps and 9-patches stored - * in the atlas. - */ - const void* getMergeId() const { - return texture->blend ? &atlas.mBlendKey : &atlas.mOpaqueKey; - } - - ~Entry() { - delete texture; - } - - private: - /** - * The pixel ref that generated this atlas entry. - */ - SkPixelRef* pixelRef; - - /** - * Atlas this entry belongs to. - */ - const AssetAtlas& atlas; - - Entry(SkPixelRef* pixelRef, Texture* texture, const UvMapper& mapper, - const AssetAtlas& atlas) - : texture(texture) - , uvMapper(mapper) - , pixelRef(pixelRef) - , atlas(atlas) { - } - - friend class AssetAtlas; - }; - - AssetAtlas(): mTexture(nullptr), mImage(nullptr), - mBlendKey(true), mOpaqueKey(false) { } - ~AssetAtlas() { terminate(); } - - /** - * Initializes the atlas with the specified buffer and - * map. The buffer is a gralloc'd texture that will be - * used as an EGLImage. The map is a list of SkBitmap* - * and their (x, y) positions - * - * This method returns immediately if the atlas is already - * initialized. To re-initialize the atlas, you must - * first call terminate(). - */ - ANDROID_API void init(const sp<GraphicBuffer>& buffer, int64_t* map, int count); - - /** - * Destroys the atlas texture. This object can be - * re-initialized after calling this method. - * - * After calling this method, the width, height - * and texture are set to 0. - */ - void terminate(); - - /** - * Returns the width of this atlas in pixels. - * Can return 0 if the atlas is not initialized. - */ - uint32_t getWidth() const { - return mTexture ? mTexture->width() : 0; - } - - /** - * Returns the height of this atlas in pixels. - * Can return 0 if the atlas is not initialized. - */ - uint32_t getHeight() const { - return mTexture ? mTexture->height() : 0; - } - - /** - * Returns the OpenGL name of the texture backing this atlas. - * Can return 0 if the atlas is not initialized. - */ - GLuint getTexture() const { - return mTexture ? mTexture->id() : 0; - } - - /** - * Returns the entry in the atlas associated with the specified - * pixelRef. If the pixelRef is not in the atlas, return NULL. - */ - Entry* getEntry(const SkPixelRef* pixelRef) const; - - /** - * Returns the texture for the atlas entry associated with the - * specified pixelRef. If the pixelRef is not in the atlas, return NULL. - */ - Texture* getEntryTexture(const SkPixelRef* pixelRef) const; - -private: - void createEntries(Caches& caches, int64_t* map, int count); - - Texture* mTexture; - Image* mImage; - - const bool mBlendKey; - const bool mOpaqueKey; - - std::unordered_map<const SkPixelRef*, std::unique_ptr<Entry>> mEntries; -}; // class AssetAtlas - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_ASSET_ATLAS_H diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index 8b3f1722dddf..699503991e02 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -35,11 +35,11 @@ namespace android { namespace uirenderer { -static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds, const Rect& texCoord) { - vertices[0] = { bounds.left, bounds.top, texCoord.left, texCoord.top }; - vertices[1] = { bounds.right, bounds.top, texCoord.right, texCoord.top }; - vertices[2] = { bounds.left, bounds.bottom, texCoord.left, texCoord.bottom }; - vertices[3] = { bounds.right, bounds.bottom, texCoord.right, texCoord.bottom }; +static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds) { + vertices[0] = { bounds.left, bounds.top, 0, 0 }; + vertices[1] = { bounds.right, bounds.top, 1, 0 }; + vertices[2] = { bounds.left, bounds.bottom, 0, 1 }; + vertices[3] = { bounds.right, bounds.bottom, 1, 1 }; } void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, @@ -48,16 +48,11 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, const BakedOpState& firstState = *(opList.states[0]); const SkBitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap; - AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(bitmap->pixelRef()); - Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(bitmap); + Texture* texture = renderer.caches().textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); TextureVertex vertices[opList.count * 4]; - Rect texCoords(0, 0, 1, 1); - if (entry) { - entry->uvMapper.map(texCoords); - } for (size_t i = 0; i < opList.count; i++) { const BakedOpState& state = *(opList.states[i]); TextureVertex* rectVerts = &vertices[i * 4]; @@ -69,7 +64,7 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, // pure translate, so snap (same behavior as onBitmapOp) opBounds.snapToPixelBoundaries(); } - storeTexturedRect(rectVerts, opBounds, texCoords); + storeTexturedRect(rectVerts, opBounds); renderer.dirtyRenderTarget(opBounds); } @@ -92,8 +87,6 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, const MergedBakedOpList& opList) { const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op)); const BakedOpState& firstState = *(opList.states[0]); - AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry( - firstOp.bitmap->pixelRef()); // Batches will usually contain a small number of items so it's // worth performing a first iteration to count the exact number @@ -105,7 +98,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( - entry, op.bitmap->width(), op.bitmap->height(), + op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); totalVertices += opMesh->verticesCount; } @@ -126,7 +119,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, // TODO: cache mesh lookups const Patch* opMesh = renderer.caches().patchCache.get( - entry, op.bitmap->width(), op.bitmap->height(), + op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); @@ -172,7 +165,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, } - Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap); + Texture* texture = renderer.caches().textureCache.get(firstOp.bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); @@ -442,7 +435,12 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op } void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) { - const static UvMapper defaultUvMapper; + Texture* texture = renderer.caches().textureCache.get(op.bitmap); + if (!texture) { + return; + } + const AutoTexture autoCleanup(texture); + const uint32_t elementCount = op.meshWidth * op.meshHeight * 6; std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]); @@ -457,9 +455,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe colors = tempColors.get(); } - Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef()); - const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper); - for (int32_t y = 0; y < op.meshHeight; y++) { for (int32_t x = 0; x < op.meshWidth; x++) { uint32_t i = (y * (op.meshWidth + 1) + x) * 2; @@ -469,8 +464,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe float v1 = float(y) / op.meshHeight; float v2 = float(y + 1) / op.meshHeight; - mapper.map(u1, v1, u2, v2); - int ax = i + (op.meshWidth + 1) * 2; int ay = ax + 1; int bx = i; @@ -491,14 +484,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe } } - if (!texture) { - texture = renderer.caches().textureCache.get(op.bitmap); - if (!texture) { - return; - } - } - const AutoTexture autoCleanup(texture); - /* * TODO: handle alpha_8 textures correctly by applying paint color, but *not* * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh. @@ -599,12 +584,11 @@ void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, } // TODO: avoid redoing the below work each frame: - AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef()); const Patch* mesh = renderer.caches().patchCache.get( - entry, op.bitmap->width(), op.bitmap->height(), + op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch); - Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap); + Texture* texture = renderer.caches().textureCache.get(op.bitmap); if (CC_LIKELY(texture)) { const AutoTexture autoCleanup(texture); Glop glop; diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index 6db345ac0006..ac7a600af85f 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -182,11 +182,7 @@ void BakedOpRenderer::clearColorBuffer(const Rect& rect) { } Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) { - Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap->pixelRef()); - if (!texture) { - return mCaches.textureCache.get(bitmap); - } - return texture; + return mCaches.textureCache.get(bitmap); } void BakedOpRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 741cdccaa778..b463e45fb39b 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -53,7 +53,6 @@ Caches::Caches(RenderState& renderState) : gradientCache(mExtensions) , patchCache(renderState) , programCache(mExtensions) - , dither(*this) , mRenderState(&renderState) , mInitialized(false) { INIT_LOGD("Creating OpenGL renderer caches"); @@ -238,7 +237,6 @@ void Caches::flush(FlushMode mode) { gradientCache.clear(); fontRenderer.clear(); fboCache.clear(); - dither.clear(); // fall through case FlushMode::Moderate: fontRenderer.flush(); diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 344ee71a2cc5..7c2e78c7d3b8 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -16,8 +16,6 @@ #pragma once -#include "AssetAtlas.h" -#include "Dither.h" #include "Extensions.h" #include "FboCache.h" #include "GammaFontRenderer.h" @@ -130,6 +128,15 @@ public: TextureVertex* getRegionMesh(); /** + * Returns the GL RGBA internal format to use for the current device + * If the device supports linear blending and needSRGB is true, + * this function returns GL_SRGB8_ALPHA8, otherwise it returns GL_RGBA + */ + constexpr GLint rgbaInternalFormat(bool needSRGB = true) const { + return extensions().hasSRGB() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; + } + + /** * Displays the memory usage of each cache and the total sum. */ void dumpMemoryUsage(); @@ -157,8 +164,6 @@ public: TaskManager tasks; - Dither dither; - bool gpuPixelBuffersEnabled; // Debug methods @@ -169,7 +174,7 @@ public: void setProgram(const ProgramDescription& description); void setProgram(Program* program); - Extensions& extensions() { return mExtensions; } + const Extensions& extensions() const { return mExtensions; } Program& program() { return *mProgram; } PixelBufferState& pixelBufferState() { return *mPixelBufferState; } TextureState& textureState() { return *mTextureState; } diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp deleted file mode 100644 index ec2013e27401..000000000000 --- a/libs/hwui/Dither.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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 "Caches.h" -#include "Dither.h" - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Lifecycle -/////////////////////////////////////////////////////////////////////////////// - -Dither::Dither(Caches& caches) - : mCaches(caches) - , mInitialized(false) - , mDitherTexture(0) { -} - -void Dither::bindDitherTexture() { - if (!mInitialized) { - glGenTextures(1, &mDitherTexture); - mCaches.textureState().bindTexture(mDitherTexture); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - if (mCaches.extensions().hasFloatTextures()) { - // We use a R16F texture, let's remap the alpha channel to the - // red channel to avoid changing the shader sampling code on GL ES 3.0+ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED); - - float dither = 1.0f / (255.0f * DITHER_KERNEL_SIZE * DITHER_KERNEL_SIZE); - const GLfloat pattern[] = { - 0 * dither, 8 * dither, 2 * dither, 10 * dither, - 12 * dither, 4 * dither, 14 * dither, 6 * dither, - 3 * dither, 11 * dither, 1 * dither, 9 * dither, - 15 * dither, 7 * dither, 13 * dither, 5 * dither - }; - - glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0, - GL_RED, GL_FLOAT, &pattern); - } else { - const uint8_t pattern[] = { - 0, 8, 2, 10, - 12, 4, 14, 6, - 3, 11, 1, 9, - 15, 7, 13, 5 - }; - - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0, - GL_ALPHA, GL_UNSIGNED_BYTE, &pattern); - } - - mInitialized = true; - } else { - mCaches.textureState().bindTexture(mDitherTexture); - } -} - -void Dither::clear() { - if (mInitialized) { - mCaches.textureState().deleteTexture(mDitherTexture); - mInitialized = false; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Program management -/////////////////////////////////////////////////////////////////////////////// - -void Dither::setupProgram(Program& program, GLuint* textureUnit) { - GLuint textureSlot = (*textureUnit)++; - mCaches.textureState().activateTexture(textureSlot); - - bindDitherTexture(); - - glUniform1i(program.getUniform("ditherSampler"), textureSlot); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h deleted file mode 100644 index 6af3e8384472..000000000000 --- a/libs/hwui/Dither.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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. - */ - -#ifndef ANDROID_HWUI_DITHER_H -#define ANDROID_HWUI_DITHER_H - -#include <GLES3/gl3.h> - -namespace android { -namespace uirenderer { - -class Caches; -class Extensions; -class Program; - -// Must be a power of two -#define DITHER_KERNEL_SIZE 4 -// These must not use the .0f notation as they are used from GLSL -#define DITHER_KERNEL_SIZE_INV (1.0 / 4.0) -#define DITHER_KERNEL_SIZE_INV_SQUARE (1.0 / 16.0) - -/** - * Handles dithering for programs. - */ -class Dither { -public: - explicit Dither(Caches& caches); - - void clear(); - void setupProgram(Program& program, GLuint* textureUnit); - -private: - void bindDitherTexture(); - - Caches& mCaches; - bool mInitialized; - GLuint mDitherTexture; -}; - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_DITHER_H diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp index 02caaa49e99c..4dc7536d60bc 100644 --- a/libs/hwui/Extensions.cpp +++ b/libs/hwui/Extensions.cpp @@ -22,6 +22,7 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> + #include <utils/Log.h> namespace android { @@ -44,6 +45,14 @@ Extensions::Extensions() { mHas1BitStencil = extensions.has("GL_OES_stencil1"); mHas4BitStencil = extensions.has("GL_OES_stencil4"); mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage"); + mHasSRGB = extensions.has("GL_EXT_sRGB"); + mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control"); + + // If linear blending is enabled, the device must have ES3.0 and GL_EXT_sRGB_write_control +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + assert(mVersionMajor >= 3 || mHasSRGB); + assert(mHasSRGBWriteControl); +#endif const char* version = (const char*) glGetString(GL_VERSION); diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index 67cc747015e0..cc73c2317720 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -43,6 +43,8 @@ public: inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; } inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; } inline bool hasFloatTextures() const { return mVersionMajor >= 3; } + inline bool hasSRGB() const { return mVersionMajor >= 3 || mHasSRGB; } + inline bool hasSRGBWriteControl() const { return hasSRGB() && mHasSRGBWriteControl; } inline int getMajorGlVersion() const { return mVersionMajor; } inline int getMinorGlVersion() const { return mVersionMinor; } @@ -55,6 +57,8 @@ private: bool mHas1BitStencil; bool mHas4BitStencil; bool mHasUnpackSubImage; + bool mHasSRGB; + bool mHasSRGBWriteControl; int mVersionMajor; int mVersionMinor; diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h index 9a39ec28aa3d..6d19b7ccdd79 100644 --- a/libs/hwui/FloatColor.h +++ b/libs/hwui/FloatColor.h @@ -16,6 +16,7 @@ #ifndef FLOATCOLOR_H #define FLOATCOLOR_H +#include "utils/Color.h" #include "utils/Macros.h" #include "utils/MathUtils.h" @@ -25,11 +26,13 @@ namespace android { namespace uirenderer { struct FloatColor { + // "color" is a gamma-encoded sRGB color + // After calling this method, the color is stored as a pre-multiplied linear color void set(uint32_t color) { a = ((color >> 24) & 0xff) / 255.0f; - r = a * ((color >> 16) & 0xff) / 255.0f; - g = a * ((color >> 8) & 0xff) / 255.0f; - b = a * ((color ) & 0xff) / 255.0f; + r = a * EOCF_sRGB(((color >> 16) & 0xff) / 255.0f); + g = a * EOCF_sRGB(((color >> 8) & 0xff) / 255.0f); + b = a * EOCF_sRGB(((color ) & 0xff) / 255.0f); } bool isNotBlack() { diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 9b60dfcb5867..effc65ea967f 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -60,11 +60,17 @@ void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) { } int transformFlags = pureTranslate ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None; +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + bool gammaCorrection = true; +#else + bool gammaCorrection = false; +#endif Glop glop; GlopBuilder(renderer->renderState(), renderer->caches(), &glop) .setRoundRectClipState(bakedState->roundRectClipState) .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha) + .setGammaCorrection(gammaCorrection) .setTransform(bakedState->computedState.transform, transformFlags) .setModelViewIdentityEmptyBounds() .build(); @@ -287,24 +293,23 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp // Copy the glyph image, taking the mask format into account switch (format) { case SkMask::kA8_Format: { - uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE; // write leading border line memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); // write glyph data if (mGammaTable) { - for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { + for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { row = cacheY * cacheWidth; cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; - for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { + for (uint32_t cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { uint8_t tempCol = bitmapBuffer[bY + bX]; cacheBuffer[row + cacheX] = mGammaTable[tempCol]; } cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; } } else { - for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { + for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { row = cacheY * cacheWidth; memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth); cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index be4fdac5f800..17ad0e36fa90 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -612,7 +612,6 @@ void FrameBuilder::deferBitmapOp(const BitmapOp& op) { && op.bitmap->colorType() != kAlpha_8_SkColorType && hasMergeableClip(*bakedState)) { mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID()); - // TODO: AssetAtlas in mergeId currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId); } else { currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); @@ -687,7 +686,6 @@ void FrameBuilder::deferPatchOp(const PatchOp& op) { && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode && hasMergeableClip(*bakedState)) { mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID()); - // TODO: AssetAtlas in mergeId // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId); diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp index 96cac86386b5..8aff0a24b4f0 100644 --- a/libs/hwui/GammaFontRenderer.cpp +++ b/libs/hwui/GammaFontRenderer.cpp @@ -24,12 +24,13 @@ namespace uirenderer { GammaFontRenderer::GammaFontRenderer() { INIT_LOGD("Creating lookup gamma font renderer"); +#ifndef ANDROID_ENABLE_LINEAR_BLENDING // Compute the gamma tables const float gamma = 1.0f / Properties::textGamma; - for (uint32_t i = 0; i <= 255; i++) { mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f)); } +#endif } void GammaFontRenderer::endPrecaching() { diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h index bd27a1a72060..c9cf69bc7d9f 100644 --- a/libs/hwui/GammaFontRenderer.h +++ b/libs/hwui/GammaFontRenderer.h @@ -18,11 +18,6 @@ #define ANDROID_HWUI_GAMMA_FONT_RENDERER_H #include "FontRenderer.h" -#include "Program.h" - -#include <SkPaint.h> - -#include <utils/String8.h> namespace android { namespace uirenderer { @@ -43,7 +38,11 @@ public: FontRenderer& getFontRenderer() { if (!mRenderer) { - mRenderer.reset(new FontRenderer(&mGammaTable[0])); + const uint8_t* table = nullptr; +#ifndef ANDROID_ENABLE_LINEAR_BLENDING + table = &mGammaTable[0]; +#endif + mRenderer.reset(new FontRenderer(table)); } return *mRenderer; } @@ -64,7 +63,9 @@ public: private: std::unique_ptr<FontRenderer> mRenderer; +#ifndef ANDROID_ENABLE_LINEAR_BLENDING uint8_t mGammaTable[256]; +#endif }; }; // namespace uirenderer diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp index 1091736ab9c3..ff88d020030c 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -223,16 +223,16 @@ void GlopBuilder::setFill(int color, float alphaScale, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage, const SkShader* shader, const SkColorFilter* colorFilter) { if (mode != SkXfermode::kClear_Mode) { - float alpha = (SkColorGetA(color) / 255.0f) * alphaScale; if (!shader) { - float colorScale = alpha / 255.0f; - mOutGlop->fill.color = { - colorScale * SkColorGetR(color), - colorScale * SkColorGetG(color), - colorScale * SkColorGetB(color), - alpha - }; + FloatColor c; + c.set(color); + c.r *= alphaScale; + c.g *= alphaScale; + c.b *= alphaScale; + c.a *= alphaScale; + mOutGlop->fill.color = c; } else { + float alpha = (SkColorGetA(color) / 255.0f) * alphaScale; mOutGlop->fill.color = { 1, 1, 1, alpha }; } } else { @@ -276,15 +276,7 @@ void GlopBuilder::setFill(int color, float alphaScale, if (colorFilter->asColorMode(&color, &mode)) { mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Blend; mDescription.colorMode = mode; - - const float alpha = SkColorGetA(color) / 255.0f; - float colorScale = alpha / 255.0f; - mOutGlop->fill.filter.color = { - colorScale * SkColorGetR(color), - colorScale * SkColorGetG(color), - colorScale * SkColorGetB(color), - alpha, - }; + mOutGlop->fill.filter.color.set(color); } else if (colorFilter->asColorMatrix(srcColorMatrix)) { mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Matrix; @@ -297,10 +289,10 @@ void GlopBuilder::setFill(int color, float alphaScale, // Skia uses the range [0..255] for the addition vector, but we need // the [0..1] range to apply the vector in GLSL float* colorVector = mOutGlop->fill.filter.matrix.vector; - colorVector[0] = srcColorMatrix[4] / 255.0f; - colorVector[1] = srcColorMatrix[9] / 255.0f; - colorVector[2] = srcColorMatrix[14] / 255.0f; - colorVector[3] = srcColorMatrix[19] / 255.0f; + colorVector[0] = EOCF_sRGB(srcColorMatrix[4] / 255.0f); + colorVector[1] = EOCF_sRGB(srcColorMatrix[9] / 255.0f); + colorVector[2] = EOCF_sRGB(srcColorMatrix[14] / 255.0f); + colorVector[3] = EOCF_sRGB(srcColorMatrix[19] / 255.0f); } else { LOG_ALWAYS_FATAL("unsupported ColorFilter"); } @@ -481,6 +473,13 @@ GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& text return *this; } +GlopBuilder& GlopBuilder::setGammaCorrection(bool enabled) { + REQUIRE_STAGES(kFillStage); + + mDescription.hasGammaCorrection = enabled; + return *this; +} + //////////////////////////////////////////////////////////////////////////////// // Transform //////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h index 11524615074e..1f3b53abdb29 100644 --- a/libs/hwui/GlopBuilder.h +++ b/libs/hwui/GlopBuilder.h @@ -105,6 +105,8 @@ public: GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState); + GlopBuilder& setGammaCorrection(bool enabled); + void build(); static void dump(const Glop& glop); diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index c8f5e9435594..8573ab078aaf 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -67,7 +67,8 @@ GradientCache::GradientCache(Extensions& extensions) , mSize(0) , mMaxSize(Properties::gradientCacheSize) , mUseFloatTexture(extensions.hasFloatTextures()) - , mHasNpot(extensions.hasNPot()){ + , mHasNpot(extensions.hasNPot()) + , mHasSRGB(extensions.hasSRGB()) { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); mCache.setOnEntryRemovedListener(this); @@ -176,71 +177,50 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, size_t GradientCache::bytesPerPixel() const { // We use 4 channels (RGBA) - return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t)); -} - -void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const { - outColor.r = (inColor >> 16) & 0xff; - outColor.g = (inColor >> 8) & 0xff; - outColor.b = (inColor >> 0) & 0xff; - outColor.a = (inColor >> 24) & 0xff; + return 4 * (mUseFloatTexture ? /* fp16 */ 2 : sizeof(uint8_t)); } -void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const { - outColor.r = ((inColor >> 16) & 0xff) / 255.0f; - outColor.g = ((inColor >> 8) & 0xff) / 255.0f; - outColor.b = ((inColor >> 0) & 0xff) / 255.0f; - outColor.a = ((inColor >> 24) & 0xff) / 255.0f; +size_t GradientCache::sourceBytesPerPixel() const { + // We use 4 channels (RGBA) and upload from floats (not half floats) + return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t)); } -void GradientCache::mixBytes(GradientColor& start, GradientColor& end, float amount, +void GradientCache::mixBytes(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const { float oppAmount = 1.0f - amount; - const float alpha = start.a * oppAmount + end.a * amount; - const float a = alpha / 255.0f; - - *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount)); - *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount)); - *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount)); - *dst++ = uint8_t(alpha); + *dst++ = uint8_t(OECF_sRGB((start.r * oppAmount + end.r * amount) * 255.0f)); + *dst++ = uint8_t(OECF_sRGB((start.g * oppAmount + end.g * amount) * 255.0f)); + *dst++ = uint8_t(OECF_sRGB((start.b * oppAmount + end.b * amount) * 255.0f)); + *dst++ = uint8_t( (start.a * oppAmount + end.a * amount) * 255.0f); } -void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float amount, +void GradientCache::mixFloats(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const { float oppAmount = 1.0f - amount; - const float a = start.a * oppAmount + end.a * amount; - float* d = (float*) dst; - *d++ = a * (start.r * oppAmount + end.r * amount); - *d++ = a * (start.g * oppAmount + end.g * amount); - *d++ = a * (start.b * oppAmount + end.b * amount); - *d++ = a; - + *d++ = start.r * oppAmount + end.r * amount; + *d++ = start.g * oppAmount + end.g * amount; + *d++ = start.b * oppAmount + end.b * amount; + *d++ = start.a * oppAmount + end.a * amount; dst += 4 * sizeof(float); } void GradientCache::generateTexture(uint32_t* colors, float* positions, const uint32_t width, const uint32_t height, Texture* texture) { - const GLsizei rowBytes = width * bytesPerPixel(); + const GLsizei rowBytes = width * sourceBytesPerPixel(); uint8_t pixels[rowBytes * height]; - static ChannelSplitter gSplitters[] = { - &android::uirenderer::GradientCache::splitToBytes, - &android::uirenderer::GradientCache::splitToFloats, - }; - ChannelSplitter split = gSplitters[mUseFloatTexture]; - static ChannelMixer gMixers[] = { - &android::uirenderer::GradientCache::mixBytes, - &android::uirenderer::GradientCache::mixFloats, + &android::uirenderer::GradientCache::mixBytes, // colors are stored gamma-encoded + &android::uirenderer::GradientCache::mixFloats, // colors are stored in linear }; ChannelMixer mix = gMixers[mUseFloatTexture]; - GradientColor start; - (this->*split)(colors[0], start); + FloatColor start; + start.set(colors[0]); - GradientColor end; - (this->*split)(colors[1], end); + FloatColor end; + end.set(colors[1]); int currentPos = 1; float startPos = positions[0]; @@ -255,7 +235,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, currentPos++; - (this->*split)(colors[currentPos], end); + end.set(colors[currentPos]); distance = positions[currentPos] - startPos; } @@ -266,10 +246,10 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, memcpy(pixels + rowBytes, pixels, rowBytes); if (mUseFloatTexture) { - // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering texture->upload(GL_RGBA16F, width, height, GL_RGBA, GL_FLOAT, pixels); } else { - texture->upload(GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + GLint internalFormat = mHasSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; + texture->upload(internalFormat, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); } texture->setFilter(GL_LINEAR); diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index 49be19ab1c81..3fd8e80dd64b 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -26,6 +26,8 @@ #include <utils/LruCache.h> #include <utils/Mutex.h> +#include "FloatColor.h" + namespace android { namespace uirenderer { @@ -150,25 +152,13 @@ private: void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info); size_t bytesPerPixel() const; + size_t sourceBytesPerPixel() const; - struct GradientColor { - float r; - float g; - float b; - float a; - }; - - typedef void (GradientCache::*ChannelSplitter)(uint32_t inColor, - GradientColor& outColor) const; - - void splitToBytes(uint32_t inColor, GradientColor& outColor) const; - void splitToFloats(uint32_t inColor, GradientColor& outColor) const; - - typedef void (GradientCache::*ChannelMixer)(GradientColor& start, GradientColor& end, + typedef void (GradientCache::*ChannelMixer)(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const; - void mixBytes(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const; - void mixFloats(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const; + void mixBytes(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const; + void mixFloats(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const; LruCache<GradientCacheEntry, Texture*> mCache; @@ -178,6 +168,7 @@ private: GLint mMaxTextureSize; bool mUseFloatTexture; bool mHasNpot; + bool mHasSRGB; mutable Mutex mLock; }; // class GradientCache diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index c688a96aa8cd..01650ef5745f 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -75,7 +75,7 @@ public: } void setSize(uint32_t width, uint32_t height) { - texture.updateSize(width, height, texture.format()); + texture.updateSize(width, height, texture.internalFormat(), texture.format()); } inline void setBlend(bool blend) { diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index a6c281dc9839..52c62cc34670 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -225,17 +225,15 @@ void PatchCache::setupMesh(Patch* newMesh) { static const UvMapper sIdentity; -const Patch* PatchCache::get(const AssetAtlas::Entry* entry, - const uint32_t bitmapWidth, const uint32_t bitmapHeight, +const Patch* PatchCache::get( const uint32_t bitmapWidth, const uint32_t bitmapHeight, const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) { const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch); const Patch* mesh = mCache.get(description); if (!mesh) { - const UvMapper& mapper = entry ? entry->uvMapper : sIdentity; Patch* newMesh = new Patch(bitmapWidth, bitmapHeight, - pixelWidth, pixelHeight, mapper, patch); + pixelWidth, pixelHeight, sIdentity, patch); if (newMesh->vertices) { setupMesh(newMesh); diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h index 6e6a730ffe2b..0624c355332c 100644 --- a/libs/hwui/PatchCache.h +++ b/libs/hwui/PatchCache.h @@ -22,7 +22,6 @@ #include <androidfw/ResourceTypes.h> -#include "AssetAtlas.h" #include "Debug.h" #include "utils/Pair.h" @@ -54,8 +53,7 @@ public: explicit PatchCache(RenderState& renderState); ~PatchCache(); - const Patch* get(const AssetAtlas::Entry* entry, - const uint32_t bitmapWidth, const uint32_t bitmapHeight, + const Patch* get(const uint32_t bitmapWidth, const uint32_t bitmapHeight, const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch); void clear(); diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index e5200a516777..f5beb62a84ed 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -85,6 +85,8 @@ namespace uirenderer { #define PROGRAM_HAS_DEBUG_HIGHLIGHT 42 #define PROGRAM_HAS_ROUND_RECT_CLIP 43 +#define PROGRAM_HAS_GAMMA_CORRECTION 44 + /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// @@ -158,6 +160,8 @@ struct ProgramDescription { bool hasDebugHighlight; bool hasRoundRectClip; + bool hasGammaCorrection; + /** * Resets this description. All fields are reset back to the default * values they hold after building a new instance. @@ -196,6 +200,8 @@ struct ProgramDescription { hasDebugHighlight = false; hasRoundRectClip = false; + + hasGammaCorrection = false; } /** @@ -262,6 +268,7 @@ struct ProgramDescription { if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS; if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT; if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP; + if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION; return key; } diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 59225e108ac7..315c60eccb3e 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -17,8 +17,8 @@ #include <utils/String8.h> #include "Caches.h" -#include "Dither.h" #include "ProgramCache.h" +#include "Properties.h" namespace android { namespace uirenderer { @@ -69,22 +69,16 @@ const char* gVS_Header_Varyings_HasBitmap = "varying highp vec2 outBitmapTexCoords;\n"; const char* gVS_Header_Varyings_HasGradient[6] = { // Linear - "varying highp vec2 linear;\n" - "varying vec2 ditherTexCoords;\n", - "varying float linear;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 linear;\n", + "varying float linear;\n", // Circular - "varying highp vec2 circular;\n" - "varying vec2 ditherTexCoords;\n", - "varying highp vec2 circular;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 circular;\n", + "varying highp vec2 circular;\n", // Sweep - "varying highp vec2 sweep;\n" - "varying vec2 ditherTexCoords;\n", - "varying highp vec2 sweep;\n" - "varying vec2 ditherTexCoords;\n", + "varying highp vec2 sweep;\n", + "varying highp vec2 sweep;\n", }; const char* gVS_Header_Varyings_HasRoundRectClip = "varying highp vec2 roundRectPos;\n"; @@ -98,22 +92,16 @@ const char* gVS_Main_OutTransformedTexCoords = " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n"; const char* gVS_Main_OutGradient[6] = { // Linear - " linear = vec2((screenSpace * position).x, 0.5);\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " linear = (screenSpace * position).x;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", + " linear = vec2((screenSpace * position).x, 0.5);\n", + " linear = (screenSpace * position).x;\n", // Circular - " circular = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " circular = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", + " circular = (screenSpace * position).xy;\n", + " circular = (screenSpace * position).xy;\n", // Sweep + " sweep = (screenSpace * position).xy;\n", " sweep = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", - " sweep = (screenSpace * position).xy;\n" - " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n", }; const char* gVS_Main_OutBitmapTexCoords = " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; @@ -147,12 +135,11 @@ const char* gFS_Uniforms_TextureSampler = "uniform sampler2D baseSampler;\n"; const char* gFS_Uniforms_ExternalTextureSampler = "uniform samplerExternalOES baseSampler;\n"; -const char* gFS_Uniforms_Dither = - "uniform sampler2D ditherSampler;"; const char* gFS_Uniforms_GradientSampler[2] = { - "%s\n" + "uniform vec2 screenSize;\n" "uniform sampler2D gradientSampler;\n", - "%s\n" + + "uniform vec2 screenSize;\n" "uniform vec4 startColor;\n" "uniform vec4 endColor;\n" }; @@ -172,18 +159,51 @@ const char* gFS_Uniforms_HasRoundRectClip = "uniform vec4 roundRectInnerRectLTRB;\n" "uniform float roundRectRadius;\n"; +// Dithering must be done in the quantization space +// When we are writing to an sRGB framebuffer, we must do the following: +// EOCF(OECF(color) + dither) +// We approximate the transfer functions with gamma 2.0 to avoid branches and pow() +// The dithering pattern is generated with a triangle noise generator in the range [-0.5,1.5[ +// TODO: Handle linear fp16 render targets +const char* gFS_Dither_Functions = + "\nmediump float triangleNoise(const highp vec2 n) {\n" + " highp vec2 p = fract(n * vec2(5.3987, 5.4421));\n" + " p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));\n" + " highp float xy = p.x * p.y;\n" + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 0.5;\n" + "}\n"; +const char* gFS_Dither_Preamble[2] = { + // Linear framebuffer + "\nvec4 dither(const vec4 color) {\n" + " return vec4(color.rgb + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0), color.a);" + "}\n", + // sRGB framebuffer + "\nvec4 dither(const vec4 color) {\n" + " vec3 dithered = sqrt(color.rgb) + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);\n" + " return vec4(dithered * dithered, color.a);\n" + "}\n" +}; + +// Uses luminance coefficients from Rec.709 to choose the appropriate gamma +// The gamma() function assumes that bright text will be displayed on a dark +// background and that dark text will be displayed on bright background +// The gamma coefficient is chosen to thicken or thin the text accordingly +// The dot product used to compute the luminance could be approximated with +// a simple max(color.r, color.g, color.b) +const char* gFS_Gamma_Preamble = + "\n#define GAMMA (%.2f)\n" + "#define GAMMA_INV (%.2f)\n" + "\nfloat gamma(float a, const vec3 color) {\n" + " float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));\n" + " return pow(a, luminance < 0.5 ? GAMMA_INV : GAMMA);\n" + "}\n"; + const char* gFS_Main = "\nvoid main(void) {\n" - " lowp vec4 fragColor;\n"; + " vec4 fragColor;\n"; -const char* gFS_Main_Dither[2] = { - // ES 2.0 - "texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE), - // ES 3.0 - "texture2D(ditherSampler, ditherTexCoords).a" -}; -const char* gFS_Main_AddDitherToGradient = - " gradientColor += %s;\n"; +const char* gFS_Main_AddDither = + " fragColor = dither(fragColor);\n"; // Fast cases const char* gFS_Fast_SingleColor = @@ -202,24 +222,32 @@ const char* gFS_Fast_SingleA8Texture = "\nvoid main(void) {\n" " gl_FragColor = texture2D(baseSampler, outTexCoords);\n" "}\n\n"; +const char* gFS_Fast_SingleA8Texture_ApplyGamma = + "\nvoid main(void) {\n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, pow(texture2D(baseSampler, outTexCoords).a, GAMMA));\n" + "}\n\n"; const char* gFS_Fast_SingleModulateA8Texture = "\nvoid main(void) {\n" " gl_FragColor = color * texture2D(baseSampler, outTexCoords).a;\n" "}\n\n"; +const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma = + "\nvoid main(void) {\n" + " gl_FragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n" + "}\n\n"; const char* gFS_Fast_SingleGradient[2] = { "\nvoid main(void) {\n" - " gl_FragColor = %s + texture2D(gradientSampler, linear);\n" + " gl_FragColor = dither(texture2D(gradientSampler, linear));\n" "}\n\n", "\nvoid main(void) {\n" - " gl_FragColor = %s + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" + " gl_FragColor = dither(mix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n" "}\n\n", }; const char* gFS_Fast_SingleModulateGradient[2] = { "\nvoid main(void) {\n" - " gl_FragColor = %s + color.a * texture2D(gradientSampler, linear);\n" + " gl_FragColor = dither(color.a * texture2D(gradientSampler, linear));\n" "}\n\n", "\nvoid main(void) {\n" - " gl_FragColor = %s + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" + " gl_FragColor = dither(color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n" "}\n\n" }; @@ -239,11 +267,13 @@ const char* gFS_Main_FetchTexture[2] = { // Modulate " fragColor = color * texture2D(baseSampler, outTexCoords);\n" }; -const char* gFS_Main_FetchA8Texture[2] = { +const char* gFS_Main_FetchA8Texture[4] = { // Don't modulate " fragColor = texture2D(baseSampler, outTexCoords);\n", + " fragColor = texture2D(baseSampler, outTexCoords);\n", // Modulate " fragColor = color * texture2D(baseSampler, outTexCoords).a;\n", + " fragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n", }; const char* gFS_Main_FetchGradient[6] = { // Linear @@ -271,29 +301,38 @@ const char* gFS_Main_BlendShadersBG = " fragColor = blendShaders(gradientColor, bitmapColor)"; const char* gFS_Main_BlendShadersGB = " fragColor = blendShaders(bitmapColor, gradientColor)"; -const char* gFS_Main_BlendShaders_Modulate[3] = { +const char* gFS_Main_BlendShaders_Modulate[6] = { // Don't modulate ";\n", + ";\n", // Modulate " * color.a;\n", + " * color.a;\n", // Modulate with alpha 8 texture " * texture2D(baseSampler, outTexCoords).a;\n", + " * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n", }; -const char* gFS_Main_GradientShader_Modulate[3] = { +const char* gFS_Main_GradientShader_Modulate[6] = { // Don't modulate " fragColor = gradientColor;\n", + " fragColor = gradientColor;\n", // Modulate " fragColor = gradientColor * color.a;\n", + " fragColor = gradientColor * color.a;\n", // Modulate with alpha 8 texture " fragColor = gradientColor * texture2D(baseSampler, outTexCoords).a;\n", + " fragColor = gradientColor * gamma(texture2D(baseSampler, outTexCoords).a, gradientColor.rgb);\n", }; -const char* gFS_Main_BitmapShader_Modulate[3] = { +const char* gFS_Main_BitmapShader_Modulate[6] = { // Don't modulate " fragColor = bitmapColor;\n", + " fragColor = bitmapColor;\n", // Modulate " fragColor = bitmapColor * color.a;\n", + " fragColor = bitmapColor * color.a;\n", // Modulate with alpha 8 texture " fragColor = bitmapColor * texture2D(baseSampler, outTexCoords).a;\n", + " fragColor = bitmapColor * gamma(texture2D(baseSampler, outTexCoords).a, bitmapColor.rgb);\n", }; const char* gFS_Main_FragColor = " gl_FragColor = fragColor;\n"; @@ -385,7 +424,8 @@ const char* gBlendOps[18] = { /////////////////////////////////////////////////////////////////////////////// ProgramCache::ProgramCache(Extensions& extensions) - : mHasES3(extensions.getMajorGlVersion() >= 3) { + : mHasES3(extensions.getMajorGlVersion() >= 3) + , mHasSRGB(extensions.hasSRGB()) { } ProgramCache::~ProgramCache() { @@ -518,6 +558,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description static bool shaderOp(const ProgramDescription& description, String8& shader, const int modulateOp, const char** snippets) { int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; + op = op * 2 + description.hasGammaCorrection; shader.append(snippets[op]); return description.hasAlpha8Texture; } @@ -570,13 +611,16 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Uniforms_ExternalTextureSampler); } if (description.hasGradient) { - shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient], - gFS_Uniforms_Dither); + shader.append(gFS_Uniforms_GradientSampler[description.isSimpleGradient]); } if (description.hasRoundRectClip) { shader.append(gFS_Uniforms_HasRoundRectClip); } + if (description.hasGammaCorrection) { + shader.appendFormat(gFS_Gamma_Preamble, Properties::textGamma, 1.0f / Properties::textGamma); + } + // Optimization for common cases if (!description.hasVertexAlpha && !blendFramebuffer @@ -607,18 +651,26 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti fast = true; } else if (singleA8Texture) { if (!description.modulate) { - shader.append(gFS_Fast_SingleA8Texture); + if (description.hasGammaCorrection) { + shader.append(gFS_Fast_SingleA8Texture_ApplyGamma); + } else { + shader.append(gFS_Fast_SingleA8Texture); + } } else { - shader.append(gFS_Fast_SingleModulateA8Texture); + if (description.hasGammaCorrection) { + shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma); + } else { + shader.append(gFS_Fast_SingleModulateA8Texture); + } } fast = true; } else if (singleGradient) { + shader.append(gFS_Dither_Functions); + shader.append(gFS_Dither_Preamble[mHasSRGB]); if (!description.modulate) { - shader.appendFormat(gFS_Fast_SingleGradient[description.isSimpleGradient], - gFS_Main_Dither[mHasES3]); + shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]); } else { - shader.appendFormat(gFS_Fast_SingleModulateGradient[description.isSimpleGradient], - gFS_Main_Dither[mHasES3]); + shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]); } fast = true; } @@ -652,6 +704,10 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.isBitmapNpot) { generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT); } + if (description.hasGradient) { + shader.append(gFS_Dither_Functions); + shader.append(gFS_Dither_Preamble[mHasSRGB]); + } // Begin the shader shader.append(gFS_Main); { @@ -659,7 +715,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasTexture || description.hasExternalTexture) { if (description.hasAlpha8Texture) { if (!description.hasGradient && !description.hasBitmap) { - shader.append(gFS_Main_FetchA8Texture[modulateOp]); + shader.append( + gFS_Main_FetchA8Texture[modulateOp * 2 + description.hasGammaCorrection]); } } else { shader.append(gFS_Main_FetchTexture[modulateOp]); @@ -671,7 +728,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } if (description.hasGradient) { shader.append(gFS_Main_FetchGradient[gradientIndex(description)]); - shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]); } if (description.hasBitmap) { if (!description.isBitmapNpot) { @@ -715,6 +771,10 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } } + if (description.hasGradient) { + shader.append(gFS_Main_AddDither); + } + // Output the fragment if (!blendFramebuffer) { shader.append(gFS_Main_FragColor); diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 9ac885b665e7..292ecdfdc2b6 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -59,6 +59,7 @@ private: std::map<programid, std::unique_ptr<Program>> mCache; const bool mHasES3; + const bool mHasSRGB; }; // class ProgramCache }; // namespace uirenderer diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index eedc9e7d5333..92a49d28c408 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -203,7 +203,7 @@ enum DebugLevel { #define PROPERTY_TEXT_LARGE_CACHE_WIDTH "ro.hwui.text_large_cache_width" #define PROPERTY_TEXT_LARGE_CACHE_HEIGHT "ro.hwui.text_large_cache_height" -// Gamma (>= 1.0, <= 10.0) +// Gamma (>= 1.0, <= 3.0) #define PROPERTY_TEXT_GAMMA "hwui.text_gamma" /////////////////////////////////////////////////////////////////////////////// @@ -222,7 +222,7 @@ enum DebugLevel { #define DEFAULT_TEXTURE_CACHE_FLUSH_RATE 0.6f -#define DEFAULT_TEXT_GAMMA 1.4f +#define DEFAULT_TEXT_GAMMA 1.45f // Match design tools // cap to 256 to limite paths in the path cache #define DEFAULT_PATH_TEXTURE_CAP 256 diff --git a/libs/hwui/PropertyValuesHolder.cpp b/libs/hwui/PropertyValuesHolder.cpp index 6ba0ab59a88c..2a03e6a3ebc5 100644 --- a/libs/hwui/PropertyValuesHolder.cpp +++ b/libs/hwui/PropertyValuesHolder.cpp @@ -16,6 +16,7 @@ #include "PropertyValuesHolder.h" +#include "utils/Color.h" #include "utils/VectorDrawableUtils.h" #include <utils/Log.h> @@ -25,18 +26,26 @@ namespace uirenderer { using namespace VectorDrawable; -inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) { - return (U8CPU) (fromValue * (1 - fraction) + toValue * fraction); +inline constexpr float lerp(float fromValue, float toValue, float fraction) { + return float (fromValue * (1 - fraction) + toValue * fraction); +} + +inline constexpr float linearize(U8CPU component) { + return EOCF_sRGB(component / 255.0f); } // TODO: Add a test for this void ColorEvaluator::evaluate(SkColor* outColor, const SkColor& fromColor, const SkColor& toColor, float fraction) const { - U8CPU alpha = lerp(SkColorGetA(fromColor), SkColorGetA(toColor), fraction); - U8CPU red = lerp(SkColorGetR(fromColor), SkColorGetR(toColor), fraction); - U8CPU green = lerp(SkColorGetG(fromColor), SkColorGetG(toColor), fraction); - U8CPU blue = lerp(SkColorGetB(fromColor), SkColorGetB(toColor), fraction); - *outColor = SkColorSetARGB(alpha, red, green, blue); + float a = lerp(SkColorGetA(fromColor) / 255.0f, SkColorGetA(toColor) / 255.0f, fraction); + float r = lerp(linearize(SkColorGetR(fromColor)), linearize(SkColorGetR(toColor)), fraction); + float g = lerp(linearize(SkColorGetG(fromColor)), linearize(SkColorGetG(toColor)), fraction); + float b = lerp(linearize(SkColorGetB(fromColor)), linearize(SkColorGetB(toColor)), fraction); + *outColor = SkColorSetARGB( + (U8CPU) roundf(a * 255.0f), + (U8CPU) roundf(OECF_sRGB(r) * 255.0f), + (U8CPU) roundf(OECF_sRGB(g) * 255.0f), + (U8CPU) roundf(OECF_sRGB(b) * 255.0f)); } void PathEvaluator::evaluate(PathData* out, diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index ddca122788d1..22c6dfc6b55a 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -197,7 +197,7 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, Texture sourceTexture(caches); sourceTexture.wrap(sourceTexId, - sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */); + sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0, 0 /* total lie */); CopyResult copyResult = copyTextureInto(caches, renderThread.renderState(), sourceTexture, texTransform, srcRect, bitmap); diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index a65c22c3a555..ebc41b18ed8c 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -216,7 +216,6 @@ struct BitmapOp : RecordedOp { : SUPER(BitmapOp) , bitmap(bitmap) {} const SkBitmap* bitmap; - // TODO: asset atlas/texture id lookup? }; struct BitmapMeshOp : RecordedOp { diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 6f4a6839be4e..9838df2f6013 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -177,11 +177,12 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode outData->endColor.set(gradInfo.fColors[1]); } - outData->ditherSampler = (*textureUnit)++; return true; } -void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) { +void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data, + const GLsizei width, const GLsizei height) { + if (CC_UNLIKELY(data.gradientTexture)) { caches.textureState().activateTexture(data.gradientSampler); bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST); @@ -191,10 +192,7 @@ void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& dat bindUniformColor(caches.program().getUniform("endColor"), data.endColor); } - // TODO: remove sampler slot incrementing from dither.setupProgram, - // since this assignment of slots is done at store, not apply time - GLuint ditherSampler = data.ditherSampler; - caches.dither.setupProgram(caches.program(), &ditherSampler); + glUniform2f(caches.program().getUniform("screenSize"), 1.0f / width, 1.0f / height); glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1, GL_FALSE, &data.screenSpace.data[0]); } @@ -208,13 +206,7 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model return false; } - /* - * Bypass the AssetAtlas, since those textures: - * 1) require UV mapping, which isn't implemented in matrix computation below - * 2) can't handle REPEAT simply - * 3) are safe to upload here (outside of sync stage), since they're static - */ - outData->bitmapTexture = caches.textureCache.getAndBypassAtlas(&bitmap); + outData->bitmapTexture = caches.textureCache.get(&bitmap); if (!outData->bitmapTexture) return false; outData->bitmapSampler = (*textureUnit)++; @@ -388,11 +380,12 @@ void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& mo outData->skiaShaderType = kNone_SkiaShaderType; } -void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) { +void SkiaShader::apply(Caches& caches, const SkiaShaderData& data, + const GLsizei width, const GLsizei height) { if (!data.skiaShaderType) return; if (data.skiaShaderType & kGradient_SkiaShaderType) { - applyGradient(caches, data.gradientData); + applyGradient(caches, data.gradientData, width, height); } if (data.skiaShaderType & kBitmap_SkiaShaderType) { applyBitmap(caches, data.bitmapData); diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index 884196d9fc62..5854289f49a7 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -62,7 +62,6 @@ struct SkiaShaderData { } bitmapData; struct GradientShaderData { Matrix4 screenSpace; - GLuint ditherSampler; // simple gradient FloatColor startColor; @@ -72,7 +71,6 @@ struct SkiaShaderData { Texture* gradientTexture; GLuint gradientSampler; GLenum wrapST; - } gradientData; struct LayerShaderData { Layer* layer; @@ -90,7 +88,8 @@ public: static void store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, GLuint* textureUnit, ProgramDescription* description, SkiaShaderData* outData); - static void apply(Caches& caches, const SkiaShaderData& data); + static void apply(Caches& caches, const SkiaShaderData& data, + const GLsizei width, const GLsizei height); }; }; // namespace uirenderer diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index 4f49a3518be0..908f57265906 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -26,19 +26,23 @@ namespace android { namespace uirenderer { +// Number of bytes used by a texture in the given format static int bytesPerPixel(GLint glFormat) { switch (glFormat) { // The wrapped-texture case, usually means a SurfaceTexture case 0: return 0; + case GL_LUMINANCE: case GL_ALPHA: return 1; + case GL_SRGB8: case GL_RGB: return 3; + case GL_SRGB8_ALPHA8: case GL_RGBA: return 4; case GL_RGBA16F: - return 16; + return 8; default: LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat); } @@ -83,14 +87,16 @@ void Texture::deleteTexture() { mId = 0; } -bool Texture::updateSize(uint32_t width, uint32_t height, GLint format) { - if (mWidth == width && mHeight == height && mFormat == format) { +bool Texture::updateSize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { + if (mWidth == width && mHeight == height && + mFormat == format && mInternalFormat == internalFormat) { return false; } mWidth = width; mHeight = height; mFormat = format; - notifySizeChanged(mWidth * mHeight * bytesPerPixel(mFormat)); + mInternalFormat = internalFormat; + notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat)); return true; } @@ -101,10 +107,10 @@ void Texture::resetCachedParams() { mMagFilter = GL_LINEAR; } -void Texture::upload(GLint internalformat, uint32_t width, uint32_t height, +void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, GLenum type, const void* pixels) { GL_CHECKPOINT(MODERATE); - bool needsAlloc = updateSize(width, height, internalformat); + bool needsAlloc = updateSize(width, height, internalFormat, format); if (!mId) { glGenTextures(1, &mId); needsAlloc = true; @@ -112,17 +118,17 @@ void Texture::upload(GLint internalformat, uint32_t width, uint32_t height, } mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); if (needsAlloc) { - glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0, + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); } else if (pixels) { - glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0, + glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); } GL_CHECKPOINT(MODERATE); } -static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp, - GLsizei width, GLsizei height, const GLvoid * data) { +static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type, + GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) { const bool useStride = stride != width && Caches::getInstance().extensions().hasUnpackRowLength(); @@ -132,7 +138,7 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str } if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data); } else { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); } @@ -156,7 +162,7 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str } if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp); } else { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); } @@ -166,31 +172,44 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str } static void uploadSkBitmapToTexture(const SkBitmap& bitmap, - bool resize, GLenum format, GLenum type) { - uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(), - bitmap.width(), bitmap.height(), bitmap.getPixels()); + bool resize, GLint internalFormat, GLenum format, GLenum type) { + uploadToTexture(resize, internalFormat, format, type, bitmap.rowBytesAsPixels(), + bitmap.bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.getPixels()); } -static void colorTypeToGlFormatAndType(SkColorType colorType, - GLint* outFormat, GLint* outType) { +static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, + bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) { switch (colorType) { case kAlpha_8_SkColorType: *outFormat = GL_ALPHA; + *outInternalFormat = GL_ALPHA; *outType = GL_UNSIGNED_BYTE; break; case kRGB_565_SkColorType: - *outFormat = GL_RGB; - *outType = GL_UNSIGNED_SHORT_5_6_5; + if (needSRGB) { + // We would ideally use a GL_RGB/GL_SRGB8 texture but the + // intermediate Skia bitmap needs to be ARGB_8888 + *outFormat = GL_RGBA; + *outInternalFormat = caches.rgbaInternalFormat(); + *outType = GL_UNSIGNED_BYTE; + } else { + *outFormat = GL_RGB; + *outInternalFormat = GL_RGB; + *outType = GL_UNSIGNED_SHORT_5_6_5; + } break; // ARGB_4444 and Index_8 are both upconverted to RGBA_8888 case kARGB_4444_SkColorType: case kIndex_8_SkColorType: case kN32_SkColorType: *outFormat = GL_RGBA; + *outInternalFormat = caches.rgbaInternalFormat(needSRGB); *outType = GL_UNSIGNED_BYTE; break; case kGray_8_SkColorType: + // TODO: Handle sRGB *outFormat = GL_LUMINANCE; + *outInternalFormat = GL_LUMINANCE; *outType = GL_UNSIGNED_BYTE; break; default: @@ -224,29 +243,36 @@ void Texture::upload(const SkBitmap& bitmap) { setDefaultParams = true; } - GLint format, type; - colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type); + sk_sp<SkColorSpace> sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); + bool needSRGB = bitmap.colorSpace() == sRGB.get(); - if (updateSize(bitmap.width(), bitmap.height(), format)) { + GLint internalFormat, format, type; + colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type); + + if (updateSize(bitmap.width(), bitmap.height(), internalFormat, format)) { needsAlloc = true; } blend = !bitmap.isOpaque(); mCaches.textureState().bindTexture(mId); + // TODO: Handle sRGB gray bitmaps + bool hasSRGB = mCaches.extensions().hasSRGB(); if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType - || bitmap.colorType() == kIndex_8_SkColorType)) { + || bitmap.colorType() == kIndex_8_SkColorType + || (bitmap.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB))) { + SkBitmap rgbaBitmap; - rgbaBitmap.allocPixels(SkImageInfo::MakeN32(mWidth, mHeight, - bitmap.alphaType())); + rgbaBitmap.allocPixels(SkImageInfo::MakeN32( + mWidth, mHeight, bitmap.alphaType(), hasSRGB ? sRGB : nullptr)); rgbaBitmap.eraseColor(0); SkCanvas canvas(rgbaBitmap); canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); - uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, format, type); + uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, internalFormat, format, type); } else { - uploadSkBitmapToTexture(bitmap, needsAlloc, format, type); + uploadSkBitmapToTexture(bitmap, needsAlloc, internalFormat, format, type); } if (canMipMap) { @@ -262,11 +288,12 @@ void Texture::upload(const SkBitmap& bitmap) { } } -void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint format) { +void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format) { mId = id; mWidth = width; mHeight = height; mFormat = format; + mInternalFormat = internalFormat; // We're wrapping an existing texture, so don't double count this memory notifySizeChanged(0); } diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index b72742f45654..aa8a6d3fae82 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -69,8 +69,8 @@ public: * * The image data is undefined after calling this. */ - void resize(uint32_t width, uint32_t height, GLint format) { - upload(format, width, height, format, GL_UNSIGNED_BYTE, nullptr); + void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { + upload(internalFormat, width, height, format, GL_UNSIGNED_BYTE, nullptr); } /** @@ -85,13 +85,13 @@ public: /** * Basically glTexImage2D/glTexSubImage2D. */ - void upload(GLint internalformat, uint32_t width, uint32_t height, + void upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, GLenum type, const void* pixels); /** * Wraps an existing texture. */ - void wrap(GLuint id, uint32_t width, uint32_t height, GLint format); + void wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format); GLuint id() const { return mId; @@ -109,6 +109,10 @@ public: return mFormat; } + GLint internalFormat() const { + return mInternalFormat; + } + /** * Generation of the backing bitmap, */ @@ -148,13 +152,14 @@ private: friend class Layer; // Returns true if the size changed, false if it was the same - bool updateSize(uint32_t width, uint32_t height, GLint format); + bool updateSize(uint32_t width, uint32_t height, GLint internalFormat, GLint format); void resetCachedParams(); GLuint mId = 0; uint32_t mWidth = 0; uint32_t mHeight = 0; GLint mFormat = 0; + GLint mInternalFormat = 0; /* See GLES spec section 3.8.14 * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 523924af5ef1..5ccdbda67e74 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -18,7 +18,6 @@ #include <utils/Mutex.h> -#include "AssetAtlas.h" #include "Caches.h" #include "Texture.h" #include "TextureCache.h" @@ -36,8 +35,7 @@ TextureCache::TextureCache() : mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity) , mSize(0) , mMaxSize(Properties::textureCacheSize) - , mFlushRate(Properties::textureCacheFlushRate) - , mAssetAtlas(nullptr) { + , mFlushRate(Properties::textureCacheFlushRate) { mCache.setOnEntryRemovedListener(this); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); @@ -84,10 +82,6 @@ void TextureCache::operator()(uint32_t&, Texture*& texture) { // Caching /////////////////////////////////////////////////////////////////////////////// -void TextureCache::setAssetAtlas(AssetAtlas* assetAtlas) { - mAssetAtlas = assetAtlas; -} - void TextureCache::resetMarkInUse(void* ownerToken) { LruCache<uint32_t, Texture*>::Iterator iter(mCache); while (iter.next()) { @@ -108,14 +102,7 @@ bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) { // Returns a prepared Texture* that either is already in the cache or can fit // in the cache (and is thus added to the cache) -Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) { - if (CC_LIKELY(mAssetAtlas != nullptr) && atlasUsageType == AtlasUsageType::Use) { - AssetAtlas::Entry* entry = mAssetAtlas->getEntry(bitmap->pixelRef()); - if (CC_UNLIKELY(entry)) { - return entry->texture; - } - } - +Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) { Texture* texture = mCache.get(bitmap->pixelRef()->getStableID()); if (!texture) { @@ -160,7 +147,7 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a } bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap) { - Texture* texture = getCachedTexture(bitmap, AtlasUsageType::Use); + Texture* texture = getCachedTexture(bitmap); if (texture) { texture->isInUse = ownerToken; } @@ -168,11 +155,11 @@ bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap } bool TextureCache::prefetch(const SkBitmap* bitmap) { - return getCachedTexture(bitmap, AtlasUsageType::Use); + return getCachedTexture(bitmap); } -Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) { - Texture* texture = getCachedTexture(bitmap, atlasUsageType); +Texture* TextureCache::get(const SkBitmap* bitmap) { + Texture* texture = getCachedTexture(bitmap); if (!texture) { if (!canMakeTextureFromBitmap(bitmap)) { diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 0a61b6b1a522..88ef7711e844 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -47,8 +47,6 @@ class Texture; // Classes /////////////////////////////////////////////////////////////////////////////// -class AssetAtlas; - /** * A simple LRU texture cache. The cache has a maximum size expressed in bytes. * Any texture added to the cache causing the cache to grow beyond the maximum @@ -85,20 +83,10 @@ public: bool prefetch(const SkBitmap* bitmap); /** - * Returns the texture associated with the specified bitmap from either within the cache, or - * the AssetAtlas. If the texture cannot be found in the cache, a new texture is generated. - */ - Texture* get(const SkBitmap* bitmap) { - return get(bitmap, AtlasUsageType::Use); - } - - /** - * Returns the texture associated with the specified bitmap. If the texture cannot be found in - * the cache, a new texture is generated, even if it resides in the AssetAtlas. + * Returns the texture associated with the specified bitmap from within the cache. + * If the texture cannot be found in the cache, a new texture is generated. */ - Texture* getAndBypassAtlas(const SkBitmap* bitmap) { - return get(bitmap, AtlasUsageType::Bypass); - } + Texture* get(const SkBitmap* bitmap); /** * Removes the texture associated with the specified pixelRef. This is meant @@ -130,18 +118,10 @@ public: */ void flush(); - void setAssetAtlas(AssetAtlas* assetAtlas); - private: - enum class AtlasUsageType { - Use, - Bypass, - }; - bool canMakeTextureFromBitmap(const SkBitmap* bitmap); - Texture* get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType); - Texture* getCachedTexture(const SkBitmap* bitmap, AtlasUsageType atlasUsageType); + Texture* getCachedTexture(const SkBitmap* bitmap); LruCache<uint32_t, Texture*> mCache; @@ -155,8 +135,6 @@ private: std::vector<uint32_t> mGarbage; mutable Mutex mLock; - - AssetAtlas* mAssetAtlas; }; // class TextureCache }; // namespace uirenderer diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 2b7994139641..4e5b9ad2f0a3 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -561,8 +561,12 @@ void Tree::updateBitmapCache(SkBitmap* outCache, bool useStagingData) { bool Tree::allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height) { if (!canReuseBitmap(*outCache, width, height)) { - SkImageInfo info = SkImageInfo::Make(width, height, - kN32_SkColorType, kPremul_SkAlphaType); +#ifndef ANDROID_ENABLE_LINEAR_BLENDING + sk_sp<SkColorSpace> colorSpace = nullptr; +#else + sk_sp<SkColorSpace> colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); +#endif + SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace); outCache->setInfo(info); // TODO: Count the bitmap cache against app's java heap outCache->allocPixels(info); diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index c1bf980658b2..db982ad0c8f4 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -19,6 +19,7 @@ #include "Vector.h" +#include "FloatColor.h" #include "utils/Macros.h" namespace android { @@ -76,21 +77,19 @@ struct TextureVertex { REQUIRE_COMPATIBLE_LAYOUT(TextureVertex); /** - * Simple structure to describe a vertex with a position, texture UV and ARGB color. + * Simple structure to describe a vertex with a position, texture UV and an + * sRGB color with alpha. The color is stored pre-multiplied in linear space. */ struct ColorTextureVertex { float x, y; float u, v; - float r, g, b, a; + float r, g, b, a; // pre-multiplied linear static inline void set(ColorTextureVertex* vertex, float x, float y, - float u, float v, int color) { - - float a = ((color >> 24) & 0xff) / 255.0f; - float r = a * ((color >> 16) & 0xff) / 255.0f; - float g = a * ((color >> 8) & 0xff) / 255.0f; - float b = a * ((color) & 0xff) / 255.0f; - *vertex = { x, y, u, v, r, g, b, a }; + float u, float v, uint32_t color) { + FloatColor c; + c.set(color); + *vertex = { x, y, u, v, c.r, c.g, c.b, c.a }; } }; // struct ColorTextureVertex diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp index 49e9f65582ae..e2844ad0649a 100644 --- a/libs/hwui/font/CacheTexture.cpp +++ b/libs/hwui/font/CacheTexture.cpp @@ -180,7 +180,12 @@ void CacheTexture::allocatePixelBuffer() { mPixelBuffer = PixelBuffer::create(mFormat, getWidth(), getHeight()); } - mTexture.resize(mWidth, mHeight, mFormat); + GLint internalFormat = mFormat; + if (mFormat == GL_RGBA) { + internalFormat = mCaches.rgbaInternalFormat(); + } + + mTexture.resize(mWidth, mHeight, internalFormat, mFormat); mTexture.setFilter(getLinearFiltering() ? GL_LINEAR : GL_NEAREST); mTexture.setWrap(GL_CLAMP_TO_EDGE); } diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp index 10a26e08f897..a9bbb273dbb5 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.cpp +++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp @@ -22,6 +22,7 @@ #include "utils/FatVector.h" #include "utils/TraceUtils.h" +#include <utils/Color.h> #include <utils/Log.h> #include <GLES2/gl2.h> @@ -44,7 +45,7 @@ OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches, uint32_t height = computeIdealDimension(viewportHeight); ATRACE_FORMAT("Allocate %ux%u HW Layer", width, height); caches.textureState().activateTexture(0); - texture.resize(width, height, GL_RGBA); + texture.resize(width, height, caches.rgbaInternalFormat(), GL_RGBA); texture.blend = true; texture.setWrap(GL_CLAMP_TO_EDGE); // not setting filter on texture, since it's set when drawing, based on transform diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index ee4619d2c222..84ab3f31e7ab 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -52,7 +52,6 @@ void RenderState::onGLContextCreated() { mCaches = &Caches::createInstance(*this); } mCaches->init(); - mCaches->textureCache.setAssetAtlas(&mAssetAtlas); } static void layerLostGlContext(Layer* layer) { @@ -64,7 +63,6 @@ void RenderState::onGLContextDestroyed() { // TODO: reset all cached state in state objects std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); - mAssetAtlas.terminate(); mCaches->terminate(); @@ -147,9 +145,17 @@ void RenderState::interruptForFunctorInvoke() { meshState().resetVertexPointers(); meshState().disableTexCoordsVertexArray(); debugOverdraw(false, false); + // TODO: We need a way to know whether the functor is sRGB aware (b/32072673) + if (mCaches->extensions().hasSRGBWriteControl()) { + glDisable(GL_FRAMEBUFFER_SRGB_EXT); + } } void RenderState::resumeFromFunctorInvoke() { + if (mCaches->extensions().hasSRGBWriteControl()) { + glEnable(GL_FRAMEBUFFER_SRGB_EXT); + } + glViewport(0, 0, mViewportWidth, mViewportHeight); glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); debugOverdraw(false, false); @@ -308,7 +314,7 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { glVertexAttribPointer(alphaLocation, 1, GL_FLOAT, GL_FALSE, vertices.stride, alphaCoords); } // Shader uniforms - SkiaShader::apply(*mCaches, fill.skiaShaderData); + SkiaShader::apply(*mCaches, fill.skiaShaderData, mViewportWidth, mViewportHeight); GL_CHECKPOINT(MODERATE); Texture* texture = (fill.skiaShaderData.skiaShaderType & kBitmap_SkiaShaderType) ? diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index 9e0fb121be65..3d119dc9e290 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -16,7 +16,6 @@ #ifndef RENDERSTATE_H #define RENDERSTATE_H -#include "AssetAtlas.h" #include "Caches.h" #include "Glop.h" #include "renderstate/Blend.h" @@ -92,7 +91,6 @@ public: void render(const Glop& glop, const Matrix4& orthoMatrix); - AssetAtlas& assetAtlas() { return mAssetAtlas; } Blend& blend() { return *mBlend; } MeshState& meshState() { return *mMeshState; } Scissor& scissor() { return *mScissor; } @@ -120,7 +118,6 @@ private: OffscreenBufferPool mLayerPool; - AssetAtlas mAssetAtlas; std::set<Layer*> mActiveLayers; std::set<renderthread::CanvasContext*> mRegisteredContexts; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 0f2d55bc209d..fe0f56ad1332 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -545,11 +545,6 @@ DeferredLayerUpdater* CanvasContext::createTextureLayer() { return mRenderPipeline->createTextureLayer(); } -void CanvasContext::setTextureAtlas(RenderThread& thread, - const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { - thread.eglManager().setTextureAtlas(buffer, map, mapSize); -} - void CanvasContext::dumpFrames(int fd) { FILE* file = fdopen(fd, "a"); fprintf(file, "\n\n---PROFILEDATA---\n"); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 652cddd968bb..41b658e083da 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -119,9 +119,6 @@ public: DeferredLayerUpdater* createTextureLayer(); - ANDROID_API static void setTextureAtlas(RenderThread& thread, - const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); - void stopDrawing(); void notifyFramePending(); diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 86731c9581be..beda0455c145 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -91,9 +91,7 @@ EglManager::EglManager(RenderThread& thread) , mEglConfig(nullptr) , mEglContext(EGL_NO_CONTEXT) , mPBufferSurface(EGL_NO_SURFACE) - , mCurrentSurface(EGL_NO_SURFACE) - , mAtlasMap(nullptr) - , mAtlasMapSize(0) { + , mCurrentSurface(EGL_NO_SURFACE) { } void EglManager::initialize() { @@ -128,7 +126,6 @@ void EglManager::initialize() { makeCurrent(mPBufferSurface); DeviceInfo::initialize(); mRenderThread.renderState().onGLContextCreated(); - initAtlas(); } void EglManager::initExtensions() { @@ -191,32 +188,6 @@ void EglManager::createContext() { "Failed to create context, error = %s", egl_error_str()); } -void EglManager::setTextureAtlas(const sp<GraphicBuffer>& buffer, - int64_t* map, size_t mapSize) { - - // Already initialized - if (mAtlasBuffer.get()) { - ALOGW("Multiple calls to setTextureAtlas!"); - delete map; - return; - } - - mAtlasBuffer = buffer; - mAtlasMap = map; - mAtlasMapSize = mapSize; - - if (hasEglContext()) { - initAtlas(); - } -} - -void EglManager::initAtlas() { - if (mAtlasBuffer.get()) { - mRenderThread.renderState().assetAtlas().init(mAtlasBuffer, - mAtlasMap, mAtlasMapSize); - } -} - void EglManager::createPBufferSurface() { LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "usePBufferSurface() called on uninitialized GlobalContext!"); @@ -229,7 +200,16 @@ void EglManager::createPBufferSurface() { EGLSurface EglManager::createSurface(EGLNativeWindowType window) { initialize(); - EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, nullptr); + + EGLint attribs[] = { +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, + EGL_COLORSPACE, EGL_COLORSPACE_sRGB, +#endif + EGL_NONE + }; + + EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, attribs); LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, "Failed to create EGLSurface for window %p, eglErr = %s", (void*) window, egl_error_str()); diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 41047fecf960..ba4a3e1c5192 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -79,8 +79,6 @@ public: // Returns true iff the surface is now preserving buffers. bool setPreserveBuffer(EGLSurface surface, bool preserve); - void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); - void fence(); private: @@ -94,7 +92,6 @@ private: void createPBufferSurface(); void loadConfig(); void createContext(); - void initAtlas(); EGLint queryBufferAge(EGLSurface surface); RenderThread& mRenderThread; @@ -106,10 +103,6 @@ private: EGLSurface mCurrentSurface; - sp<GraphicBuffer> mAtlasBuffer; - int64_t* mAtlasMap; - size_t mAtlasMapSize; - enum class SwapBehavior { Discard, Preserved, diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index dcbc980763ec..c2ed8643c0ad 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -480,23 +480,6 @@ void RenderProxy::dumpGraphicsMemory(int fd) { staticPostAndWait(task); } -CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, - size_t size) { - CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size); - args->buffer->decStrong(nullptr); - return nullptr; -} - -void RenderProxy::setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size) { - SETUP_TASK(setTextureAtlas); - args->thread = &mRenderThread; - args->buffer = buffer.get(); - args->buffer->incStrong(nullptr); - args->map = map; - args->size = size; - post(task); -} - CREATE_BRIDGE2(setProcessStatsBuffer, RenderThread* thread, int fd) { args->thread->jankTracker().switchStorageToAshmem(args->fd); close(args->fd); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index d4aaea6d7280..50a6f64fe5ca 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -112,7 +112,6 @@ public: uint32_t frameTimePercentile(int p); ANDROID_API static void dumpGraphicsMemory(int fd); - ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size); ANDROID_API void setProcessStatsBuffer(int fd); ANDROID_API int getRenderThreadTid(); diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 78e9bc475826..51c0a05fb6d7 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -124,8 +124,9 @@ public: static SkBitmap createSkBitmap(int width, int height, SkColorType colorType = kN32_SkColorType) { SkBitmap bitmap; + sk_sp<SkColorSpace> colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); SkImageInfo info = SkImageInfo::Make(width, height, - colorType, kPremul_SkAlphaType); + colorType, kPremul_SkAlphaType, colorSpace); bitmap.setInfo(info); bitmap.allocPixels(info); return bitmap; diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp index a30ada0df453..5cab04d26c2a 100644 --- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp +++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp @@ -18,6 +18,7 @@ #include <gtest/gtest.h> #include <SkColorMatrixFilter.h> +#include <SkColorSpace.h> #include <SkImagePriv.h> #include <SkShader.h> @@ -82,3 +83,9 @@ TEST(SkiaBehavior, porterDuffCreateIsCached) { paint.setXfermodeMode(SkXfermode::kOverlay_Mode); ASSERT_EQ(expected, paint.getXfermode()); } + +TEST(SkiaBehavior, srgbColorSpaceIsSingleton) { + sk_sp<SkColorSpace> sRGB1 = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); + sk_sp<SkColorSpace> sRGB2 = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); + ASSERT_EQ(sRGB1.get(), sRGB2.get()); +} diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index b5157f401438..c8f8c7071075 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -16,6 +16,8 @@ #ifndef COLOR_H #define COLOR_H +#include <math.h> + #include <SkColor.h> namespace android { @@ -80,6 +82,28 @@ namespace uirenderer { }; static constexpr int BrightColorsCount = sizeof(BrightColors) / sizeof(Color::Color); + // Opto-electronic conversion function for the sRGB color space + // Takes a gamma-encoded sRGB value and converts it to a linear sRGB value + static constexpr float OECF_sRGB(float linear) { +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + // IEC 61966-2-1:1999 + return linear <= 0.0031308f ? + linear * 12.92f : (powf(linear, 1.0f / 2.4f) * 1.055f) - 0.055f; +#else + return linear; +#endif + } + + // Electro-optical conversion function for the sRGB color space + // Takes a linear sRGB value and converts it to a gamma-encoded sRGB value + static constexpr float EOCF_sRGB(float srgb) { +#ifdef ANDROID_ENABLE_LINEAR_BLENDING + // IEC 61966-2-1:1999 + return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f); +#else + return srgb; +#endif + } } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp index b879f781bce1..624d20763384 100644 --- a/libs/hwui/utils/TestWindowContext.cpp +++ b/libs/hwui/utils/TestWindowContext.cpp @@ -110,9 +110,10 @@ public: } bool capturePixels(SkBitmap* bmp) { + sk_sp<SkColorSpace> colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); SkImageInfo destinationConfig = SkImageInfo::Make(mSize.width(), mSize.height(), - kRGBA_8888_SkColorType, kPremul_SkAlphaType); + kRGBA_8888_SkColorType, kPremul_SkAlphaType, colorSpace); bmp->allocPixels(destinationConfig); android_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED, mSize.width() * mSize.height() * 4); |